, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
jfree-jfreechart-cb8ff67/pom.xml 0000664 0000000 0000000 00000023001 14636042355 0016750 0 ustar 00root root 0000000 0000000
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/ 0000775 0000000 0000000 00000000000 14636042355 0016226 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/ 0000775 0000000 0000000 00000000000 14636042355 0017152 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/ 0000775 0000000 0000000 00000000000 14636042355 0020073 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/ 0000775 0000000 0000000 00000000000 14636042355 0020662 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/ 0000775 0000000 0000000 00000000000 14636042355 0021755 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ 0000775 0000000 0000000 00000000000 14636042355 0023056 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartColor.java 0000664 0000000 0000000 00000020420 14636042355 0025757 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000272361 14636042355 0026325 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005406 14636042355 0025742 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006602 14636042355 0025774 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006614 14636042355 0027004 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004324 14636042355 0027504 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000336676 14636042355 0025767 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000020324 14636042355 0027435 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003777 14636042355 0025763 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000020434 14636042355 0027316 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000064316 14636042355 0026015 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000024014 14636042355 0025626 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000161055 14636042355 0025706 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000110362 14636042355 0025741 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010717 14636042355 0027760 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003571 14636042355 0027125 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007145 14636042355 0027760 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011541 14636042355 0027276 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000044237 14636042355 0031115 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015171 14636042355 0025437 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000020322 14636042355 0026737 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000161333 14636042355 0027435 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015063 14636042355 0025633 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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/ 0000775 0000000 0000000 00000000000 14636042355 0025413 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/AbstractAnnotation.java 0000664 0000000 0000000 00000017475 14636042355 0032072 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015246 14636042355 0032345 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004312 14636042355 0030370 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004474 14636042355 0032077 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000034413 14636042355 0032703 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000042742 14636042355 0033440 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000022114 14636042355 0032733 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000025113 14636042355 0031237 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005032 14636042355 0030651 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004320 14636042355 0032637 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000032470 14636042355 0031330 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000030274 14636042355 0032414 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007740 14636042355 0032430 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000025672 14636042355 0032327 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000024067 14636042355 0031625 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000023042 14636042355 0033547 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000031217 14636042355 0031465 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000035162 14636042355 0031506 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000044526 14636042355 0032225 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000030406 14636042355 0032224 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000023775 14636042355 0031650 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000047447 14636042355 0031536 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000032506 14636042355 0031661 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000232 14636042355 0027671 0 ustar 00root root 0000000 0000000
A framework for adding annotations to charts.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/ 0000775 0000000 0000000 00000000000 14636042355 0024022 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/Axis.java 0000664 0000000 0000000 00000162216 14636042355 0025601 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010355 14636042355 0027611 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007643 14636042355 0030234 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013203 14636042355 0027261 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000025726 14636042355 0026561 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011542 14636042355 0026575 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007407 14636042355 0027605 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000145525 14636042355 0027303 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000021774 14636042355 0031142 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000036753 14636042355 0031330 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007405 14636042355 0031252 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011507 14636042355 0027261 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011440 14636042355 0027443 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000120323 14636042355 0030072 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000203131 14636042355 0026367 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010056 14636042355 0026357 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007622 14636042355 0030724 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000024536 14636042355 0027227 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012260 14636042355 0030060 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000017315 14636042355 0030757 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000110276 14636042355 0026242 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004653 14636042355 0026231 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000123721 14636042355 0027762 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000020363 14636042355 0027524 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000033475 14636042355 0026765 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000021735 14636042355 0027731 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000120311 14636042355 0026740 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006164 14636042355 0026737 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010736 14636042355 0027577 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015050 14636042355 0030752 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000122674 14636042355 0026750 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000026461 14636042355 0030521 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014426 14636042355 0030266 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007756 14636042355 0031300 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000036132 14636042355 0027746 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000066457 14636042355 0027001 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013733 14636042355 0025566 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006171 14636042355 0026426 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012111 14636042355 0026413 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005220 14636042355 0027577 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013154 14636042355 0026606 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011267 14636042355 0026442 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000157744 14636042355 0026610 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010064 14636042355 0026555 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000211 14636042355 0026275 0 ustar 00root root 0000000 0000000
Axis classes and interfaces.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/ 0000775 0000000 0000000 00000000000 14636042355 0024150 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/AbstractBlock.java 0000664 0000000 0000000 00000046341 14636042355 0027541 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005275 14636042355 0027267 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006625 14636042355 0026056 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000017076 14636042355 0027216 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000022620 14636042355 0027712 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004317 14636042355 0027025 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007754 14636042355 0027226 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004417 14636042355 0027252 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000047717 14636042355 0030434 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000026454 14636042355 0030432 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012775 14636042355 0027060 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000035031 14636042355 0030436 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007534 14636042355 0027075 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003604 14636042355 0030411 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003474 14636042355 0030451 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000040576 14636042355 0030122 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000037603 14636042355 0030075 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000033043 14636042355 0027010 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010014 14636042355 0031137 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000016652 14636042355 0027052 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000026537 14636042355 0031001 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000317 14636042355 0026432 0 ustar 00root root 0000000 0000000
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/ 0000775 0000000 0000000 00000000000 14636042355 0023773 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/date/MonthConstants.java 0000664 0000000 0000000 00000004225 14636042355 0027623 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000073546 14636042355 0026672 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000033154 14636042355 0027711 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000000145 14636042355 0027162 0 ustar 00root root 0000000 0000000 /**
* Date-related classes formerly in the JCommon class library.
*/
package org.jfree.chart.date;
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/ 0000775 0000000 0000000 00000000000 14636042355 0024344 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/ChartEditor.java 0000664 0000000 0000000 00000003605 14636042355 0027423 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003457 14636042355 0030760 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005607 14636042355 0030722 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000041774 14636042355 0030604 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000022630 14636042355 0030727 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004101 14636042355 0032250 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012003 14636042355 0031225 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013340 14636042355 0031741 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000055633 14636042355 0030615 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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 CategoryPlot s and
* XYPlot s).
*/
private PlotOrientation plotOrientation;
/**
* The orientation combo box (for CategoryPlot s and
* XYPlot s).
*/
private JComboBox orientationCombo;
/** Whether or not to draw lines between each data point (for
* LineAndShapeRenderer s and StandardXYItemRenderer s).
*/
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
* LineAndShapeRenderer s and StandardXYItemRenderer s).
*/
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 LineAndShapeRenderer s and
* StandardXYItemRenderer s.
*/
private void attemptDrawLinesSelection() {
this.drawLines = this.drawLinesCheckBox.isSelected();
}
/**
* Allow the user to modify whether or not shapes are drawn at data points
* by LineAndShapeRenderer s and StandardXYItemRenderer s.
*/
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.java 0000664 0000000 0000000 00000015234 14636042355 0031604 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000023717 14636042355 0030756 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000033405 14636042355 0031571 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000300 14636042355 0026616 0 ustar 00root root 0000000 0000000
Provides a simple (but so far incomplete) framework for editing chart
properties.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/ 0000775 0000000 0000000 00000000000 14636042355 0024660 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/EncoderUtil.java 0000664 0000000 0000000 00000017030 14636042355 0027741 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006337 14636042355 0030056 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012227 14636042355 0031401 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003517 14636042355 0027724 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013347 14636042355 0031547 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010222 14636042355 0031433 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000262 14636042355 0027141 0 ustar 00root root 0000000 0000000
Classes related to the encoding of charts to different image formats.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/ 0000775 0000000 0000000 00000000000 14636042355 0024372 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/AxisEntity.java 0000664 0000000 0000000 00000014221 14636042355 0027336 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015034 14636042355 0031031 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010471 14636042355 0031152 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000032263 14636042355 0027501 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005665 14636042355 0030541 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007636 14636042355 0027355 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014403 14636042355 0030411 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012540 14636042355 0030451 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005555 14636042355 0027331 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000016240 14636042355 0030477 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014442 14636042355 0027355 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013551 14636042355 0032213 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004364 14636042355 0030273 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015254 14636042355 0027522 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010330 14636042355 0031022 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013061 14636042355 0027612 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000251 14636042355 0026651 0 ustar 00root root 0000000 0000000
Classes representing components of (or entities in) a chart.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/ 0000775 0000000 0000000 00000000000 14636042355 0024177 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/AnnotationChangeEvent.java 0000664 0000000 0000000 00000004732 14636042355 0031272 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003631 14636042355 0031773 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004163 14636042355 0030062 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004211 14636042355 0030560 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007333 14636042355 0030221 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010156 14636042355 0031060 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003772 14636042355 0030730 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007312 14636042355 0030635 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003550 14636042355 0031341 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004445 14636042355 0030402 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003617 14636042355 0031106 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003503 14636042355 0030574 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003445 14636042355 0031305 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004345 14636042355 0030076 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003524 14636042355 0030600 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006217 14636042355 0030726 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003560 14636042355 0031430 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004204 14636042355 0030233 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003550 14636042355 0030742 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000440 14636042355 0026456 0 ustar 00root root 0000000 0000000
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/ 0000775 0000000 0000000 00000000000 14636042355 0024636 5 ustar 00root root 0000000 0000000 DynamicDriveToolTipTagFragmentGenerator.java 0000664 0000000 0000000 00000006123 14636042355 0035304 0 ustar 00root root 0000000 0000000 jfree-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.java 0000664 0000000 0000000 00000023146 14636042355 0030210 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004557 14636042355 0034201 0 ustar 00root root 0000000 0000000 jfree-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.java 0000664 0000000 0000000 00000004353 14636042355 0034471 0 ustar 00root root 0000000 0000000 jfree-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.java0000664 0000000 0000000 00000004322 14636042355 0033614 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004540 14636042355 0033065 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004542 14636042355 0032137 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000315 14636042355 0027116 0 ustar 00root root 0000000 0000000
Classes, including {@link org.jfree.chart.imagemap.ImageMapUtils}, for creating HTML image maps.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/ 0000775 0000000 0000000 00000000000 14636042355 0024320 5 ustar 00root root 0000000 0000000 AbstractCategoryItemLabelGenerator.java 0000664 0000000 0000000 00000026105 14636042355 0033777 0 ustar 00root root 0000000 0000000 jfree-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.java 0000664 0000000 0000000 00000017126 14636042355 0033021 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000027000 14636042355 0032634 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012075 14636042355 0033062 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013125 14636042355 0033340 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000021234 14636042355 0032267 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006123 14636042355 0032370 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004362 14636042355 0032727 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005112 14636042355 0032121 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003513 14636042355 0031731 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013245 14636042355 0032105 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003705 14636042355 0030706 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000016411 14636042355 0032155 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011753 14636042355 0034023 0 ustar 00root root 0000000 0000000 jfree-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.java 0000664 0000000 0000000 00000012456 14636042355 0033637 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000022233 14636042355 0032660 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000021552 14636042355 0032417 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000024175 14636042355 0030165 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004000 14636042355 0027623 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000022203 14636042355 0030545 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000017211 14636042355 0033223 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006741 14636042355 0032044 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004171 14636042355 0031065 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013107 14636042355 0033772 0 ustar 00root root 0000000 0000000 jfree-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.java 0000664 0000000 0000000 00000012102 14636042355 0034320 0 ustar 00root root 0000000 0000000 jfree-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.java 0000664 0000000 0000000 00000012334 14636042355 0033606 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011142 14636042355 0033407 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010245 14636042355 0032364 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000021252 14636042355 0033517 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012026 14636042355 0032544 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015066 14636042355 0032642 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012403 14636042355 0033166 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015056 14636042355 0032375 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000020253 14636042355 0032522 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012413 14636042355 0032654 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004150 14636042355 0031151 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004364 14636042355 0031514 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004024 14636042355 0030705 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004111 14636042355 0031034 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000273 14636042355 0026603 0 ustar 00root root 0000000 0000000
Generators and other classes used for the display of item labels and tooltips.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/ 0000775 0000000 0000000 00000000000 14636042355 0024312 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/ArrowNeedle.java 0000664 0000000 0000000 00000012265 14636042355 0027372 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007347 14636042355 0027174 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012771 14636042355 0027201 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000026575 14636042355 0027365 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011122 14636042355 0030134 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010744 14636042355 0027026 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010215 14636042355 0027206 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011504 14636042355 0027713 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010174 14636042355 0027200 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007462 14636042355 0027204 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000330 14636042355 0026567 0 ustar 00root root 0000000 0000000
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.html 0000664 0000000 0000000 00000000320 14636042355 0025332 0 ustar 00root root 0000000 0000000
Core classes, including {@link org.jfree.chart.JFreeChart} and
{@link org.jfree.chart.ChartPanel}.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/panel/ 0000775 0000000 0000000 00000000000 14636042355 0024155 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/panel/AbstractOverlay.java 0000664 0000000 0000000 00000007333 14636042355 0030133 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000053725 14636042355 0030333 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004250 14636042355 0026442 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000247 14636042355 0026441 0 ustar 00root root 0000000 0000000
Classes related to the {@link org.jfree.chart.ChartPanel} class.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/ 0000775 0000000 0000000 00000000000 14636042355 0024034 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/AbstractPieLabelDistributor.java 0000664 0000000 0000000 00000006465 14636042355 0032306 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013002 14636042355 0031327 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014251 14636042355 0027621 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000502556 14636042355 0027330 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003252 14636042355 0027573 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000060120 14636042355 0032103 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000060174 14636042355 0030677 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000047530 14636042355 0031742 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000057623 14636042355 0030531 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000065025 14636042355 0027153 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000052172 14636042355 0026643 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000026076 14636042355 0027650 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010177 14636042355 0031124 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000042104 14636042355 0031324 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007372 14636042355 0026542 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005657 14636042355 0030033 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000102670 14636042355 0027767 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000017775 14636042355 0027646 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000052225 14636042355 0026126 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015375 14636042355 0027473 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000121467 14636042355 0026625 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000052364 14636042355 0030001 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005654 14636042355 0026431 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000016600 14636042355 0030612 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007775 14636042355 0030233 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000016542 14636042355 0027523 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000345713 14636042355 0026270 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*
* Special notes:
*
* the default starting point is 12 o'clock and the pie sections proceed
* in a clockwise direction, but these settings can be changed;
* negative values in the dataset are ignored;
* there are utility methods for creating a {@link PieDataset} from a
* {@link org.jfree.data.category.CategoryDataset};
*
*
* @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.java 0000664 0000000 0000000 00000115405 14636042355 0026450 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015243 14636042355 0027261 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000140665 14636042355 0025631 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010752 14636042355 0030036 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000021153 14636042355 0030271 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003763 14636042355 0026627 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012164 14636042355 0030276 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000200112 14636042355 0026607 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000056036 14636042355 0026447 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010103 14636042355 0030756 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000152007 14636042355 0027427 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000144217 14636042355 0030042 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
* three sub-ranges which could be viewed as 'Normal', 'Warning'
* and 'Critical' ranges.
* the thermometer can be run in two modes:
*
* fixed range, or
* range adjusts to current sub-range.
*
*
* settable units to be displayed.
* settable display location for the value text.
*
*/
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.java 0000664 0000000 0000000 00000003657 14636042355 0027452 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012424 14636042355 0027120 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000033526 14636042355 0027251 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003403 14636042355 0030116 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000542325 14636042355 0026111 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014072 14636042355 0026453 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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/ 0000775 0000000 0000000 00000000000 14636042355 0024745 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/AbstractDialLayer.java 0000664 0000000 0000000 00000014475 14636042355 0031155 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000035370 14636042355 0030072 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000017663 14636042355 0030476 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000023465 14636042355 0027117 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004332 14636042355 0027436 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007156 14636042355 0027467 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004232 14636042355 0031567 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003636 14636042355 0032302 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000057005 14636042355 0027327 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000043740 14636042355 0030032 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004115 14636042355 0027432 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000026415 14636042355 0031371 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000053232 14636042355 0031320 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000024570 14636042355 0031125 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000030200 14636042355 0031112 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000073604 14636042355 0031124 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000207 14636042355 0027225 0 ustar 00root root 0000000 0000000
Classes for creating dial plots.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/flow/ 0000775 0000000 0000000 00000000000 14636042355 0025003 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/flow/FlowPlot.java 0000664 0000000 0000000 00000077311 14636042355 0027425 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
* @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.java 0000664 0000000 0000000 00000000150 14636042355 0030166 0 ustar 00root root 0000000 0000000 /**
* 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.html 0000664 0000000 0000000 00000000221 14636042355 0026310 0 ustar 00root root 0000000 0000000
Plot classes and related interfaces.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/ 0000775 0000000 0000000 00000000000 14636042355 0024664 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/AbstractRenderer.java 0000664 0000000 0000000 00000315240 14636042355 0030766 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010057 14636042355 0031362 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000073420 14636042355 0032425 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014702 14636042355 0030401 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000025630 14636042355 0030752 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003655 14636042355 0031523 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013575 14636042355 0027165 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012136 14636042355 0030011 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013123 14636042355 0032022 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004746 14636042355 0027565 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014340 14636042355 0031114 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006721 14636042355 0030304 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000022411 14636042355 0030316 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000030227 14636042355 0030724 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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/ 0000775 0000000 0000000 00000000000 14636042355 0026501 5 ustar 00root root 0000000 0000000 AbstractCategoryItemRenderer.java 0000664 0000000 0000000 00000173040 14636042355 0035041 0 ustar 00root root 0000000 0000000 jfree-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.java 0000664 0000000 0000000 00000026503 14636042355 0031711 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000006336 14636042355 0031403 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000127601 14636042355 0031546 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000116521 14636042355 0033551 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000153210 14636042355 0033431 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014071 14636042355 0034354 0 ustar 00root root 0000000 0000000 jfree-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.java 0000664 0000000 0000000 00000031114 14636042355 0033444 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000003766 14636042355 0034671 0 ustar 00root root 0000000 0000000 jfree-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.java 0000664 0000000 0000000 00000054522 14636042355 0032120 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000031346 14636042355 0033060 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000032157 14636042355 0034315 0 ustar 00root root 0000000 0000000 jfree-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:
*
*
*/
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.java 0000664 0000000 0000000 00000021276 14636042355 0033254 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000037631 14636042355 0033057 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000037265 14636042355 0032117 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000070107 14636042355 0033333 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java0000664 0000000 0000000 00000044411 14636042355 0033726 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000046326 14636042355 0032453 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000042651 14636042355 0033212 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000032612 14636042355 0033042 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000015635 14636042355 0033066 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java0000664 0000000 0000000 00000051031 14636042355 0033744 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000041615 14636042355 0035463 0 ustar 00root root 0000000 0000000 jfree-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:
*
*
*/
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.java 0000664 0000000 0000000 00000041365 14636042355 0033412 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.html 0000664 0000000 0000000 00000000267 14636042355 0030767 0 ustar 00root root 0000000 0000000
Plug-in renderers for the {@link org.jfree.chart.plot.CategoryPlot} class.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/package.html 0000664 0000000 0000000 00000000372 14636042355 0027147 0 ustar 00root root 0000000 0000000
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/ 0000775 0000000 0000000 00000000000 14636042355 0025324 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/AbstractXYItemRenderer.java 0000664 0000000 0000000 00000173125 14636042355 0032532 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000074417 14636042355 0032117 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*
* 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.java 0000664 0000000 0000000 00000032324 14636042355 0032522 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*
* 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.java 0000664 0000000 0000000 00000041545 14636042355 0032175 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003650 14636042355 0032346 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000031734 14636042355 0031610 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000031053 14636042355 0032436 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000031126 14636042355 0032160 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000040756 14636042355 0031233 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000030166 14636042355 0032527 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000057260 14636042355 0032320 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*
* 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.java 0000664 0000000 0000000 00000047744 14636042355 0032410 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000034763 14636042355 0032157 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000015644 14636042355 0032172 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000103006 14636042355 0032516 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000030076 14636042355 0031126 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000015467 14636042355 0031413 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000061642 14636042355 0031020 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000035572 14636042355 0031105 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000006346 14636042355 0030510 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000130431 14636042355 0030645 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000055274 14636042355 0031374 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000035702 14636042355 0031200 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000072173 14636042355 0032661 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*
* 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.java 0000664 0000000 0000000 00000032155 14636042355 0031340 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000132162 14636042355 0032176 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000027756 14636042355 0030706 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000036106 14636042355 0031236 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000141274 14636042355 0031046 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013255 14636042355 0032044 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000117374 14636042355 0032447 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*
*/
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.java 0000664 0000000 0000000 00000047413 14636042355 0031210 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*
* 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.java 0000664 0000000 0000000 00000044070 14636042355 0031376 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000052741 14636042355 0031654 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000030613 14636042355 0031055 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.java 0000664 0000000 0000000 00000025772 14636042355 0031610 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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:
*
*
*/
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.html 0000664 0000000 0000000 00000000261 14636042355 0027604 0 ustar 00root root 0000000 0000000
Plug-in renderers for the {@link org.jfree.chart.plot.XYPlot} class.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/servlet/ 0000775 0000000 0000000 00000000000 14636042355 0024542 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/servlet/ChartDeleter.java 0000664 0000000 0000000 00000007223 14636042355 0027757 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011636 14636042355 0030003 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000037212 14636042355 0030732 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000250 14636042355 0027020 0 ustar 00root root 0000000 0000000
Classes for providing useful servlet and JSP functionality.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/ 0000775 0000000 0000000 00000000000 14636042355 0024042 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/AttributedStringUtils.java 0000664 0000000 0000000 00000006154 14636042355 0031232 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004357 14636042355 0027537 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000024723 14636042355 0026614 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012265 14636042355 0027745 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000027140 14636042355 0026306 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000022222 14636042355 0027315 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000020207 14636042355 0026442 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003331 14636042355 0027335 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000067373 14636042355 0026672 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000000145 14636042355 0027231 0 ustar 00root root 0000000 0000000 /**
* Text-related classes formerly in the JCommon class library.
*/
package org.jfree.chart.text;
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/ 0000775 0000000 0000000 00000000000 14636042355 0024177 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/CompositeTitle.java 0000664 0000000 0000000 00000020500 14636042355 0030003 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014176 14636042355 0026732 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000031310 14636042355 0027064 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000055240 14636042355 0027544 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012766 14636042355 0031711 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000057356 14636042355 0027262 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000065542 14636042355 0030220 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000020215 14636042355 0030010 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000075316 14636042355 0027004 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000035671 14636042355 0026137 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000240 14636042355 0026454 0 ustar 00root root 0000000 0000000
Classes used to display chart titles and subtitles.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/ 0000775 0000000 0000000 00000000000 14636042355 0023473 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/Align.java 0000664 0000000 0000000 00000010440 14636042355 0025367 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007223 14636042355 0027560 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003167 14636042355 0026066 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015527 14636042355 0027561 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005613 14636042355 0027543 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010512 14636042355 0031624 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003564 14636042355 0031322 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007457 14636042355 0030343 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000021176 14636042355 0026143 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003454 14636042355 0025420 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007326 14636042355 0030470 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005616 14636042355 0030065 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006307 14636042355 0026561 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013477 14636042355 0027411 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013531 14636042355 0027032 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000037675 14636042355 0027452 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010440 14636042355 0025435 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013465 14636042355 0033004 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006424 14636042355 0030116 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011602 14636042355 0026747 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000022046 14636042355 0026421 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015034 14636042355 0025677 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007401 14636042355 0027750 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000234 14636042355 0025753 0 ustar 00root root 0000000 0000000
Utility classes that relate to user interface items.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/ 0000775 0000000 0000000 00000000000 14636042355 0024043 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/CategoryURLGenerator.java 0000664 0000000 0000000 00000004463 14636042355 0030724 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013322 14636042355 0032111 0 ustar 00root root 0000000 0000000 /* ======================================
* 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.java 0000664 0000000 0000000 00000015007 14636042355 0031053 0 ustar 00root root 0000000 0000000 /* ======================================
* 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.java 0000664 0000000 0000000 00000013475 14636042355 0030705 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005267 14636042355 0027667 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015503 14636042355 0032402 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012655 14636042355 0031347 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013500 14636042355 0031160 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004150 14636042355 0031313 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000015272 14636042355 0031220 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004543 14636042355 0027506 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004507 14636042355 0027640 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000255 14636042355 0026326 0 ustar 00root root 0000000 0000000
Classes for adding URLS to charts for HTML image map generation.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/ 0000775 0000000 0000000 00000000000 14636042355 0024033 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/AbstractObjectList.java 0000664 0000000 0000000 00000016457 14636042355 0030441 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010343 14636042355 0025573 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013114 14636042355 0026775 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000026725 14636042355 0030034 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005330 14636042355 0027112 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011472 14636042355 0026764 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000022650 14636042355 0031304 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java0000664 0000000 0000000 00000012463 14636042355 0034036 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000023500 14636042355 0027200 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014214 14636042355 0026425 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007251 14636042355 0027654 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011347 14636042355 0027752 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000014315 14636042355 0026612 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000016005 14636042355 0026572 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006452 14636042355 0026747 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012617 14636042355 0027134 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000035006 14636042355 0026723 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.java 0000664 0000000 0000000 00000010652 14636042355 0026611 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000016121 14636042355 0026773 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004053 14636042355 0027723 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000037462 14636042355 0030434 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000013270 14636042355 0031163 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004544 14636042355 0026504 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000055033 14636042355 0027144 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004352 14636042355 0027776 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011200 14636042355 0026564 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000044537 14636042355 0026774 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004010 14636042355 0026614 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005740 14636042355 0027173 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000010713 14636042355 0027003 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003770 14636042355 0026730 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003724 14636042355 0026465 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007557 14636042355 0030126 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000220 14636042355 0026306 0 ustar 00root root 0000000 0000000
Utility classes used by JFreeChart.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/ 0000775 0000000 0000000 00000000000 14636042355 0022666 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/ComparableObjectItem.java 0000664 0000000 0000000 00000012416 14636042355 0027550 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000036314 14636042355 0030107 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000021643 14636042355 0025431 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011340 14636042355 0027073 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000032544 14636042355 0027267 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000041535 14636042355 0027455 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005537 14636042355 0025566 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007540 14636042355 0025742 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003173 14636042355 0025104 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000023126 14636042355 0026243 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007705 14636042355 0025732 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000023760 14636042355 0026114 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000036232 14636042355 0026300 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003455 14636042355 0025576 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000012204 14636042355 0027616 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006334 14636042355 0030467 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006353 14636042355 0025761 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006420 14636042355 0026142 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000011050 14636042355 0027425 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006116 14636042355 0027246 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000032773 14636042355 0024601 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000005167 14636042355 0025412 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000007520 14636042355 0025433 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003441 14636042355 0027522 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000003204 14636042355 0024604 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004105 14636042355 0024770 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004273 14636042355 0025164 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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/ 0000775 0000000 0000000 00000000000 14636042355 0024503 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/CategoryDataset.java 0000664 0000000 0000000 00000003620 14636042355 0030432 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000004164 14636042355 0030721 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000023704 14636042355 0031400 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000030740 14636042355 0031742 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000064473 14636042355 0033461 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000006251 14636042355 0032142 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.java 0000664 0000000 0000000 00000025140 14636042355 0031745 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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.html 0000664 0000000 0000000 00000000324 14636042355 0026763 0 ustar 00root root 0000000 0000000
A package containing the {@link org.jfree.data.category.CategoryDataset} interface and related classes.
jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/flow/ 0000775 0000000 0000000 00000000000 14636042355 0023635 5 ustar 00root root 0000000 0000000 jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/flow/DefaultFlowDataset.java 0000664 0000000 0000000 00000031120 14636042355 0030217 0 ustar 00root root 0000000 0000000 /* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Project Info: http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 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