diff --git a/LICENSE b/LICENSE index 3f471b9..1df667b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ jfreechart-builder: a builder pattern module for working with the jfreechart library -(C) Copyright 2020, by Matt E. +(C) Copyright 2020, by Matt E. and project contributors This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/README.md b/README.md index f0baed0..50eb619 100644 --- a/README.md +++ b/README.md @@ -4,140 +4,166 @@ A [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern) module for wo Takes an opinionated approach to creating "good enough" charts while providing a more declarative way of parameterizing them. -Code like this: - -``` -public static final Stroke SOLID = - new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND); -private static final Color DARK_GREEN = new Color(0, 150, 0); +## Capabilities -ChartBuilder.instance() +* XY time series plots using a [CombinedDomainXYPlot](https://github.com/jfree/jfreechart/blob/master/src/main/java/org/jfree/chart/plot/CombinedDomainXYPlot.java) in all cases. This produces left-to-right horizontal time axes and vertical value axes. The time axis is meant to be shared by all sub-plots. If you need different time axes then you'll need to `build()` multiple charts and lay those out as desired in your app. - .title("Multi Plot Minute Time Series") +* Stock market OHLC candlestick charts - .timeData(timestampsArray) +* Stock market volume bar charts - .xyPlot(XYPlotBuilder.instance().yAxisName("Values") - .series(XYTimeSeriesBuilder.instance().data(array1).color(Color.BLUE).style(SOLID)) - .series(XYTimeSeriesBuilder.instance().data(array2).color(Color.RED).style(SOLID)) - .series(XYTimeSeriesBuilder.instance().data(array3).color(DARK_GREEN).style(SOLID)) - .series(XYTimeSeriesBuilder.instance().data(array4).color(Color.MAGENTA).style(SOLID))) +* Stright lines - .xyPlot(XYPlotBuilder.instance().yAxisName("Amplitudes") - .series(XYTimeSeriesBuilder.instance().data(array2).color(Color.GRAY).style(SOLID)) - .series(XYTimeSeriesBuilder.instance().data(array3).color(Color.LIGHT_GRAY).style(SOLID))) +* Annotations (arrows and text) - .xyPlot(XYPlotBuilder.instance().yAxisName("Series 1") - .series(XYTimeSeriesBuilder.instance().data(array1).color(Color.BLUE).style(SOLID))) +In the future, more parameterization may be added like specifying background and axis colors, or even the actual series renderer objects themselves to fully leverage what [jfreechart](https://github.com/jfree/jfreechart) provides. - .xyPlot(XYPlotBuilder.instance().yAxisName("Series 2") - .series(XYTimeSeriesBuilder.instance().data(array2).color(Color.RED).style(SOLID))) +## Samples - .xyPlot(XYPlotBuilder.instance().yAxisName("Series 3") - .series(XYTimeSeriesBuilder.instance().data(array3).color(DARK_GREEN).style(SOLID))) +Code like this: -.build() +``` +ChartBuilder.get() + .title("Simple Time Series With Annotations") + .timeData(timeArray) + .xyPlot(XYPlotBuilder.get() + .series(XYTimeSeriesBuilder.get().name("Amplitude").data(array1).color(Color.BLUE).style(SOLID_LINE)) + .annotation(XYArrowBuilder.get().x(arrowX).y(arrowY).angle(180.0).color(Color.RED).text(arrowTxt)) + .annotation(XYArrowBuilder.get().x(arrowX).y(arrowY).angle(0.0).color(Color.RED)) + .annotation(XYTextBuilder.get().x(arrowX).y(arrowY).color(DARK_GREEN) + .text("This value!").textPaddingLeft(5).textAlign(TextAnchor.BASELINE_LEFT).angle(90.0))) + .build() ``` Produces a chart like this: -![A multi-plot minute time series chart](./screenshots/multi-plot-minute-time-series.png "Screenshot") +![A multi-plot minute time series chart](./screenshots/simple-time-series-with-annotations.png "Screenshot") -And code like this: +Multiple series can be multi-plotted like this: ``` -ChartBuilder.instance() +public static final Stroke SOLID = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND); - .title("Stock Chart Time Series With Weekend Gaps") +private static final Color DARK_GREEN = new Color(0, 150, 0); - .timeData(timestampsArray) // Has weekend date timestamps missing +ChartBuilder.get() - .xyPlot(OhlcPlotBuilder.instance().yAxisName("Price").plotWeight(3) - .series(OhlcSeriesBuilder.instance().ohlcv(ohlcv).upColor(Color.WHITE).downColor(Color.RED)) - .series(XYTimeSeriesBuilder.instance().name("MA(20)").data(sma20).color(Color.MAGENTA).style(SOLID)) - .series(XYTimeSeriesBuilder.instance().name("MA(50)").data(sma50).color(Color.BLUE).style(SOLID)) - .series(XYTimeSeriesBuilder.instance().name("MA(200)").data(sma200).color(Color.RED).style(SOLID))) + .title("Multi Plot Minute Time Series") - .xyPlot(VolumeXYPlotBuilder.instance().yAxisName("Volume") - .series(VolumeXYTimeSeriesBuilder.instance().ohlcv(ohlcv).closeUpSeries().color(Color.WHITE)) - .series(VolumeXYTimeSeriesBuilder.instance().ohlcv(ohlcv).closeDownSeries().color(Color.RED)) - .series(XYTimeSeriesBuilder.instance().name("MA(90)").data(volSma90).color(Color.BLUE).style(SOLID))) + .timeData(timeArray) - .xyPlot(XYPlotBuilder.instance().yAxisName("Stoch").yAxisRange(0.0, 100.0).yAxisTickSize(50.0) - .series(XYTimeSeriesBuilder.instance().name("K(" + K + ")").data(stochK).color(Color.RED).style(SOLID)) - .series(XYTimeSeriesBuilder.instance().name("D(" + D + ")").data(stochD).color(Color.BLUE).style(SOLID)) - .line(LineBuilder.instance().orientation(Orientation.HORIZONTAL).atValue(80.0).color(Color.BLACK).style(SOLID)) - .line(LineBuilder.instance().orientation(Orientation.HORIZONTAL).atValue(50.0).color(Color.BLUE).style(SOLID)) - .line(LineBuilder.instance().orientation(Orientation.HORIZONTAL).atValue(20.0).color(Color.BLACK).style(SOLID))) + .xyPlot(XYPlotBuilder.get().yAxisName("Values") + .series(XYTimeSeriesBuilder.get().data(array1).color(Color.BLUE).style(SOLID)) + .series(XYTimeSeriesBuilder.get().data(array2).color(Color.RED).style(SOLID)) + .series(XYTimeSeriesBuilder.get().data(array3).color(DARK_GREEN).style(SOLID)) + .series(XYTimeSeriesBuilder.get().data(array4).color(Color.MAGENTA).style(SOLID))) - .build() -``` + .xyPlot(XYPlotBuilder.get().yAxisName("Amplitudes") + .series(XYTimeSeriesBuilder.get().data(array2).color(Color.GRAY).style(SOLID)) + .series(XYTimeSeriesBuilder.get().data(array3).color(Color.LIGHT_GRAY).style(SOLID))) -Produces a chart like this: + .xyPlot(XYPlotBuilder.get().yAxisName("Series 1") + .series(XYTimeSeriesBuilder.get().data(array1).color(Color.BLUE).style(SOLID))) -![A stock chart time series chart with weekend gaps](./screenshots/stock-chart-time-series-weekend-gaps.png "Screenshot") + .xyPlot(XYPlotBuilder.get().yAxisName("Series 2") + .series(XYTimeSeriesBuilder.get().data(array2).color(Color.RED).style(SOLID))) + .xyPlot(XYPlotBuilder.get().yAxisName("Series 3") + .series(XYTimeSeriesBuilder.get().data(array3).color(DARK_GREEN).style(SOLID))) -## Demo App +.build() +``` -See the [jfreechart-builder-demo](https://github.com/matoos32/jfreechart-builder-demo) for an interactive demo used for development. +Produces a chart like this: -## Scope +![A multi-plot minute time series chart](./screenshots/multi-plot-minute-time-series.png "Screenshot") -Currently only supports XY time series plots and uses a [CombinedDomainXYPlot](https://github.com/jfree/jfreechart/blob/master/src/main/java/org/jfree/chart/plot/CombinedDomainXYPlot.java) in all cases. This produces left-to-right horizontal time axes and vertical value axes. The time axis is meant to be shared by all sub-plots. If you need different time axes then you'll need to `build()` multiple charts and lay those out as desired in your app. +Stock chart code and data like this: -Currently also only supports lines, stock market OHLC candlestick charts, and stock market volume bar charts. +``` +ChartBuilder.get() + + .title("Stock Chart Time Series With Weekend Gaps, Lines, and Annotations") + .timeData(timeArray) + + .xyPlot(OhlcPlotBuilder.get().yAxisName("Price").plotWeight(3) + .series(OhlcSeriesBuilder.get().ohlcv(dohlcv).upColor(Color.WHITE).downColor(Color.RED)) + .series(XYTimeSeriesBuilder.get().name("MA(20)").data(sma20).color(Color.MAGENTA).style(SOLID_LINE)) + .series(XYTimeSeriesBuilder.get().name("MA(50)").data(sma50).color(Color.BLUE).style(SOLID_LINE)) + .series(XYTimeSeriesBuilder.get().name("MA(200)").data(sma200).color(Color.RED).style(SOLID_LINE)) + .annotation(XYArrowBuilder.get().x(stockEventDate).y(stockEventPrice).angle(270.0).color(DARK_GREEN) + .textAlign(TextAnchor.BOTTOM_CENTER).text(String.format("%.2f", stockEventPrice))) + .line(LineBuilder.get().horizontal().at(resistanceLevel) + .color(Color.LIGHT_GRAY).style(SOLID_LINE))) + + .xyPlot(VolumeXYPlotBuilder.get().yAxisName("Volume").plotWeight(1) + .series(VolumeXYTimeSeriesBuilder.get().ohlcv(dohlcv).closeUpSeries().color(Color.WHITE)) + .series(VolumeXYTimeSeriesBuilder.get().ohlcv(dohlcv).closeDownSeries().color(Color.RED)) + .series(XYTimeSeriesBuilder.get().name("MA(90)").data(volSma90).color(Color.BLUE).style(SOLID_LINE)) + .annotation(XYArrowBuilder.get().x(stockEventDate).y(stockEventVolume).angle(270.0).color(DARK_GREEN) + .textAlign(TextAnchor.BOTTOM_CENTER).text(String.format("%.0f", stockEventVolume))) + .line(LineBuilder.get().horizontal().at(volumeLine) + .color(DARK_GREEN).style(SOLID_LINE))) + + .xyPlot(XYPlotBuilder.get().yAxisName("Stoch").yAxisRange(0.0, 100.0).yAxisTickSize(50.0).plotWeight(1) + .series(XYTimeSeriesBuilder.get().name("K(" + K + ")").data(stoch.getPctK()).color(Color.RED).style(SOLID_LINE)) + .series(XYTimeSeriesBuilder.get().name("D(" + D + ")").data(stoch.getPctD()).color(Color.BLUE).style(SOLID_LINE)) + .line(LineBuilder.get().horizontal().at(80.0).color(Color.BLACK).style(SOLID_LINE)) + .line(LineBuilder.get().horizontal().at(50.0).color(Color.BLUE).style(SOLID_LINE)) + .line(LineBuilder.get().horizontal().at(20.0).color(Color.BLACK).style(SOLID_LINE))) + + .build() +``` -In the future, more parameterization may be added like specifying background and axis colors, or even the actual series renderer objects themselves to fully leverage what [jfreechart](https://github.com/jfree/jfreechart) provides. +Produces a chart like this: +![A stock chart time series chart with weekend gaps](./screenshots/stock-chart-time-series-weekend-gaps.png "Screenshot") -## Thread-safety and garbage collection -No thread-safety measures are deliberately taken. If you require thread-safety then either provide deep copies of parameter objects or or synchronize access to builders and data vs. what your app is doing. +## Demo App -Generally, primitive data arrays are copied into **jfreechart** objects. **jfreechart-builder** will maintain references to other objects passed-in like strings, colors, and drawing strokes. When the builders and charts they produce go out of scope, -the objects you provided (and other objects that may be referencing them) should be garbage collected as applicable. +See the [jfreechart-builder-demo](https://github.com/matoos32/jfreechart-builder-demo) for an interactive demo used for development. ## Versioning -The major and minor numbers are the same as the **jfreechart** major and minor used. This is to indicate general compatibility. The incremental ("patch") number is the version of this module. +The major and minor numbers are the same as the **jfreechart** major and minor to denote what version is compatible. The incremental ("patch") number is the monolithic version number of **jfreechart-builder**. -## License +## Branching model -**jfreechart-builder** is not affiliated with the **jfreechart** project but for compatibility is provided under the terms of the same [LGPL 2.1 license](./license-LGPL.txt). +If you want the latest and greatest contributions use the `develop` branch. These commits have not yet been merged into `main` nor received a version tag, but give you a preview of what's to come. -You should be aware of the contents of the **jfreechart-builder** JAR file built form this project. - -It's understood it will contain the compiled `.class` files of only **jfreechart-builder** and should not incorporate any from **jfreechart**, however you must verify its contents to know what your build tools are actually producing. - -If you need clarification on the LGPL vs. Java, please see the [FSF's tech note about it](https://www.gnu.org/licenses/lgpl-java.html). +Each time `develop` is merged into `main`, a version tag is added onto that merge commit so that each commit to `main` represents the next version. ## Incorporating into your project -### Building Prerequisites + +### Prerequisites * JDK 8 or greater [[1](https://openjdk.java.net/)] [[2](https://www.oracle.com/java/)] installed. * [Apache Maven](https://maven.apache.org/) installed. * Internet connection so Maven can download artifacts or you provide and install those into your local Maven repo by alternative means. + ### Installing source code -`git clone` this repo locally. +``` +git clone +``` ### Building and installing the JAR ``` cd path/to/the/cloned/repo -``` -Next, use `git` to checkout the desired branch or tag. +git checkout +``` -If you want to simply build the jar and figure out what to do with it next ... +To simply build the jar and figure out what to do with it next: ``` mvn package @@ -146,7 +172,7 @@ mvn package The jar will be in the `target/` folder. -If you want to build and install the jar into your Maven repo: +To build and install the jar into your Maven repo: ``` mvn install @@ -161,11 +187,31 @@ Add this dependency to your project's `.pom` file: com.jfcbuilder jfreechart-builder - 1.5.0 + 1.5.1 ``` +## Thread-safety and garbage collection + +No thread-safety measures are deliberately taken. If you require thread-safety then provide deep copies of objects, don't share builders, don't share charts, or add synchronization to your business logic. + +Generally, primitive data arrays are copied into **jfreechart** objects. **jfreechart-builder** will maintain references to other objects passed-in like strings, colors, and drawing strokes. When the builders and charts they produce go out of scope, +the objects you provided (and other objects that may be referencing them) should be garbage collected as applicable. + + +## License + +**jfreechart-builder** is not affiliated with the **jfreechart** project but for compatibility is provided under the terms of the same [LGPL 2.1 license](./license-LGPL.txt). + +You should be aware of the contents of the **jfreechart-builder** JAR file built from this project. + +It should contain the compiled `.class` files only of **jfreechart-builder** and should not incorporate any from **jfreechart**, however you must verify its contents to know what the build tools are actually producing. + +If you need clarification on the LGPL vs. Java, please see the [FSF's tech note about it](https://www.gnu.org/licenses/lgpl-java.html). + ## Contributing -Contributions are welcome. The project maintainers' time permitting, merge/pull requests will be reviewed. If accepted they will be merged. No guarantees are made that requests will be reviewed or merged. +Contributions are welcome and will be accepted as the maintainers' time permits. + +Please use indentations of two spaces (no tabs) and wrap lines at a max width of 100 characters. diff --git a/pom.xml b/pom.xml index 3caab75..2be3543 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.jfcbuilder jfreechart-builder - 1.5.0 + 1.5.1 JFreeChartBuilder A builder framework for creating JFreeChart charts. @@ -15,8 +15,8 @@ - GNU Lesser General Public Licence 3 - http://www.gnu.org/licenses/lgpl-3.0.txt + GNU Lesser General Public Licence 2.1 + http://www.gnu.org/licenses/lgpl-2.1.txt repo, manual diff --git a/screenshots/simple-time-series-with-annotations.png b/screenshots/simple-time-series-with-annotations.png new file mode 100644 index 0000000..b601b12 Binary files /dev/null and b/screenshots/simple-time-series-with-annotations.png differ diff --git a/screenshots/stock-chart-time-series-weekend-gaps.png b/screenshots/stock-chart-time-series-weekend-gaps.png index ddbb004..fced1b9 100644 Binary files a/screenshots/stock-chart-time-series-weekend-gaps.png and b/screenshots/stock-chart-time-series-weekend-gaps.png differ diff --git a/src/main/java/com/jfcbuilder/builders/ChartBuilder.java b/src/main/java/com/jfcbuilder/builders/ChartBuilder.java index ede456a..c2d4cdf 100644 --- a/src/main/java/com/jfcbuilder/builders/ChartBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/ChartBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -70,7 +70,7 @@ private ChartBuilder() { * * @return New instance of this class */ - public static ChartBuilder instance() { + public static ChartBuilder get() { return new ChartBuilder(); } @@ -146,8 +146,8 @@ private void checkBuildPreconditions() throws IllegalStateException { /** * Builds the JFreeChart using all configured settings. * - * @return New instance of a JFreeChart corresponding to all configured data sets and settings. - * @throws IllegalStateException If time data to be shared by all plots was not set. + * @return New instance of a JFreeChart corresponding to all configured data sets and settings + * @throws IllegalStateException If time data to be shared by all plots was not set */ public JFreeChart build() { diff --git a/src/main/java/com/jfcbuilder/builders/ITimeSeriesBuilder.java b/src/main/java/com/jfcbuilder/builders/ITimeSeriesBuilder.java index 75c60e7..a8b9505 100644 --- a/src/main/java/com/jfcbuilder/builders/ITimeSeriesBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/ITimeSeriesBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/main/java/com/jfcbuilder/builders/IXYAnnotationBuilder.java b/src/main/java/com/jfcbuilder/builders/IXYAnnotationBuilder.java new file mode 100644 index 0000000..9d239a6 --- /dev/null +++ b/src/main/java/com/jfcbuilder/builders/IXYAnnotationBuilder.java @@ -0,0 +1,107 @@ +/* + * jfreechart-builder: a builder pattern module for working with the jfreechart library + * + * (C) Copyright 2020, by Matt E. and project contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.jfcbuilder.builders; + +import java.awt.Color; + +import org.jfree.chart.annotations.XYAnnotation; +import org.jfree.chart.ui.TextAnchor; + +/** + * Interface for all XYAnnotation builders. + */ +public interface IXYAnnotationBuilder> { + + /** + * Sets the color used to draw the annotation. + * + * @param color The color to be used when drawing the annotation + * @return Reference to this builder for chaining method calls + */ + T color(Color color); + + /** + * Sets the alignment of the annotation relative to the XY anchor point. + * + * @param alignment The TextAnchor specifying the alignment + * @return Reference to this builder for chaining method calls + */ + T textAlign(TextAnchor alignment); + + /** + * Sets the annotation text to be drawn + * + * @param text The text to be drawn + * @return Reference to this builder for chaining method calls + */ + T text(String text); + + /** + * Pads the right side of the text with a number of spaces. Use this to offset the text a desired + * distance from the anchored XY coordinate. + * + * @param n Number of space characters to insert at right of text + * @return Reference to this builder instance for method chaining + */ + T textPaddingRight(int n); + + /** + * Pads the left side of the text with a number of spaces. Use this to offset the text a desired + * distance from the anchored XY coordinate. + * + * @param n Number of space characters to insert at left of text + * @return Reference to this builder instance for method chaining + */ + T textPaddingLeft(int n); + + /** + * Sets the rotational orientation angle of the annotation in degrees. + * + * @param degrees The rotation angle of the annotation + * @return Reference to this builder for chaining method calls + */ + T angle(double degrees); + + /** + * Sets the x-coordinate of the annotation. + * + * @param x The x-coordinate + * @return Reference to this builder for chaining method calls + */ + T x(double x); + + /** + * Sets the y-coordinate of the annotation. + * + * @param y The y-coordinate + * @return Reference to this builder for chaining method calls + */ + T y(double y); + + /** + * Builds the XYAnnotation from all configured data and properties. + * + * @return New instance of an XYAnnotation corresponding to all configured data and properties + * @throws IllegalStateException If the builder is missing properties when {@code build()} is + * called + */ + XYAnnotation build() throws IllegalStateException; +} diff --git a/src/main/java/com/jfcbuilder/builders/IXYDatasetBuilder.java b/src/main/java/com/jfcbuilder/builders/IXYDatasetBuilder.java index 5e83ead..ac59585 100644 --- a/src/main/java/com/jfcbuilder/builders/IXYDatasetBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/IXYDatasetBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -41,16 +41,16 @@ public interface IXYDatasetBuilder> { * This is an optimization to allow clients to supply existing data without having to copy and/or * crop it to match the desired size in chart. * - * @param indexRange The index range to be set. - * @return Reference to this builder instance for method chaining. + * @param indexRange The index range to be set + * @return Reference to this builder instance for method chaining */ T indexRange(ZeroBasedIndexRange indexRange); /** * Sets the time data to be used for generating the XYDataset. * - * @param timeData Ascending date-time values represented as milliseconds since the epoch start. - * @return Reference to this builder instance for method chaining. + * @param timeData Ascending date-time values represented as milliseconds since the epoch start + * @return Reference to this builder instance for method chaining */ T timeData(long[] timeData); diff --git a/src/main/java/com/jfcbuilder/builders/IXYPlotBuilder.java b/src/main/java/com/jfcbuilder/builders/IXYPlotBuilder.java index d798068..31e96cc 100644 --- a/src/main/java/com/jfcbuilder/builders/IXYPlotBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/IXYPlotBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -90,6 +90,15 @@ public interface IXYPlotBuilder> { */ T line(LineBuilder line); + /** + * Registers an IXYAnnotationBuilder whose {@code build()} method will be called to generate its + * XYAnnotation when this plot builder's {@code build()} method is called. + * + * @param annotation The annotation builder representing the annotation that it will build + * @return Reference to this builder instance for method chaining + */ + T annotation(IXYAnnotationBuilder annotation); + /** * Sets the plot weight to be attributed to the plot. A default of 1 will be used if this is not * set. @@ -135,11 +144,12 @@ public interface IXYPlotBuilder> { /** * Builds the XYPlot from all configured data and properties. * - * @return New instance of an XYPlot corresponding to all configured data and properties. + * @return New instance of an XYPlot corresponding to all configured data and properties * @throws IllegalStateException If a XYTimeSeriesBuilder or LineBuilder are not configured, * possibly if at least one XYTimeSeriesBuilder or LineBuilder are not of a specifically * needed sub-type based on the implementation, if a time axis was not set, or if time * data was not set. */ XYPlot build() throws IllegalStateException; -} \ No newline at end of file + +} diff --git a/src/main/java/com/jfcbuilder/builders/IXYTimeSeriesBuilder.java b/src/main/java/com/jfcbuilder/builders/IXYTimeSeriesBuilder.java index dd954ba..ac55da3 100644 --- a/src/main/java/com/jfcbuilder/builders/IXYTimeSeriesBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/IXYTimeSeriesBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/main/java/com/jfcbuilder/builders/LineBuilder.java b/src/main/java/com/jfcbuilder/builders/LineBuilder.java index 06036cd..f2e47ca 100644 --- a/src/main/java/com/jfcbuilder/builders/LineBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/LineBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -55,7 +55,7 @@ private LineBuilder() { * * @return New instance of this class */ - public static LineBuilder instance() { + public static LineBuilder get() { return new LineBuilder(); } @@ -70,6 +70,26 @@ public LineBuilder orientation(Orientation orientation) { return this; } + /** + * Sets the orientation of the line to horizontal. + * + * @return Reference to this builder for chaining method calls + */ + public LineBuilder horizontal() { + this.orientation = Orientation.HORIZONTAL; + return this; + } + + /** + * Sets the orientation of the line to vertical. + * + * @return Reference to this builder for chaining method calls + */ + public LineBuilder vertical() { + this.orientation = Orientation.VERTICAL; + return this; + } + /** * Gets the currently configured line orientation * @@ -85,7 +105,7 @@ public Orientation orientation() { * @param value The desired line value * @return Reference to this builder for chaining method calls */ - public LineBuilder atValue(double value) { + public LineBuilder at(double value) { this.value = value; return this; } diff --git a/src/main/java/com/jfcbuilder/builders/OhlcPlotBuilder.java b/src/main/java/com/jfcbuilder/builders/OhlcPlotBuilder.java index 48dac12..1159cb1 100644 --- a/src/main/java/com/jfcbuilder/builders/OhlcPlotBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/OhlcPlotBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,6 +25,7 @@ import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.CandlestickRenderer; import org.jfree.chart.renderer.xy.StandardXYItemRenderer; @@ -34,6 +35,7 @@ import com.jfcbuilder.builders.renderers.CandlestickRendererBuilder; import com.jfcbuilder.builders.types.BuilderConstants; +import com.jfcbuilder.builders.types.Orientation; import com.jfcbuilder.builders.types.ZeroBasedIndexRange; /** @@ -61,7 +63,7 @@ private OhlcPlotBuilder() { * * @return New instance of this class */ - public static OhlcPlotBuilder instance() { + public static OhlcPlotBuilder get() { return new OhlcPlotBuilder(); } @@ -123,6 +125,12 @@ public OhlcPlotBuilder line(LineBuilder line) { return this; } + @Override + public OhlcPlotBuilder annotation(IXYAnnotationBuilder annotation) { + elements.annotation(annotation); + return this; + } + @Override public OhlcPlotBuilder plotWeight(int weight) { elements.plotWeight(weight); @@ -167,7 +175,7 @@ private void checkBuildPreconditions() throws IllegalStateException { /** * Builds the DOHLC plot. The DOHLC series is shown behind all other series that were added. * - * @return New instance of an plot corresponding to all configured data sets and settings. + * @return New instance of an plot corresponding to all configured data sets and settings * @throws IllegalStateException If an OhlcSeriesBuilder, if time axis was not set, or if time * data was not set. */ @@ -224,6 +232,21 @@ public XYPlot build() throws IllegalStateException { } } + for (LineBuilder builder : elements.unmodifiableLines()) { + ValueMarker line = builder.build(); + + if (builder.orientation() == Orientation.HORIZONTAL) { + plot.addRangeMarker(line); + } else { + plot.addDomainMarker(line); + } + } + + for (IXYAnnotationBuilder builder : elements.unmodifiableAnnotations()) { + // Annotations don't have ability to get their max/min y-value to adjust y-axis range :( + plot.addAnnotation(builder.build()); + } + yAxis.setLabel(elements.yAxisName() + axisSubName.toString()); final int seriesIndex = plot.getSeriesCount(); @@ -262,7 +285,7 @@ private CandlestickRenderer getCandleRenderer() { Color downColor = (ohlcSeriesBuilder != null) ? ohlcSeriesBuilder.downColor() : BuilderConstants.DEFAULT_DOWN_COLOR; - return CandlestickRendererBuilder.instance().upColor(upColor).downColor(downColor).build(); + return CandlestickRendererBuilder.get().upColor(upColor).downColor(downColor).build(); } } diff --git a/src/main/java/com/jfcbuilder/builders/OhlcSeriesBuilder.java b/src/main/java/com/jfcbuilder/builders/OhlcSeriesBuilder.java index e68f5c7..5263882 100644 --- a/src/main/java/com/jfcbuilder/builders/OhlcSeriesBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/OhlcSeriesBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -62,7 +62,7 @@ private OhlcSeriesBuilder() { * * @return New instance of this class */ - public static OhlcSeriesBuilder instance() { + public static OhlcSeriesBuilder get() { return new OhlcSeriesBuilder(); } diff --git a/src/main/java/com/jfcbuilder/builders/VolumeXYPlotBuilder.java b/src/main/java/com/jfcbuilder/builders/VolumeXYPlotBuilder.java index 645267f..13f33ff 100644 --- a/src/main/java/com/jfcbuilder/builders/VolumeXYPlotBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/VolumeXYPlotBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,7 +21,6 @@ package com.jfcbuilder.builders; import java.awt.Color; -import java.util.List; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberTickUnit; @@ -72,7 +71,7 @@ private VolumeXYPlotBuilder() { * * @return New instance of this class */ - public static VolumeXYPlotBuilder instance() { + public static VolumeXYPlotBuilder get() { return new VolumeXYPlotBuilder(); } @@ -181,6 +180,12 @@ public VolumeXYPlotBuilder line(LineBuilder line) { return this; } + @Override + public VolumeXYPlotBuilder annotation(IXYAnnotationBuilder annotation) { + elements.annotation(annotation); + return this; + } + @Override public VolumeXYPlotBuilder plotWeight(int weight) { elements.plotWeight(weight); @@ -216,8 +221,6 @@ public XYPlot build() throws IllegalStateException { elements.checkBuildPreconditions(); - final List> seriesBuilders = elements.unmodifiableSeries(); - final List lineBuilders = elements.unmodifiableLines(); final ValueAxis xAxis = elements.xAxis(); final long[] timeData = elements.timeData(); @@ -231,7 +234,7 @@ public XYPlot build() throws IllegalStateException { final ZeroBasedIndexRange indexRange = elements.indexRange(); - for (IXYTimeSeriesBuilder builder : seriesBuilders) { + for (IXYTimeSeriesBuilder builder : elements.unmodifiableSeries()) { builder.indexRange(indexRange); builder.timeData(timeData); @@ -257,19 +260,6 @@ public XYPlot build() throws IllegalStateException { final XYPlot plot = new XYPlot(xyCollection, xAxis, yAxis, stdXRenderer); - for (LineBuilder line : lineBuilders) { - ValueMarker marker = line.build(); - - if (line.orientation() == Orientation.HORIZONTAL) { - yMax = Math.max(yMax, marker.getValue()); - yMin = Math.min(yMin, marker.getValue()); - plot.addRangeMarker(marker); - } else { - // Vertical lines have infinite y-value so don't count them in y-min/max - plot.addDomainMarker(marker); - } - } - if (uniformVolSeriesBuilder != null) { uniformVolSeriesBuilder.indexRange(indexRange); @@ -332,6 +322,24 @@ public XYPlot build() throws IllegalStateException { } + for (LineBuilder line : elements.unmodifiableLines()) { + ValueMarker marker = line.build(); + + if (line.orientation() == Orientation.HORIZONTAL) { + yMax = Math.max(yMax, marker.getValue()); + yMin = Math.min(yMin, marker.getValue()); + plot.addRangeMarker(marker); + } else { + // Vertical lines have infinite y-value so don't count them in y-min/max + plot.addDomainMarker(marker); + } + } + + for (IXYAnnotationBuilder builder : elements.unmodifiableAnnotations()) { + // Annotations don't have ability to get their max/min y-value to adjust y-axis range :( + plot.addAnnotation(builder.build()); + } + yAxis.setMinorTickMarksVisible(true); yAxis.setMinorTickCount(2); yAxis.setMinorTickMarkOutsideLength(2); @@ -355,7 +363,7 @@ public XYPlot build() throws IllegalStateException { } private XYBarRenderer createXYBarRenderer(Color fillColor, Color outlineColor) { - return XYBarRendererBuilder.instance().fillColor(fillColor).outlineColor(outlineColor).build(); + return XYBarRendererBuilder.get().fillColor(fillColor).outlineColor(outlineColor).build(); } } diff --git a/src/main/java/com/jfcbuilder/builders/VolumeXYTimeSeriesBuilder.java b/src/main/java/com/jfcbuilder/builders/VolumeXYTimeSeriesBuilder.java index 46137e5..5357453 100644 --- a/src/main/java/com/jfcbuilder/builders/VolumeXYTimeSeriesBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/VolumeXYTimeSeriesBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -96,7 +96,7 @@ private VolumeXYTimeSeriesBuilder() { * * @return New instance of this class */ - public static VolumeXYTimeSeriesBuilder instance() { + public static VolumeXYTimeSeriesBuilder get() { return new VolumeXYTimeSeriesBuilder(); } diff --git a/src/main/java/com/jfcbuilder/builders/XYAnnotationElements.java b/src/main/java/com/jfcbuilder/builders/XYAnnotationElements.java new file mode 100644 index 0000000..0b8bb82 --- /dev/null +++ b/src/main/java/com/jfcbuilder/builders/XYAnnotationElements.java @@ -0,0 +1,248 @@ +/* + * jfreechart-builder: a builder pattern module for working with the jfreechart library + * + * (C) Copyright 2020, by Matt E. and project contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.jfcbuilder.builders; + +import java.awt.Color; +import java.util.Collections; + +import org.jfree.chart.ui.TextAnchor; + +import com.jfcbuilder.builders.types.BuilderConstants; + +/** + * Helper class for storing and accessing properties common to different kinds of XYAnnotation + * builders. Intended for use in composition-type implementations. + */ +public class XYAnnotationElements { + + private static final String DEFAULT_TEXT = ""; + private static final int DEFAULT_TEXT_PADDING = 0; + protected static final double DEFAULT_ANGLE = 0.0; + protected static final TextAnchor DEFAULT_TEXT_ALIGNMENT = TextAnchor.BASELINE_RIGHT; + + protected double x; + protected double y; + protected double angle; + protected String text; + private int textPaddingLeft; + private int textPaddingRight; + protected TextAnchor textAlign; + protected Color color; + + /** + * Constructor. + */ + public XYAnnotationElements() { + x = Double.NaN; + y = Double.NaN; + angle = DEFAULT_ANGLE; + text = DEFAULT_TEXT; + textPaddingLeft = DEFAULT_TEXT_PADDING; + textPaddingRight = DEFAULT_TEXT_PADDING; + textAlign = DEFAULT_TEXT_ALIGNMENT; + color = BuilderConstants.DEFAULT_LINE_COLOR; + } + + /** + * Sets the x-coordinate of the annotation. + * + * @param x The x-coordinate + */ + public void x(double x) { + this.x = x; + } + + /** + * Gets the x-coordinate of the annotation. + * + * @return The x-coordinate + */ + public double x() { + return x; + } + + /** + * Sets the y-coordinate of the annotation. + * + * @param y The y-coordinate + */ + public void y(double y) { + this.y = y; + } + + /** + * Gets the y-coordinate of the annotation. + * + * @return The y-coordinate + */ + public double y() { + return y; + } + + /** + * Sets the angular orientation of the annotation in degrees. + * + * @param degrees The annotation orientation angle + */ + public void angle(double degrees) { + angle = Double.isNaN(degrees) ? DEFAULT_ANGLE : degrees; + } + + /** + * Gets the angular orientation of the annotation in degrees. + * + * @return The annotation orientation angle + */ + public double angle() { + return angle; + } + + /** + * Sets the annotation text. + * + * @param text The text to be set + */ + public void text(String text) { + this.text = text == null ? DEFAULT_TEXT : text; + } + + /** + * Gets the annotation text. + * + * @return The annotation's text + */ + public String text() { + return text; + } + + /** + * Gets the number of padding spaces to left side of the text. + * + * @return The number of left side padding spaces + */ + public void textPaddingLeft(int n) { + textPaddingLeft = (n < 0) ? 0 : n; + } + + /** + * Sets the number of padding spaces to left side of the text. Use this to offset the text a + * desired distance from the anchored XY coordinate. + * + * @param n Number of space characters to insert at left of text + */ + public int textPaddingLeft() { + return textPaddingLeft; + } + + /** + * Sets the number of padding spaces to right side of the text. Use this to offset the text a + * desired distance from the anchored XY coordinate. + * + * @param n Number of space characters to insert at right of text + */ + public void textPaddingRight(int n) { + textPaddingRight = (n < 0) ? 0 : n; + } + + /** + * Gets the number of padding spaces to right side of the text. + * + * @return The number of right side padding spaces + */ + public int textPaddingRight() { + return textPaddingRight; + } + + /** + * Sets the annotation's text alignment relative to the anchored XY coordinate. + * + * @param alignment The annotation's text alignment + */ + public void textAlign(TextAnchor alignment) { + this.textAlign = alignment == null ? DEFAULT_TEXT_ALIGNMENT : alignment; + } + + /** + * Gets the annotation's text alignment relative to the anchored XY coordinate. + * + * @return The annotation's text alignment + */ + public TextAnchor textAlignment() { + return textAlign; + } + + /** + * Sets the annotation's color. + * + * @param color The color to be set + */ + public void color(Color color) { + this.color = color == null ? BuilderConstants.DEFAULT_LINE_COLOR : color; + } + + /** + * Gets the color of the annotation + * + * @return The annotation's color + */ + public Color color() { + return color; + } + + /** + * Checks to see if all preconditions for building the annotation are satisfied and throws an + * exception if not. + * + * @throws IllegalStateException If x or y have not been configured. + */ + public void checkBuildPreconditions() throws IllegalStateException { + + if (Double.isNaN(x) || Double.isNaN(y)) { + throw new IllegalStateException("X or Y value not set"); + } + } + + /** + * Helper method to build the left/right padded text to be displayed. + * + * @return The text including the configured left and right padding spaces + */ + public String paddedText() { + + // TODO: Can and should this be made more efficient? Strings are accumulated with += + // would StringBuilder be better for such a low number of +=? How does this + // fare for huge numbers of annotations and/or charts being generated in bulk? + + String label = ""; + + if (textPaddingLeft > 0) { + label += String.join("", Collections.nCopies(textPaddingLeft, " ")); + } + + label += text; + + if (textPaddingRight > 0) { + label += String.join("", Collections.nCopies(textPaddingRight, " ")); + } + return label; + } + +} \ No newline at end of file diff --git a/src/main/java/com/jfcbuilder/builders/XYArrowBuilder.java b/src/main/java/com/jfcbuilder/builders/XYArrowBuilder.java new file mode 100644 index 0000000..ba579e8 --- /dev/null +++ b/src/main/java/com/jfcbuilder/builders/XYArrowBuilder.java @@ -0,0 +1,128 @@ +/* + * jfreechart-builder: a builder pattern module for working with the jfreechart library + * + * (C) Copyright 2020, by Matt E. and project contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.jfcbuilder.builders; + +import java.awt.Color; + +import org.jfree.chart.annotations.XYAnnotation; +import org.jfree.chart.annotations.XYPointerAnnotation; +import org.jfree.chart.ui.TextAnchor; + +/** + * Builder for producing XYPointerAnnotation (arrow) annotations. + */ +public class XYArrowBuilder implements IXYAnnotationBuilder { + + /** + * radial length of the arrow. + */ + private static final double DEFAULT_ARROW_LENGTH = 20.0; + + /** + * Radial spacing the tip of the arrow will be away from the anchored XY coordinate. + */ + private static final double DEFAULT_XY_COORD_SPACING = 5.0; + + private XYAnnotationElements elems; + + /** + * Hidden constructor. + */ + private XYArrowBuilder() { + elems = new XYAnnotationElements(); + } + + /** + * Factory method for obtaining new instances of this class. + * + * @return New instance of this class + */ + public static XYArrowBuilder get() { + return new XYArrowBuilder(); + } + + @Override + public XYArrowBuilder x(double x) { + elems.x(x); + return this; + } + + @Override + public XYArrowBuilder y(double y) { + elems.y(y); + return this; + } + + @Override + public XYArrowBuilder angle(double degrees) { + elems.angle(degrees); + return this; + } + + @Override + public XYArrowBuilder text(String text) { + elems.text(text); + return this; + } + + @Override + public XYArrowBuilder textPaddingLeft(int n) { + elems.textPaddingLeft(n); + return this; + } + + @Override + public XYArrowBuilder textPaddingRight(int n) { + elems.textPaddingRight(n); + return this; + } + + @Override + public XYArrowBuilder textAlign(TextAnchor alignment) { + elems.textAlign(alignment); + return this; + } + + @Override + public XYArrowBuilder color(Color color) { + elems.color(color); + return this; + } + + private void checkBuildPreconditions() throws IllegalStateException { + elems.checkBuildPreconditions(); + } + + @Override + public XYAnnotation build() throws IllegalStateException { + + checkBuildPreconditions(); + + XYPointerAnnotation arrow = new XYPointerAnnotation(elems.paddedText(), elems.x(), elems.y(), + Math.toRadians(elems.angle())); + arrow.setTextAnchor(elems.textAlignment()); + arrow.setBaseRadius(DEFAULT_ARROW_LENGTH); + arrow.setTipRadius(DEFAULT_XY_COORD_SPACING); + arrow.setArrowPaint(elems.color()); + return arrow; + } + +} diff --git a/src/main/java/com/jfcbuilder/builders/XYPlotBuilder.java b/src/main/java/com/jfcbuilder/builders/XYPlotBuilder.java index a33d3a2..cd77d5a 100644 --- a/src/main/java/com/jfcbuilder/builders/XYPlotBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/XYPlotBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,8 +20,6 @@ package com.jfcbuilder.builders; -import java.util.List; - import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberTickUnit; import org.jfree.chart.axis.ValueAxis; @@ -55,7 +53,7 @@ private XYPlotBuilder() { * * @return New instance of this class */ - public static XYPlotBuilder instance() { + public static XYPlotBuilder get() { return new XYPlotBuilder(); } @@ -95,6 +93,12 @@ public XYPlotBuilder line(LineBuilder line) { return this; } + @Override + public XYPlotBuilder annotation(IXYAnnotationBuilder annotation) { + elements.annotation(annotation); + return this; + } + @Override public XYPlotBuilder plotWeight(int weight) { elements.plotWeight(weight); @@ -129,8 +133,6 @@ public XYPlot build() throws IllegalStateException { elements.checkBuildPreconditions(); - final List> seriesBuilders = elements.unmodifiableSeries(); - final List lineBuilders = elements.unmodifiableLines(); final ValueAxis xAxis = elements.xAxis(); final long[] timeData = elements.timeData(); @@ -144,7 +146,7 @@ public XYPlot build() throws IllegalStateException { final ZeroBasedIndexRange indexRange = elements.indexRange(); - for (IXYTimeSeriesBuilder builder : seriesBuilders) { + for (IXYTimeSeriesBuilder builder : elements.unmodifiableSeries()) { builder.indexRange(indexRange); builder.timeData(timeData); @@ -177,7 +179,7 @@ public XYPlot build() throws IllegalStateException { final XYPlot plot = new XYPlot(collection, xAxis, yAxis, renderer); - for (LineBuilder builder : lineBuilders) { + for (LineBuilder builder : elements.unmodifiableLines()) { ValueMarker line = builder.build(); yMax = Math.max(yMax, line.getValue()); @@ -190,6 +192,11 @@ public XYPlot build() throws IllegalStateException { } } + for (IXYAnnotationBuilder builder : elements.unmodifiableAnnotations()) { + // Annotations don't have ability to get their max/min y-value to adjust y-axis range :( + plot.addAnnotation(builder.build()); + } + if (elements.yAxisRange() != null) { yAxis.setRange(elements.yAxisRange()); } else { @@ -204,4 +211,5 @@ public XYPlot build() throws IllegalStateException { return plot; } + } diff --git a/src/main/java/com/jfcbuilder/builders/XYTextBuilder.java b/src/main/java/com/jfcbuilder/builders/XYTextBuilder.java new file mode 100644 index 0000000..923a3d3 --- /dev/null +++ b/src/main/java/com/jfcbuilder/builders/XYTextBuilder.java @@ -0,0 +1,116 @@ +/* + * jfreechart-builder: a builder pattern module for working with the jfreechart library + * + * (C) Copyright 2020, by Matt E. and project contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.jfcbuilder.builders; + +import java.awt.Color; + +import org.jfree.chart.annotations.XYAnnotation; +import org.jfree.chart.annotations.XYTextAnnotation; +import org.jfree.chart.ui.TextAnchor; + +/** + * Builder for producing XYTextAnnotation instances. + */ +public class XYTextBuilder implements IXYAnnotationBuilder { + + private XYAnnotationElements elems; + + /** + * Hidden constructor. + */ + private XYTextBuilder() { + elems = new XYAnnotationElements(); + } + + /** + * Factory method for obtaining new instances of this class. + * + * @return New instance of this class + */ + public static XYTextBuilder get() { + return new XYTextBuilder(); + } + + @Override + public XYTextBuilder x(double x) { + elems.x(x); + return this; + } + + @Override + public XYTextBuilder y(double y) { + elems.y(y); + return this; + } + + @Override + public XYTextBuilder angle(double degrees) { + elems.angle(degrees); + return this; + } + + @Override + public XYTextBuilder text(String text) { + elems.text(text); + return this; + } + + @Override + public XYTextBuilder textAlign(TextAnchor alignment) { + elems.textAlign(alignment); + return this; + } + + @Override + public XYTextBuilder color(Color color) { + elems.color(color); + return this; + } + + @Override + public XYTextBuilder textPaddingLeft(int n) { + elems.textPaddingLeft(n); + return this; + } + + @Override + public XYTextBuilder textPaddingRight(int n) { + elems.textPaddingRight(n); + return this; + } + + private void checkBuildPreconditions() throws IllegalStateException { + elems.checkBuildPreconditions(); + } + + @Override + public XYAnnotation build() throws IllegalStateException { + + checkBuildPreconditions(); + + XYTextAnnotation txt = new XYTextAnnotation(elems.paddedText(), elems.x(), elems.y()); + txt.setTextAnchor(elems.textAlignment()); + txt.setRotationAngle(Math.toRadians(elems.angle())); + txt.setPaint(elems.color()); + return txt; + } + +} diff --git a/src/main/java/com/jfcbuilder/builders/XYTimeSeriesBuilder.java b/src/main/java/com/jfcbuilder/builders/XYTimeSeriesBuilder.java index bbf7d34..4dd0aaf 100644 --- a/src/main/java/com/jfcbuilder/builders/XYTimeSeriesBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/XYTimeSeriesBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -49,7 +49,7 @@ private XYTimeSeriesBuilder() { * * @return New instance of this class */ - public static XYTimeSeriesBuilder instance() { + public static XYTimeSeriesBuilder get() { return new XYTimeSeriesBuilder(); } diff --git a/src/main/java/com/jfcbuilder/builders/XYTimeSeriesElements.java b/src/main/java/com/jfcbuilder/builders/XYTimeSeriesElements.java index 892dba9..7981fc2 100644 --- a/src/main/java/com/jfcbuilder/builders/XYTimeSeriesElements.java +++ b/src/main/java/com/jfcbuilder/builders/XYTimeSeriesElements.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/main/java/com/jfcbuilder/builders/XYTimeSeriesPlotBuilderElements.java b/src/main/java/com/jfcbuilder/builders/XYTimeSeriesPlotBuilderElements.java index 86e5ee0..e61f3eb 100644 --- a/src/main/java/com/jfcbuilder/builders/XYTimeSeriesPlotBuilderElements.java +++ b/src/main/java/com/jfcbuilder/builders/XYTimeSeriesPlotBuilderElements.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -48,6 +48,7 @@ public class XYTimeSeriesPlotBuilderElements { private List> seriesBuilders; private List> datasetBuilders; private List lineBuilders; + private List> annotationBuilders; private ValueAxis xAxis; private long[] timeData; private ZeroBasedIndexRange indexRange; @@ -63,6 +64,7 @@ public XYTimeSeriesPlotBuilderElements() { seriesBuilders = new ArrayList<>(); datasetBuilders = new ArrayList<>(); lineBuilders = new ArrayList<>(); + annotationBuilders = new ArrayList<>(); xAxis = null; timeData = null; indexRange = null; @@ -108,7 +110,7 @@ public void xAxis(ValueAxis xAxis) { public ValueAxis xAxis() { return xAxis; } - + /** * Sets the time data to be used with all data series. Values should be in ascending order and * representing milliseconds since the epoch start. @@ -189,6 +191,26 @@ public List unmodifiableLines() { return Collections.unmodifiableList(lineBuilders); } + /** + * Registers an IXYAnnotationBuilder to be used for building the plot. + * + * @param annotation The builder to be registered + */ + public void annotation(IXYAnnotationBuilder annotation) { + if (annotation != null) { + annotationBuilders.add(annotation); + } + } + + /** + * Gets an unmodifiable list of the annotation builders to be used for building the plot. + * + * @return The unmodifiable list of annotation builders + */ + public List> unmodifiableAnnotations() { + return Collections.unmodifiableList(annotationBuilders); + } + /** * Sets the layout weight to be used when rendering the plot. * diff --git a/src/main/java/com/jfcbuilder/builders/renderers/CandlestickRendererBuilder.java b/src/main/java/com/jfcbuilder/builders/renderers/CandlestickRendererBuilder.java index ab4f213..034a391 100644 --- a/src/main/java/com/jfcbuilder/builders/renderers/CandlestickRendererBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/renderers/CandlestickRendererBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -40,7 +40,7 @@ private CandlestickRendererBuilder() { downColor = BuilderConstants.DEFAULT_DOWN_COLOR; } - public static CandlestickRendererBuilder instance() { + public static CandlestickRendererBuilder get() { return new CandlestickRendererBuilder(); } diff --git a/src/main/java/com/jfcbuilder/builders/renderers/XYBarRendererBuilder.java b/src/main/java/com/jfcbuilder/builders/renderers/XYBarRendererBuilder.java index e49792f..0029e16 100644 --- a/src/main/java/com/jfcbuilder/builders/renderers/XYBarRendererBuilder.java +++ b/src/main/java/com/jfcbuilder/builders/renderers/XYBarRendererBuilder.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -44,7 +44,7 @@ private XYBarRendererBuilder() { outlineColor = BuilderConstants.DEFAULT_FILL_COLOR; } - public static XYBarRendererBuilder instance() { + public static XYBarRendererBuilder get() { return new XYBarRendererBuilder(); } diff --git a/src/main/java/com/jfcbuilder/builders/types/BuilderConstants.java b/src/main/java/com/jfcbuilder/builders/types/BuilderConstants.java index 07983ff..5b405c3 100644 --- a/src/main/java/com/jfcbuilder/builders/types/BuilderConstants.java +++ b/src/main/java/com/jfcbuilder/builders/types/BuilderConstants.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/main/java/com/jfcbuilder/builders/types/DohlcvSeries.java b/src/main/java/com/jfcbuilder/builders/types/DohlcvSeries.java index 7bb0ae9..798daed 100644 --- a/src/main/java/com/jfcbuilder/builders/types/DohlcvSeries.java +++ b/src/main/java/com/jfcbuilder/builders/types/DohlcvSeries.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/main/java/com/jfcbuilder/builders/types/OhlcvSeries.java b/src/main/java/com/jfcbuilder/builders/types/OhlcvSeries.java index 41f1f10..fbe0c3e 100644 --- a/src/main/java/com/jfcbuilder/builders/types/OhlcvSeries.java +++ b/src/main/java/com/jfcbuilder/builders/types/OhlcvSeries.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/main/java/com/jfcbuilder/builders/types/Orientation.java b/src/main/java/com/jfcbuilder/builders/types/Orientation.java index b062d45..0bf71ef 100644 --- a/src/main/java/com/jfcbuilder/builders/types/Orientation.java +++ b/src/main/java/com/jfcbuilder/builders/types/Orientation.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/main/java/com/jfcbuilder/builders/types/ZeroBasedIndexRange.java b/src/main/java/com/jfcbuilder/builders/types/ZeroBasedIndexRange.java index f5d36b8..429e85d 100644 --- a/src/main/java/com/jfcbuilder/builders/types/ZeroBasedIndexRange.java +++ b/src/main/java/com/jfcbuilder/builders/types/ZeroBasedIndexRange.java @@ -1,7 +1,7 @@ /* * jfreechart-builder: a builder pattern module for working with the jfreechart library * - * (C) Copyright 2020, by Matt E. + * (C) Copyright 2020, by Matt E. and project contributors * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -39,8 +39,8 @@ public ZeroBasedIndexRange() { /** * Constructor that sets the zero-based indexes defining the range. * - * @param startIndex Zero based index of the start of the range . - * @param endIndex Zero based index of the end of the range. + * @param startIndex Zero based index of the start of the range + * @param endIndex Zero based index of the end of the range * @throws IllegalArgumentException If startIndex or endIndex are negative, or if startIndex is * greater than endIndex. */ @@ -51,8 +51,8 @@ public ZeroBasedIndexRange(int startIndex, int endIndex) throws IllegalArgumentE /** * Sets the zero-based indexes defining the range. * - * @param startIndex Zero based index of the start of the range . - * @param endIndex Zero based index of the end of the range. + * @param startIndex Zero based index of the start of the range + * @param endIndex Zero based index of the end of the range * @throws IllegalArgumentException If startIndex or endIndex are negative, or if startIndex is * greater than endIndex. */