Showing 13 of 57 files from the diff.
Other files ignored by Codecov
README.md has changed.

@@ -0,0 +1,166 @@
Loading
1 +
package de.gsi.chart.renderer.spi.financial.css;
2 +
3 +
import static de.gsi.chart.renderer.spi.financial.css.FinancialColorSchemeConstants.*;
4 +
import static de.gsi.dataset.utils.StreamUtils.CLASSPATH_PREFIX;
5 +
6 +
import javafx.geometry.Insets;
7 +
import javafx.scene.image.Image;
8 +
import javafx.scene.layout.*;
9 +
import javafx.scene.paint.Color;
10 +
11 +
import de.gsi.chart.XYChart;
12 +
import de.gsi.chart.axes.spi.AbstractAxisParameter;
13 +
import de.gsi.chart.renderer.Renderer;
14 +
import de.gsi.chart.renderer.spi.financial.CandleStickRenderer;
15 +
import de.gsi.chart.renderer.spi.financial.HighLowRenderer;
16 +
import de.gsi.dataset.DataSet;
17 +
import de.gsi.dataset.utils.StreamUtils;
18 +
19 +
public class FinancialColorSchemeConfig implements FinancialColorSchemeAware {
20 +
    public void applySchemeToDataset(String theme, String customColorScheme, DataSet dataSet, Renderer renderer) {
21 +
        // customization
22 +
        if (customColorScheme != null) {
23 +
            dataSet.setStyle(customColorScheme);
24 +
            return;
25 +
        }
26 +
        // driven-by CandleStickRenderer
27 +
        if (renderer instanceof CandleStickRenderer) {
28 +
            switch (theme) {
29 +
            case CLASSIC:
30 +
                dataSet.setStyle("strokeWidth=1.6; candleLongColor=green; candleShortColor=red; candleLongWickColor=green; "
31 +
                                 + "candleShortWickColor=red; candleVolumeLongColor=rgba(139,199,194,0.4); candleVolumeShortColor=rgba(235,160,159,0.4)");
32 +
                break;
33 +
            case CLEARLOOK:
34 +
                dataSet.setStyle("strokeWidth=0.9; strokeColor=black; candleLongColor=white; candleShortColor=red; "
35 +
                                 + "candleVolumeLongColor=rgba(139,199,194,0.4); candleVolumeShortColor=rgba(235,160,159,0.4)");
36 +
                break;
37 +
            case SAND:
38 +
                dataSet.setStyle("strokeWidth=0.9; strokeColor=black; candleLongColor=white; candleShortColor=red; "
39 +
                                 + "candleShadowColor=rgba(72,72,72,0.2); candleVolumeLongColor=rgba(139,199,194,0.4); candleVolumeShortColor=rgba(235,160,159,0.4)");
40 +
                break;
41 +
            case BLACKBERRY:
42 +
                dataSet.setStyle("strokeWidth=1.5; strokeColor=black; candleLongColor=#00022e; candleShortColor=#780000; "
43 +
                                 + "candleLongWickColor=white; candleShortWickColor=red; candleVolumeLongColor=rgba(139,199,194,0.4); candleVolumeShortColor=rgba(235,160,159,0.4)");
44 +
                break;
45 +
            case DARK:
46 +
                dataSet.setStyle("strokeWidth=1.5; strokeColor=black; candleLongColor=#298988; candleShortColor=#963838; "
47 +
                                 + "candleLongWickColor=#89e278; candleShortWickColor=#e85656; candleVolumeLongColor=rgba(139,199,194,0.4); candleVolumeShortColor=rgba(235,160,159,0.4)");
48 +
                break;
49 +
            default:
50 +
                throw new IllegalArgumentException("CandleStickRenderer: Not implemented yet. ColorScheme=" + theme);
51 +
            }
52 +
        }
53 +
        // driven-by HighLowRenderer
54 +
        else if (renderer instanceof HighLowRenderer) {
55 +
            switch (theme) {
56 +
            case CLASSIC:
57 +
                dataSet.setStyle("highLowBodyLineWidth=1.6; highLowTickLineWidth=2.0; highLowLongColor=green; highLowLongTickColor=green; "
58 +
                                 + "highLowShortColor=red; highLowShortTickColor=red; highLowVolumeLongColor=rgba(139,199,194,0.4); highLowVolumeShortColor=rgba(235,160,159,0.4)");
59 +
                break;
60 +
            case CLEARLOOK:
61 +
                dataSet.setStyle("highLowBodyLineWidth=1.6; highLowTickLineWidth=2.0; highLowLongColor=black; highLowLongTickColor=black; "
62 +
                                 + "highLowShortColor=red; highLowShortTickColor=red; highLowVolumeLongColor=rgba(139,199,194,0.4); highLowVolumeShortColor=rgba(235,160,159,0.4)");
63 +
                break;
64 +
            case SAND:
65 +
                dataSet.setStyle("highLowBodyLineWidth=1.2; highLowTickLineWidth=1.2; highLowLongColor=black; highLowLongTickColor=black; "
66 +
                                 + "highLowShortColor=red; highLowShortTickColor=red; hiLowShadowColor=rgba(72,72,72,0.2); highLowVolumeLongColor=rgba(139,199,194,0.4); highLowVolumeShortColor=rgba(235,160,159,0.4)");
67 +
                break;
68 +
            case BLACKBERRY:
69 +
                dataSet.setStyle("highLowBodyLineWidth=2.0; highLowTickLineWidth=2.5; highLowLongColor=white; highLowLongTickColor=white; "
70 +
                                 + "highLowShortColor=red; highLowShortTickColor=red; highLowVolumeLongColor=rgba(139,199,194,0.4); highLowVolumeShortColor=rgba(235,160,159,0.4)");
71 +
                break;
72 +
            case DARK:
73 +
                dataSet.setStyle("highLowBodyLineWidth=2.0; highLowTickLineWidth=2.5; highLowLongColor=#89e278; highLowLongTickColor=#89e278; "
74 +
                                 + "highLowShortColor=#e85656; highLowShortTickColor=#e85656; highLowVolumeLongColor=rgba(139,199,194,0.4); highLowVolumeShortColor=rgba(235,160,159,0.4)");
75 +
                break;
76 +
            default:
77 +
                throw new IllegalArgumentException("HighLowRenderer: Not implemented yet. ColorScheme=" + theme);
78 +
            }
79 +
        }
80 +
    }
81 +
82 +
    @Override
83 +
    public void applyTo(String theme, String customColorScheme, XYChart chart) throws Exception {
84 +
        // fill global datasets
85 +
        for (DataSet dataset : chart.getDatasets()) {
86 +
            for (Renderer renderer : chart.getRenderers()) {
87 +
                applySchemeToDataset(theme, customColorScheme, dataset, renderer);
88 +
            }
89 +
        }
90 +
        // fill specific renderer datasets
91 +
        for (Renderer renderer : chart.getRenderers()) {
92 +
            for (DataSet dataset : renderer.getDatasets()) {
93 +
                applySchemeToDataset(theme, customColorScheme, dataset, renderer);
94 +
            }
95 +
        }
96 +
        // predefine axis, grid, an additional chart params
97 +
        switch (theme) {
98 +
        case CLEARLOOK:
99 +
        case CLASSIC:
100 +
            // not yet specific configuration
101 +
            break;
102 +
103 +
        case SAND:
104 +
            chart.getPlotBackground().setBackground(new Background(new BackgroundImage(
105 +
                    new Image(StreamUtils.getInputStream(CLASSPATH_PREFIX + "de/gsi/chart/images/sand.png")),
106 +
                    BackgroundRepeat.REPEAT, BackgroundRepeat.REPEAT, BackgroundPosition.DEFAULT, BackgroundSize.DEFAULT)));
107 +
            chart.getGridRenderer().getVerticalMinorGrid().setVisible(true);
108 +
            chart.getGridRenderer().getVerticalMajorGrid().setVisible(true);
109 +
            chart.getGridRenderer().getHorizontalMajorGrid().setVisible(true);
110 +
            chart.getGridRenderer().getHorizontalMajorGrid().setVisible(true);
111 +
            chart.getGridRenderer().getHorizontalMajorGrid().setStroke(Color.DARKGREY);
112 +
            chart.getGridRenderer().getVerticalMajorGrid().setStroke(Color.DARKGREY);
113 +
            if (chart.getXAxis() instanceof AbstractAxisParameter) {
114 +
                ((AbstractAxisParameter) chart.getXAxis()).setTickLabelFill(Color.BLACK);
115 +
            }
116 +
            if (chart.getYAxis() instanceof AbstractAxisParameter) {
117 +
                ((AbstractAxisParameter) chart.getYAxis()).setTickLabelFill(Color.BLACK);
118 +
            }
119 +
            break;
120 +
121 +
        case BLACKBERRY:
122 +
            chart.getPlotBackground().setBackground(new Background(
123 +
                    new BackgroundFill(Color.rgb(0, 2, 46), CornerRadii.EMPTY, Insets.EMPTY)));
124 +
            chart.getGridRenderer().getVerticalMinorGrid().setVisible(false);
125 +
            chart.getGridRenderer().getVerticalMajorGrid().setVisible(false);
126 +
            chart.getGridRenderer().getHorizontalMajorGrid().setVisible(false);
127 +
            chart.getGridRenderer().getHorizontalMajorGrid().setVisible(false);
128 +
            chart.setTitlePaint(Color.WHITE);
129 +
            if (chart.getXAxis() instanceof AbstractAxisParameter) {
130 +
                ((AbstractAxisParameter) chart.getXAxis()).setTickLabelFill(Color.WHITESMOKE);
131 +
            }
132 +
            if (chart.getYAxis() instanceof AbstractAxisParameter) {
133 +
                ((AbstractAxisParameter) chart.getYAxis()).setTickLabelFill(Color.WHITESMOKE);
134 +
            }
135 +
            break;
136 +
137 +
        case DARK:
138 +
            chart.getPlotBackground().setBackground(new Background(
139 +
                    new BackgroundFill(Color.rgb(47, 47, 47), CornerRadii.EMPTY, Insets.EMPTY)));
140 +
            chart.getGridRenderer().getVerticalMinorGrid().setVisible(false);
141 +
            chart.getGridRenderer().getVerticalMajorGrid().setVisible(false);
142 +
            chart.getGridRenderer().getHorizontalMajorGrid().setVisible(true);
143 +
            chart.getGridRenderer().getHorizontalMinorGrid().setVisible(false);
144 +
            chart.getGridRenderer().getHorizontalMajorGrid().setStroke(Color.rgb(106, 106, 106));
145 +
            chart.setTitlePaint(Color.WHITE);
146 +
            if (chart.getXAxis() instanceof AbstractAxisParameter) {
147 +
                ((AbstractAxisParameter) chart.getXAxis()).setTickLabelFill(Color.rgb(194, 194, 194));
148 +
            }
149 +
            if (chart.getYAxis() instanceof AbstractAxisParameter) {
150 +
                ((AbstractAxisParameter) chart.getYAxis()).setTickLabelFill(Color.rgb(194, 194, 194));
151 +
            }
152 +
            break;
153 +
        default:
154 +
            throw new IllegalArgumentException("Theme is not implemented yet. Theme=" + theme);
155 +
        }
156 +
    }
157 +
158 +
    @Override
159 +
    public void applyTo(String theme, XYChart chart) throws Exception {
160 +
        applyTo(theme, null, chart);
161 +
    }
162 +
163 +
    public void applySchemeToDataset(String theme, DataSet dataSet, Renderer renderer) {
164 +
        applySchemeToDataset(theme, null, dataSet, renderer);
165 +
    }
166 +
}

@@ -0,0 +1,224 @@
Loading
1 +
package de.gsi.dataset.spi.financial.api.attrs;
2 +
3 +
import java.util.HashMap;
4 +
import java.util.LinkedHashMap;
5 +
import java.util.Map;
6 +
import java.util.Set;
7 +
8 +
public class AttributeModel implements Cloneable {
9 +
    private Map<AttributeKey<?>, Object> attributes;
10 +
11 +
    public AttributeModel() {
12 +
        attributes = new LinkedHashMap<>();
13 +
    }
14 +
15 +
    public AttributeModel(Map<AttributeKey<?>, Object> attributes) {
16 +
        this.attributes = attributes;
17 +
    }
18 +
19 +
    /**
20 +
     * @return factory method for builder template
21 +
     */
22 +
    public static AttributeModel configure() {
23 +
        return new AttributeModel();
24 +
    }
25 +
26 +
    /**
27 +
     * @param template configure model by given filled model
28 +
     * @return factory method for builder template
29 +
     */
30 +
    public static AttributeModel configure(AttributeModel template) {
31 +
        return template.deepCopyAttributes();
32 +
    }
33 +
34 +
    /**
35 +
     * Returns an attribute of the plugin model
36 +
     *
37 +
     * @param key Key which identifies the attribute
38 +
     * @param <T> Type of the value
39 +
     * @return Attribute value or <code>null</code> if the attribute is not set
40 +
     */
41 +
    @SuppressWarnings("unchecked")
42 +
    public <T> T getAttribute(AttributeKey<T> key) {
43 +
        return (T) attributes.get(key);
44 +
    }
45 +
46 +
    /**
47 +
     * Returns an attribute value as required
48 +
     *
49 +
     * @param key Key which identifies the attribute
50 +
     * @param <T> Type of the value
51 +
     * @return Attribute value, <code>null</code> - throws IllegalArgumentException - required attributes
52 +
     */
53 +
    public <T> T getRequiredAttribute(AttributeKey<T> key) {
54 +
        T value = getAttribute(key);
55 +
        if (value == null) {
56 +
            throw new IllegalArgumentException("The attribute " + key + " is required!");
57 +
        }
58 +
        return value;
59 +
    }
60 +
61 +
    /**
62 +
     * Returns an attribute of the model
63 +
     *
64 +
     * @param key          Key which identifies the attribute
65 +
     * @param <T>          Type of the value
66 +
     * @param defaultValue value is used, if the value doesn't exist
67 +
     * @return Attribute value or <code>default value</code> if the attribute is not set
68 +
     */
69 +
    @SuppressWarnings("unchecked")
70 +
    public <T> T getAttribute(AttributeKey<T> key, T defaultValue) {
71 +
        T value = (T) attributes.get(key);
72 +
        if (value == null) {
73 +
            return defaultValue;
74 +
        }
75 +
        return value;
76 +
    }
77 +
78 +
    /**
79 +
     * Returns an attribute of the model, if it is not available - the default
80 +
     * value is taken and automatically is set to the model!
81 +
     *
82 +
     * @param key          Key which identifies the attribute
83 +
     * @param <T>          Type of the value
84 +
     * @param defaultValue value is used, if the value doesn't exist, and set to the instance model
85 +
     * @return Attribute value or <code>default value</code> if the attribute is not set
86 +
     */
87 +
    @SuppressWarnings("unchecked")
88 +
    public <T> T getAttributeAndSet(AttributeKey<T> key, T defaultValue) {
89 +
        T value = (T) attributes.get(key);
90 +
        if (value == null) {
91 +
            setAttribute(key, defaultValue);
92 +
            return defaultValue;
93 +
        }
94 +
        return value;
95 +
    }
96 +
97 +
    /**
98 +
     * Returns an attribute of the plugin model
99 +
     *
100 +
     * @param key   Key which identifies the attribute
101 +
     * @param <T>   Type of the value
102 +
     * @param <E>   retype to specific inherited class
103 +
     * @param clazz specific class which will be used for casting
104 +
     * @return Attribute value or <code>default value</code> if the attribute is not set
105 +
     */
106 +
    @SuppressWarnings("unchecked")
107 +
    public <T, E extends T> E getAttribute(AttributeKey<T> key, Class<E> clazz) {
108 +
        T value = (T) attributes.get(key);
109 +
        return (E) value;
110 +
    }
111 +
112 +
    /**
113 +
     * Returns an attribute of the plugin model
114 +
     *
115 +
     * @param key          Key which identifies the attribute
116 +
     * @param <T>          Type of the value
117 +
     * @param <E>          retype to specific inherited class
118 +
     * @param clazz        specific class which will be used for casting
119 +
     * @param defaultValue default value is used, if the value doesn't exist
120 +
     * @return Attribute value or <code>default value</code> if the attribute is not set
121 +
     */
122 +
    @SuppressWarnings("unchecked")
123 +
    public <T, E extends T> E getAttribute(AttributeKey<T> key, Class<E> clazz, E defaultValue) {
124 +
        T value = (T) attributes.get(key);
125 +
        if (value == null) {
126 +
            return defaultValue;
127 +
        }
128 +
        return (E) value;
129 +
    }
130 +
131 +
    /**
132 +
     * Sets an attribute on the plugin model. The key must not be null. If a value is null,
133 +
     * the attribute is removed, so containsAttribute method will return false
134 +
     * after this operation.
135 +
     *
136 +
     * @param key   Key which identifies the attribute
137 +
     * @param <T>   Type of the value
138 +
     * @param value Attribute value
139 +
     * @return this instance, can be used for builder syntax
140 +
     */
141 +
    public <T> AttributeModel setAttribute(AttributeKey<T> key, T value) {
142 +
        if (key == null) {
143 +
            throw new IllegalArgumentException("The attribute key hasn't be null");
144 +
        }
145 +
        if (value == null) {
146 +
            attributes.remove(key);
147 +
        } else {
148 +
            attributes.put(key, value);
149 +
        }
150 +
        return this;
151 +
    }
152 +
153 +
    /**
154 +
     * Returns true if the plugin model contains an attribute specified by given key
155 +
     *
156 +
     * @param key Key which identifies the attribute
157 +
     */
158 +
    public boolean containsAttribute(AttributeKey<?> key) {
159 +
        return attributes.containsKey(key);
160 +
    }
161 +
162 +
    /**
163 +
     * @return provides all attribute keys in the model
164 +
     */
165 +
    public Set<AttributeKey<?>> getAttributes() {
166 +
        return attributes.keySet();
167 +
    }
168 +
169 +
    /**
170 +
     * Merge model to this actual model
171 +
     *
172 +
     * @param model AttributeModel
173 +
     */
174 +
    public synchronized void merge(AttributeModel model) {
175 +
        AttributeModel copiedModel = (AttributeModel) model.clone();
176 +
        // clone the included attribute models
177 +
        for (AttributeKey attributeKey : copiedModel.getAttributes()) {
178 +
            if (AttributeModel.class.isAssignableFrom(attributeKey.getType())) {
179 +
                AttributeModel attributeModel = (AttributeModel) copiedModel.getAttribute(attributeKey);
180 +
                attributeModel = attributeModel.deepCopyAttributes();
181 +
                setAttribute(attributeKey, attributeModel);
182 +
            } else {
183 +
                setAttribute(attributeKey, copiedModel.getAttribute(attributeKey));
184 +
            }
185 +
        }
186 +
    }
187 +
188 +
    /**
189 +
     * @return copy attributes model - deep copy of objects
190 +
     */
191 +
    @SuppressWarnings({ "rawtypes", "unchecked" })
192 +
    public synchronized AttributeModel deepCopyAttributes() {
193 +
        //Cloner cloner = new Cloner();
194 +
        //HashMap<AttributeKey<?>, Object> _attributes = (HashMap)cloner.deepClone(attributes);
195 +
        AttributeModel copiedModel = (AttributeModel) clone();
196 +
        // clone the included attribute models
197 +
        for (AttributeKey attributeKey : copiedModel.getAttributes()) {
198 +
            if (AttributeModel.class.isAssignableFrom(attributeKey.getType())) {
199 +
                AttributeModel attributeModel = (AttributeModel) copiedModel.getAttribute(attributeKey);
200 +
                attributeModel = attributeModel.deepCopyAttributes();
201 +
                copiedModel.setAttribute(attributeKey, attributeModel);
202 +
            }
203 +
        }
204 +
        return copiedModel;
205 +
    }
206 +
207 +
    @SuppressWarnings({ "unchecked", "rawtypes" })
208 +
    @Override
209 +
    public Object clone() {
210 +
        try {
211 +
            AttributeModel model = (AttributeModel) super.clone();
212 +
            model.attributes = (HashMap) ((HashMap) attributes).clone();
213 +
            return model;
214 +
215 +
        } catch (CloneNotSupportedException ignored) {
216 +
        }
217 +
        return null;
218 +
    }
219 +
220 +
    @Override
221 +
    public String toString() {
222 +
        return "AttributeModel [attributes=" + attributes + "]";
223 +
    }
224 +
}

@@ -73,6 +73,14 @@
Loading
73 73
        }
74 74
    }
75 75
76 +
    public static Color getColorPropertyValue(final String style, final String key, final Color defaultColor) {
77 +
        Color color = getColorPropertyValue(style, key);
78 +
        if (color == null) {
79 +
            color = defaultColor;
80 +
        }
81 +
        return color;
82 +
    }
83 +
76 84
    public static double[] getFloatingDecimalArrayPropertyValue(final String style, final String key) {
77 85
        if (style == null || key == null) {
78 86
            return null;
@@ -126,6 +134,14 @@
Loading
126 134
        }
127 135
    }
128 136
137 +
    public static double getFloatingDecimalPropertyValue(String style, String key, double defaultValue) {
138 +
        Double value = getFloatingDecimalPropertyValue(style, key);
139 +
        if (value == null) {
140 +
            value = defaultValue;
141 +
        }
142 +
        return value;
143 +
    }
144 +
129 145
    public static Font getFontPropertyValue(final String style) {
130 146
        if (style == null) {
131 147
            return Font.font(StyleParser.DEFAULT_FONT, StyleParser.DEFAULT_FONT_SIZE);

@@ -0,0 +1,29 @@
Loading
1 +
package de.gsi.chart.renderer.spi.financial.service;
2 +
3 +
import javafx.scene.canvas.GraphicsContext;
4 +
5 +
import de.gsi.dataset.DataSet;
6 +
import de.gsi.dataset.spi.financial.api.attrs.AttributeModelAware;
7 +
import de.gsi.dataset.spi.financial.api.ohlcv.IOhlcvItem;
8 +
import de.gsi.dataset.spi.financial.api.ohlcv.IOhlcvItemAware;
9 +
10 +
/**
11 +
 * Domain object for OHLC/V Renderer Extension Points
12 +
 */
13 +
public class OhlcvRendererEpData {
14 +
    public GraphicsContext gc;
15 +
    public DataSet ds;
16 +
    public AttributeModelAware attrs; // addon (if available)
17 +
    public IOhlcvItemAware ohlcvItemAware; // get item by index (if available)
18 +
    public IOhlcvItem ohlcvItem; // item domain object (if available)
19 +
    public int index; // index of rendered bar
20 +
    public double barWidth; // width of bar
21 +
    public double barWidthHalf; // half of bar
22 +
    public double xCenter; // x0 center of bar
23 +
    public double yOpen; // open in display coords
24 +
    public double yHigh; // high in display coords
25 +
    public double yLow; // low in display coords
26 +
    public double yClose; // close in display coords
27 +
    public double yDiff; // diff = open - close
28 +
    public double yMin; // minimal y coord of bar
29 +
}

@@ -0,0 +1,296 @@
Loading
1 +
package de.gsi.chart.renderer.spi.financial;
2 +
3 +
import static de.gsi.chart.renderer.spi.financial.css.FinancialCss.*;
4 +
import static de.gsi.dataset.DataSet.DIM_X;
5 +
6 +
import java.security.InvalidParameterException;
7 +
import java.util.ArrayList;
8 +
import java.util.List;
9 +
10 +
import javafx.collections.ObservableList;
11 +
import javafx.scene.canvas.Canvas;
12 +
import javafx.scene.canvas.GraphicsContext;
13 +
import javafx.scene.paint.Color;
14 +
import javafx.scene.paint.Paint;
15 +
16 +
import de.gsi.chart.Chart;
17 +
import de.gsi.chart.XYChart;
18 +
import de.gsi.chart.axes.Axis;
19 +
import de.gsi.chart.axes.spi.CategoryAxis;
20 +
import de.gsi.chart.renderer.Renderer;
21 +
import de.gsi.chart.renderer.spi.financial.service.OhlcvRendererEpData;
22 +
import de.gsi.chart.renderer.spi.financial.service.RendererPaintAfterEP;
23 +
import de.gsi.chart.renderer.spi.utils.DefaultRenderColorScheme;
24 +
import de.gsi.chart.utils.StyleParser;
25 +
import de.gsi.dataset.DataSet;
26 +
import de.gsi.dataset.spi.financial.OhlcvDataSet;
27 +
import de.gsi.dataset.spi.financial.api.attrs.AttributeModelAware;
28 +
import de.gsi.dataset.spi.financial.api.ohlcv.IOhlcvItemAware;
29 +
import de.gsi.dataset.utils.ProcessingProfiler;
30 +
31 +
/**
32 +
 * <h1>High-Low renderer (OHLC-V/OI Chart)</h1>
33 +
 *<p>
34 +
 * An open-high-low-close chart (also OHLC) is a type of chart typically used to illustrate movements in the price of a financial instrument over time.
35 +
 * Each vertical line on the chart shows the price range (the highest and lowest prices) over one unit of time, e.g., one day or one hour.
36 +
 * Tick marks project from each side of the line indicating the opening price (e.g., for a daily bar chart this would be the starting price for that day) on the left,
37 +
 * and the closing price for that time period on the right. The bars may be shown in different hues depending on whether prices rose or fell in that period.
38 +
 *<p>
39 +
 * The OHLC bars do not require color or fill pattern to show the Open and Close levels, and they do not create confusion in cases when,
40 +
 * for example, the Open price is lower than the Close price (a bullish sign), but the Close price for the studied bar is lower
41 +
 * than the Close price for the previous bar, i.e. the bar to the left on the same chart (a bearish sign).
42 +
 *
43 +
 * @see <a href="https://www.investopedia.com/terms/o/ohlcchart.asp">OHLC Chart Ivestopedia</a>
44 +
 *
45 +
 * @author A.Fischer
46 +
 */
47 +
@SuppressWarnings({ "PMD.ExcessiveMethodLength", "PMD.NPathComplexity", "PMD.ExcessiveParameterList" })
48 +
// designated purpose of this class
49 +
public class HighLowRenderer extends AbstractFinancialRenderer<HighLowRenderer> implements Renderer {
50 +
    private final boolean paintVolume;
51 +
    private final FindAreaDistances findAreaDistances;
52 +
53 +
    protected List<RendererPaintAfterEP> paintAfterEPS = new ArrayList<>();
54 +
55 +
    public HighLowRenderer(boolean paintVolume) {
56 +
        this.paintVolume = paintVolume;
57 +
        this.findAreaDistances = paintVolume ? new XMinVolumeMaxAreaDistances() : new XMinAreaDistances();
58 +
    }
59 +
60 +
    public HighLowRenderer() {
61 +
        this(false);
62 +
    }
63 +
64 +
    public boolean isPaintVolume() {
65 +
        return paintVolume;
66 +
    }
67 +
68 +
    @Override
69 +
    public Canvas drawLegendSymbol(DataSet dataSet, int dsIndex, int width, int height) {
70 +
        final Canvas canvas = new Canvas(width, height);
71 +
        final GraphicsContext gc = canvas.getGraphicsContext2D();
72 +
        final String style = dataSet.getStyle();
73 +
74 +
        gc.save();
75 +
        Color longBodyColor = StyleParser.getColorPropertyValue(style, DATASET_HILOW_BODY_LONG_COLOR, Color.GREEN);
76 +
        Color shortBodyColor = StyleParser.getColorPropertyValue(style, DATASET_HILOW_BODY_SHORT_COLOR, Color.RED);
77 +
78 +
        gc.setStroke(shortBodyColor);
79 +
        double x = width / 4.0;
80 +
        gc.strokeLine(2, 3, x, 3);
81 +
        gc.strokeLine(x, height - 4.0, width / 2.0 - 2.0, height - 4.0);
82 +
        gc.strokeLine(x, 1, x, height - 2.0);
83 +
84 +
        gc.setStroke(longBodyColor);
85 +
        x = 3.0 * width / 4.0;
86 +
        gc.strokeLine(x - 3.0, height - 8.0, x, height - 8.0);
87 +
        gc.strokeLine(x, 5.0, x + 3.0, 5.0);
88 +
        gc.strokeLine(x, 2, x, height - 2.0);
89 +
        gc.restore();
90 +
91 +
        return canvas;
92 +
    }
93 +
94 +
    @Override
95 +
    protected HighLowRenderer getThis() {
96 +
        return this;
97 +
    }
98 +
99 +
    @Override
100 +
    public List<DataSet> render(final GraphicsContext gc, final Chart chart, final int dataSetOffset,
101 +
            final ObservableList<DataSet> datasets) {
102 +
        if (!(chart instanceof XYChart)) {
103 +
            throw new InvalidParameterException(
104 +
                    "must be derivative of XYChart for renderer - " + this.getClass().getSimpleName());
105 +
        }
106 +
        final XYChart xyChart = (XYChart) chart;
107 +
108 +
        // make local copy and add renderer specific data sets
109 +
        final List<DataSet> localDataSetList = new ArrayList<>(datasets);
110 +
        localDataSetList.addAll(super.getDatasets());
111 +
112 +
        long start = 0;
113 +
        if (ProcessingProfiler.getDebugState()) {
114 +
            start = ProcessingProfiler.getTimeStamp();
115 +
        }
116 +
117 +
        final Axis xAxis = xyChart.getXAxis();
118 +
        final Axis yAxis = xyChart.getYAxis();
119 +
120 +
        final double xAxisWidth = xAxis.getWidth();
121 +
        final double xmin = xAxis.getValueForDisplay(0);
122 +
        final double xmax = xAxis.getValueForDisplay(xAxisWidth);
123 +
        int index = 0;
124 +
125 +
        for (final DataSet ds : localDataSetList) {
126 +
            if (ds.getDimension() < 7)
127 +
                continue;
128 +
            final int lindex = index;
129 +
130 +
            ds.lock().readLockGuardOptimistic(() -> {
131 +
                // update categories in case of category axes for the first (index == '0') indexed data set
132 +
                if (lindex == 0 && xyChart.getXAxis() instanceof CategoryAxis) {
133 +
                    final CategoryAxis axis = (CategoryAxis) xyChart.getXAxis();
134 +
                    axis.updateCategories(ds);
135 +
                }
136 +
                AttributeModelAware attrs = null;
137 +
                if (ds instanceof AttributeModelAware) {
138 +
                    attrs = (AttributeModelAware) ds;
139 +
                }
140 +
                IOhlcvItemAware itemAware = null;
141 +
                if (ds instanceof IOhlcvItemAware) {
142 +
                    itemAware = (IOhlcvItemAware) ds;
143 +
                }
144 +
                boolean isEpAvailable = !paintAfterEPS.isEmpty() || paintBarMarker != null;
145 +
146 +
                gc.save();
147 +
                // default styling level
148 +
                String style = ds.getStyle();
149 +
                DefaultRenderColorScheme.setLineScheme(gc, style, lindex);
150 +
                DefaultRenderColorScheme.setGraphicsContextAttributes(gc, style);
151 +
                // financial styling level
152 +
                Color longBodyColor = StyleParser.getColorPropertyValue(style, DATASET_HILOW_BODY_LONG_COLOR, Color.GREEN);
153 +
                Color shortBodyColor = StyleParser.getColorPropertyValue(style, DATASET_HILOW_BODY_SHORT_COLOR, Color.RED);
154 +
                Color longTickColor = StyleParser.getColorPropertyValue(style, DATASET_HILOW_TICK_LONG_COLOR, Color.GREEN);
155 +
                Color shortTickColor = StyleParser.getColorPropertyValue(style, DATASET_HILOW_TICK_SHORT_COLOR, Color.RED);
156 +
                Color hiLowShadowColor = StyleParser.getColorPropertyValue(style, DATASET_HILOW_SHADOW_COLOR, null);
157 +
                Color candleVolumeLongColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_VOLUME_LONG_COLOR, Color.rgb(139, 199, 194, 0.2));
158 +
                Color candleVolumeShortColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_VOLUME_SHORT_COLOR, Color.rgb(235, 160, 159, 0.2));
159 +
                double bodyLineWidth = StyleParser.getFloatingDecimalPropertyValue(style, DATASET_HILOW_BODY_LINEWIDTH, 1.2d);
160 +
                double tickLineWidth = StyleParser.getFloatingDecimalPropertyValue(style, DATASET_HILOW_TICK_LINEWIDTH, 1.2d);
161 +
                double barWidthPercent = StyleParser.getFloatingDecimalPropertyValue(style, DATASET_HILOW_BAR_WIDTH_PERCENTAGE, 0.6d);
162 +
                double shadowLineWidth = StyleParser.getFloatingDecimalPropertyValue(style, DATASET_SHADOW_LINE_WIDTH, 2.5d);
163 +
                double shadowTransPercent = StyleParser.getFloatingDecimalPropertyValue(style, DATASET_SHADOW_TRANSPOSITION_PERCENT, 0.5d);
164 +
165 +
                if (ds.getDataCount() > 0) {
166 +
                    int iMin = ds.getIndex(DIM_X, xmin);
167 +
                    if (iMin < 0)
168 +
                        iMin = 0;
169 +
                    int iMax = Math.min(ds.getIndex(DIM_X, xmax) + 1, ds.getDataCount());
170 +
171 +
                    double[] distances = null;
172 +
                    double minRequiredWidth = 0.0;
173 +
                    if (lindex == 0) {
174 +
                        distances = findAreaDistances(findAreaDistances, ds, xAxis, yAxis, xmin, xmax);
175 +
                        minRequiredWidth = distances[0];
176 +
                    }
177 +
                    double localBarWidth = minRequiredWidth * barWidthPercent;
178 +
                    double barWidthHalf = localBarWidth / 2.0;
179 +
180 +
                    for (int i = iMin; i < iMax; i++) {
181 +
                        double x0 = xAxis.getDisplayPosition(ds.get(DIM_X, i));
182 +
                        double yOpen = yAxis.getDisplayPosition(ds.get(OhlcvDataSet.DIM_Y_OPEN, i));
183 +
                        double yHigh = yAxis.getDisplayPosition(ds.get(OhlcvDataSet.DIM_Y_HIGH, i));
184 +
                        double yLow = yAxis.getDisplayPosition(ds.get(OhlcvDataSet.DIM_Y_LOW, i));
185 +
                        double yClose = yAxis.getDisplayPosition(ds.get(OhlcvDataSet.DIM_Y_CLOSE, i));
186 +
187 +
                        // prepare extension point data (if EPs available)
188 +
                        OhlcvRendererEpData data = null;
189 +
                        if (isEpAvailable) {
190 +
                            data = new OhlcvRendererEpData();
191 +
                            data.gc = gc;
192 +
                            data.ds = ds;
193 +
                            data.attrs = attrs;
194 +
                            data.ohlcvItemAware = itemAware;
195 +
                            data.ohlcvItem = itemAware != null ? itemAware.getItem(i) : null;
196 +
                            data.index = i;
197 +
                            data.barWidth = localBarWidth;
198 +
                            data.barWidthHalf = barWidthHalf;
199 +
                            data.xCenter = x0;
200 +
                            data.yOpen = yOpen;
201 +
                            data.yHigh = yHigh;
202 +
                            data.yLow = yLow;
203 +
                            data.yClose = yClose;
204 +
                        }
205 +
206 +
                        // paint volume
207 +
                        if (paintVolume) {
208 +
                            assert distances != null;
209 +
                            paintVolume(gc, ds, i, candleVolumeLongColor, candleVolumeShortColor, yAxis, distances, localBarWidth, barWidthHalf, x0);
210 +
                        }
211 +
212 +
                        // paint shadow
213 +
                        if (hiLowShadowColor != null) {
214 +
                            double lineWidth = gc.getLineWidth();
215 +
                            paintHiLowShadow(gc, hiLowShadowColor, shadowLineWidth, shadowTransPercent, barWidthHalf, x0, yOpen, yClose, yLow, yHigh);
216 +
                            gc.setLineWidth(lineWidth);
217 +
                        }
218 +
219 +
                        // choose color of the bar
220 +
                        Paint barPaint = getPaintBarColor(data);
221 +
222 +
                        // the ohlc body
223 +
                        gc.setStroke(barPaint != null ? barPaint : yOpen > yClose ? longBodyColor : shortBodyColor);
224 +
                        gc.setLineWidth(bodyLineWidth);
225 +
                        gc.strokeLine(x0, yLow, x0, yHigh);
226 +
227 +
                        // paint open/close tick
228 +
                        gc.setStroke(barPaint != null ? barPaint : yOpen > yClose ? longTickColor : shortTickColor);
229 +
                        gc.setLineWidth(tickLineWidth);
230 +
                        gc.strokeLine(x0 - barWidthHalf, yOpen, x0, yOpen);
231 +
                        gc.strokeLine(x0, yClose, x0 + barWidthHalf, yClose);
232 +
233 +
                        // extension point - paint after painting of bar
234 +
                        if (!paintAfterEPS.isEmpty()) {
235 +
                            paintAfter(data);
236 +
                        }
237 +
                    }
238 +
                }
239 +
                gc.restore();
240 +
            });
241 +
            index++;
242 +
        }
243 +
        if (ProcessingProfiler.getDebugState()) {
244 +
            ProcessingProfiler.getTimeDiff(start);
245 +
        }
246 +
247 +
        return localDataSetList;
248 +
    }
249 +
250 +
    /**
251 +
     * Handle extension point PaintAfter
252 +
     *
253 +
     * @param data filled domain object which is provided to external extension points.
254 +
     */
255 +
    protected void paintAfter(OhlcvRendererEpData data) {
256 +
        for (RendererPaintAfterEP paintAfterEP : paintAfterEPS) {
257 +
            paintAfterEP.paintAfter(data);
258 +
        }
259 +
    }
260 +
261 +
    /**
262 +
     * Simple support for HiLow OHLC shadows painting. Without effects - performance problems.
263 +
     * The shadow has to be activated by parameter configuration hiLowShadowColor in css.
264 +
     *
265 +
     * @param gc                 GraphicsContext
266 +
     * @param shadowColor        color for shadow
267 +
     * @param shadowLineWidth    line width for painting shadow
268 +
     * @param shadowTransPercent object transposition for painting shadow in percentage
269 +
     * @param barWidthHalf       half width of bar
270 +
     * @param x0                 the center of the bar for X coordination
271 +
     * @param yOpen              coordination of Open price
272 +
     * @param yClose             coordination of Close price
273 +
     * @param yLow               coordination of Low price
274 +
     * @param yHigh              coordination of High price
275 +
     */
276 +
    protected void paintHiLowShadow(GraphicsContext gc, Color shadowColor, double shadowLineWidth, double shadowTransPercent, double barWidthHalf,
277 +
            double x0, double yOpen, double yClose, double yLow, double yHigh) {
278 +
        double trans = shadowTransPercent * barWidthHalf;
279 +
        gc.setLineWidth(shadowLineWidth);
280 +
        gc.setStroke(shadowColor);
281 +
        gc.strokeLine(x0 + trans, yLow + trans, x0 + trans, yHigh + trans);
282 +
        gc.strokeLine(x0 - barWidthHalf + trans, yOpen + trans, x0 + trans, yOpen + trans);
283 +
        gc.strokeLine(x0 + trans, yClose + trans, x0 + barWidthHalf + trans, yClose + trans);
284 +
    }
285 +
286 +
    //-------------- injections --------------------------------------------
287 +
288 +
    /**
289 +
     * Inject extension point for Paint after bar.
290 +
     *
291 +
     * @param paintAfterEP service
292 +
     */
293 +
    public void addPaintAfterEp(RendererPaintAfterEP paintAfterEP) {
294 +
        paintAfterEPS.add(paintAfterEP);
295 +
    }
296 +
}

@@ -0,0 +1,317 @@
Loading
1 +
package de.gsi.chart.renderer.spi.financial;
2 +
3 +
import static de.gsi.chart.renderer.spi.financial.css.FinancialCss.*;
4 +
import static de.gsi.dataset.DataSet.DIM_X;
5 +
6 +
import java.security.InvalidParameterException;
7 +
import java.util.ArrayList;
8 +
import java.util.List;
9 +
10 +
import javafx.collections.ObservableList;
11 +
import javafx.scene.canvas.Canvas;
12 +
import javafx.scene.canvas.GraphicsContext;
13 +
import javafx.scene.paint.Color;
14 +
import javafx.scene.paint.Paint;
15 +
16 +
import de.gsi.chart.Chart;
17 +
import de.gsi.chart.XYChart;
18 +
import de.gsi.chart.axes.Axis;
19 +
import de.gsi.chart.axes.spi.CategoryAxis;
20 +
import de.gsi.chart.renderer.Renderer;
21 +
import de.gsi.chart.renderer.spi.financial.service.OhlcvRendererEpData;
22 +
import de.gsi.chart.renderer.spi.financial.service.RendererPaintAfterEP;
23 +
import de.gsi.chart.renderer.spi.utils.DefaultRenderColorScheme;
24 +
import de.gsi.chart.utils.StyleParser;
25 +
import de.gsi.dataset.DataSet;
26 +
import de.gsi.dataset.spi.financial.OhlcvDataSet;
27 +
import de.gsi.dataset.spi.financial.api.attrs.AttributeModelAware;
28 +
import de.gsi.dataset.spi.financial.api.ohlcv.IOhlcvItemAware;
29 +
import de.gsi.dataset.utils.ProcessingProfiler;
30 +
31 +
/**
32 +
 * <h1>Candlestick renderer</h1>
33 +
 *<p>
34 +
 * A candlestick chart (also called Japanese candlestick chart) is a style of financial chart used to describe price movements of a security,
35 +
 * derivative, or currency.
36 +
 *<p>
37 +
 * If the asset closed higher than it opened, the body is hollow or unfilled, with the opening price at the bottom of the body and the closing price at the top.
38 +
 * If the asset closed lower than it opened, the body is solid or filled, with the opening price at the top and the closing price at the bottom.
39 +
 * Thus, the color of the candle represents the price movement relative to the prior period's close and the "fill" (solid or hollow)
40 +
 * of the candle represents the price direction of the period in isolation (solid for a higher open and lower close; hollow for a lower open and a higher close).
41 +
 * <p>
42 +
 * A black (or red) candle represents a price action with a lower closing price than the prior candle's close.
43 +
 * A white (or green) candle represents a higher closing price than the prior candle's close.
44 +
 * <p>
45 +
 * In practice, any color can be assigned to rising or falling price candles. A candlestick need not have either a body or a wick.
46 +
 * Generally, the longer the body of the candle, the more intense the trading.
47 +
 *
48 +
 * @see <a href="https://www.investopedia.com/terms/c/candlestick.asp">Candlestick Investopedia</a>
49 +
 *
50 +
 * @author A.Fischer
51 +
 */
52 +
@SuppressWarnings({ "PMD.ExcessiveMethodLength", "PMD.NPathComplexity", "PMD.ExcessiveParameterList" })
53 +
// designated purpose of this class
54 +
public class CandleStickRenderer extends AbstractFinancialRenderer<CandleStickRenderer> implements Renderer {
55 +
    private final boolean paintVolume;
56 +
    private final FindAreaDistances findAreaDistances;
57 +
58 +
    protected List<RendererPaintAfterEP> paintAfterEPS = new ArrayList<>();
59 +
60 +
    public CandleStickRenderer(boolean paintVolume) {
61 +
        this.paintVolume = paintVolume;
62 +
        this.findAreaDistances = paintVolume ? new XMinVolumeMaxAreaDistances() : new XMinAreaDistances();
63 +
    }
64 +
65 +
    public CandleStickRenderer() {
66 +
        this(false);
67 +
    }
68 +
69 +
    public boolean isPaintVolume() {
70 +
        return paintVolume;
71 +
    }
72 +
73 +
    @Override
74 +
    public Canvas drawLegendSymbol(DataSet dataSet, int dsIndex, int width, int height) {
75 +
        final Canvas canvas = new Canvas(width, height);
76 +
        final GraphicsContext gc = canvas.getGraphicsContext2D();
77 +
        final String style = dataSet.getStyle();
78 +
79 +
        gc.save();
80 +
        Color candleLongColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_LONG_COLOR, Color.GREEN);
81 +
        Color candleShortColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_SHORT_COLOR, Color.RED);
82 +
83 +
        gc.setFill(candleLongColor);
84 +
        gc.setStroke(candleLongColor);
85 +
        gc.fillRect(1, 3, width / 2.0 - 2.0, height - 8.0);
86 +
        double x = width / 4.0;
87 +
        gc.strokeLine(x, 1, x, height - 2.0);
88 +
89 +
        gc.setFill(candleShortColor);
90 +
        gc.setStroke(candleShortColor);
91 +
        gc.fillRect(width / 2.0 + 2.0, 4, width - 2.0, height - 12.0);
92 +
        x = 3.0 * width / 4.0 + 1.5;
93 +
        gc.strokeLine(x, 1, x, height - 3.0);
94 +
        gc.restore();
95 +
96 +
        return canvas;
97 +
    }
98 +
99 +
    @Override
100 +
    protected CandleStickRenderer getThis() {
101 +
        return this;
102 +
    }
103 +
104 +
    @Override
105 +
    public List<DataSet> render(final GraphicsContext gc, final Chart chart, final int dataSetOffset,
106 +
            final ObservableList<DataSet> datasets) {
107 +
        if (!(chart instanceof XYChart)) {
108 +
            throw new InvalidParameterException(
109 +
                    "must be derivative of XYChart for renderer - " + this.getClass().getSimpleName());
110 +
        }
111 +
        final XYChart xyChart = (XYChart) chart;
112 +
113 +
        // make local copy and add renderer specific data sets
114 +
        final List<DataSet> localDataSetList = new ArrayList<>(datasets);
115 +
        localDataSetList.addAll(super.getDatasets());
116 +
117 +
        long start = 0;
118 +
        if (ProcessingProfiler.getDebugState()) {
119 +
            start = ProcessingProfiler.getTimeStamp();
120 +
        }
121 +
122 +
        final Axis xAxis = xyChart.getXAxis();
123 +
        final Axis yAxis = xyChart.getYAxis();
124 +
125 +
        final double xAxisWidth = xAxis.getWidth();
126 +
        final double xmin = xAxis.getValueForDisplay(0);
127 +
        final double xmax = xAxis.getValueForDisplay(xAxisWidth);
128 +
        int index = 0;
129 +
130 +
        for (final DataSet ds : localDataSetList) {
131 +
            if (ds.getDimension() < 7)
132 +
                continue;
133 +
            final int lindex = index;
134 +
135 +
            ds.lock().readLockGuardOptimistic(() -> {
136 +
                // update categories in case of category axes for the first (index == '0') indexed data set
137 +
                if (lindex == 0 && xyChart.getXAxis() instanceof CategoryAxis) {
138 +
                    final CategoryAxis axis = (CategoryAxis) xyChart.getXAxis();
139 +
                    axis.updateCategories(ds);
140 +
                }
141 +
                AttributeModelAware attrs = null;
142 +
                if (ds instanceof AttributeModelAware) {
143 +
                    attrs = (AttributeModelAware) ds;
144 +
                }
145 +
                IOhlcvItemAware itemAware = null;
146 +
                if (ds instanceof IOhlcvItemAware) {
147 +
                    itemAware = (IOhlcvItemAware) ds;
148 +
                }
149 +
                boolean isEpAvailable = !paintAfterEPS.isEmpty() || paintBarMarker != null;
150 +
151 +
                gc.save();
152 +
                // default styling level
153 +
                String style = ds.getStyle();
154 +
                DefaultRenderColorScheme.setLineScheme(gc, style, lindex);
155 +
                DefaultRenderColorScheme.setGraphicsContextAttributes(gc, style);
156 +
                // financial styling level
157 +
                Color candleLongColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_LONG_COLOR, Color.GREEN);
158 +
                Color candleShortColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_SHORT_COLOR, Color.RED);
159 +
                Color candleLongWickColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_LONG_WICK_COLOR, Color.BLACK);
160 +
                Color candleShortWickColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_SHORT_WICK_COLOR, Color.BLACK);
161 +
                Color candleShadowColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_SHADOW_COLOR, null);
162 +
                Color candleVolumeLongColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_VOLUME_LONG_COLOR, Color.rgb(139, 199, 194, 0.2));
163 +
                Color candleVolumeShortColor = StyleParser.getColorPropertyValue(style, DATASET_CANDLESTICK_VOLUME_SHORT_COLOR, Color.rgb(235, 160, 159, 0.2));
164 +
                double barWidthPercent = StyleParser.getFloatingDecimalPropertyValue(style, DATASET_CANDLESTICK_BAR_WIDTH_PERCENTAGE, 0.5d);
165 +
                double shadowLineWidth = StyleParser.getFloatingDecimalPropertyValue(style, DATASET_SHADOW_LINE_WIDTH, 2.5d);
166 +
                double shadowTransPercent = StyleParser.getFloatingDecimalPropertyValue(style, DATASET_SHADOW_TRANSPOSITION_PERCENT, 0.5d);
167 +
168 +
                if (ds.getDataCount() > 0) {
169 +
                    int iMin = ds.getIndex(DIM_X, xmin);
170 +
                    if (iMin < 0)
171 +
                        iMin = 0;
172 +
                    int iMax = Math.min(ds.getIndex(DIM_X, xmax) + 1, ds.getDataCount());
173 +
174 +
                    double[] distances = null;
175 +
                    double minRequiredWidth = 0.0;
176 +
                    if (lindex == 0) {
177 +
                        distances = findAreaDistances(findAreaDistances, ds, xAxis, yAxis, xmin, xmax);
178 +
                        minRequiredWidth = distances[0];
179 +
                    }
180 +
                    double localBarWidth = minRequiredWidth * barWidthPercent;
181 +
                    double barWidthHalf = localBarWidth / 2.0;
182 +
183 +
                    for (int i = iMin; i < iMax; i++) {
184 +
                        double x0 = xAxis.getDisplayPosition(ds.get(DIM_X, i));
185 +
                        double yOpen = yAxis.getDisplayPosition(ds.get(OhlcvDataSet.DIM_Y_OPEN, i));
186 +
                        double yHigh = yAxis.getDisplayPosition(ds.get(OhlcvDataSet.DIM_Y_HIGH, i));
187 +
                        double yLow = yAxis.getDisplayPosition(ds.get(OhlcvDataSet.DIM_Y_LOW, i));
188 +
                        double yClose = yAxis.getDisplayPosition(ds.get(OhlcvDataSet.DIM_Y_CLOSE, i));
189 +
190 +
                        double yDiff = yOpen - yClose;
191 +
                        double yMin = yDiff > 0 ? yClose : yOpen;
192 +
193 +
                        // prepare extension point data (if EPs available)
194 +
                        OhlcvRendererEpData data = null;
195 +
                        if (isEpAvailable) {
196 +
                            data = new OhlcvRendererEpData();
197 +
                            data.gc = gc;
198 +
                            data.ds = ds;
199 +
                            data.attrs = attrs;
200 +
                            data.ohlcvItemAware = itemAware;
201 +
                            data.ohlcvItem = itemAware != null ? itemAware.getItem(i) : null;
202 +
                            data.index = i;
203 +
                            data.barWidth = localBarWidth;
204 +
                            data.barWidthHalf = barWidthHalf;
205 +
                            data.xCenter = x0;
206 +
                            data.yOpen = yOpen;
207 +
                            data.yHigh = yHigh;
208 +
                            data.yLow = yLow;
209 +
                            data.yClose = yClose;
210 +
                            data.yDiff = yDiff;
211 +
                            data.yMin = yMin;
212 +
                        }
213 +
214 +
                        // paint volume
215 +
                        if (paintVolume) {
216 +
                            assert distances != null;
217 +
                            paintVolume(gc, ds, i, candleVolumeLongColor, candleVolumeShortColor, yAxis, distances, localBarWidth, barWidthHalf, x0);
218 +
                        }
219 +
220 +
                        // paint shadow
221 +
                        if (candleShadowColor != null) {
222 +
                            double lineWidth = gc.getLineWidth();
223 +
                            paintCandleShadow(gc,
224 +
                                    candleShadowColor, shadowLineWidth, shadowTransPercent,
225 +
                                    localBarWidth, barWidthHalf, x0, yOpen, yClose, yLow, yHigh, yDiff, yMin);
226 +
                            gc.setLineWidth(lineWidth);
227 +
                        }
228 +
229 +
                        // choose color of the bar
230 +
                        Paint barPaint = getPaintBarColor(data);
231 +
232 +
                        if (yDiff > 0) {
233 +
                            gc.setFill(barPaint != null ? barPaint : candleLongColor);
234 +
                            gc.setStroke(barPaint != null ? barPaint : candleLongWickColor);
235 +
                        } else {
236 +
                            yDiff = Math.abs(yDiff);
237 +
                            gc.setFill(barPaint != null ? barPaint : candleShortColor);
238 +
                            gc.setStroke(barPaint != null ? barPaint : candleShortWickColor);
239 +
                        }
240 +
241 +
                        // paint candle
242 +
                        gc.strokeLine(x0, yLow, x0, yDiff > 0 ? yOpen : yClose);
243 +
                        gc.strokeLine(x0, yHigh, x0, yDiff > 0 ? yClose : yOpen);
244 +
                        gc.fillRect(x0 - barWidthHalf, yMin, localBarWidth, yDiff); // open-close
245 +
                        gc.strokeRect(x0 - barWidthHalf, yMin, localBarWidth, yDiff); // open-close
246 +
247 +
                        // extension point - paint after painting of candle
248 +
                        if (!paintAfterEPS.isEmpty()) {
249 +
                            paintAfter(data);
250 +
                        }
251 +
                    }
252 +
                }
253 +
                gc.restore();
254 +
            });
255 +
            index++;
256 +
        }
257 +
        if (ProcessingProfiler.getDebugState()) {
258 +
            ProcessingProfiler.getTimeDiff(start);
259 +
        }
260 +
261 +
        return localDataSetList;
262 +
    }
263 +
264 +
    /**
265 +
     * Handle extension point PaintAfter
266 +
     *
267 +
     * @param data filled domain object which is provided to external extension points.
268 +
     */
269 +
    protected void paintAfter(OhlcvRendererEpData data) {
270 +
        for (RendererPaintAfterEP paintAfterEP : paintAfterEPS) {
271 +
            paintAfterEP.paintAfter(data);
272 +
        }
273 +
    }
274 +
275 +
    /**
276 +
     * Simple support for candle shadows painting. Without effects - performance problems.
277 +
     * The shadow has to be activated by parameter configuration candleShadowColor in css.
278 +
     *
279 +
     * @param gc                 GraphicsContext
280 +
     * @param shadowColor        color for shadow
281 +
     * @param shadowLineWidth    line width for painting shadow
282 +
     * @param shadowTransPercent object transposition for painting shadow in percentage
283 +
     * @param localBarWidth      width of bar
284 +
     * @param barWidthHalf       half width of bar
285 +
     * @param x0                 the center of the bar for X coordination
286 +
     * @param yOpen              coordination of Open price
287 +
     * @param yClose             coordination of Close price
288 +
     * @param yLow               coordination of Low price
289 +
     * @param yHigh              coordination of High price
290 +
     * @param yDiff              Difference of candle for painting candle body
291 +
     * @param yMin               minimal coordination for painting of candle body
292 +
     */
293 +
    protected void paintCandleShadow(GraphicsContext gc, Color shadowColor, double shadowLineWidth, double shadowTransPercent, double localBarWidth, double barWidthHalf,
294 +
            double x0, double yOpen, double yClose, double yLow,
295 +
            double yHigh, double yDiff, double yMin) {
296 +
        double trans = shadowTransPercent * barWidthHalf;
297 +
        gc.setLineWidth(shadowLineWidth);
298 +
        gc.setFill(shadowColor);
299 +
        gc.setStroke(shadowColor);
300 +
        gc.strokeLine(x0 + trans, yLow + trans,
301 +
                x0 + trans, yDiff > 0 ? yOpen + trans : yClose + trans);
302 +
        gc.strokeLine(x0 + trans, yHigh + trans,
303 +
                x0 + trans, yDiff > 0 ? yClose + trans : yOpen + trans);
304 +
        gc.fillRect(x0 - barWidthHalf + trans, yMin + trans, localBarWidth, Math.abs(yDiff));
305 +
    }
306 +
307 +
    //-------------- injections --------------------------------------------
308 +
309 +
    /**
310 +
     * Inject extension point for Paint after candle.
311 +
     *
312 +
     * @param paintAfterEP service
313 +
     */
314 +
    public void addPaintAfterEp(RendererPaintAfterEP paintAfterEP) {
315 +
        paintAfterEPS.add(paintAfterEP);
316 +
    }
317 +
}

@@ -0,0 +1,53 @@
Loading
1 +
package de.gsi.dataset.utils;
2 +
3 +
import java.io.*;
4 +
import java.util.zip.ZipFile;
5 +
6 +
import org.slf4j.Logger;
7 +
import org.slf4j.LoggerFactory;
8 +
9 +
public class StreamUtils {
10 +
    private static final Logger logger = LoggerFactory.getLogger(StreamUtils.class);
11 +
12 +
    public static final String CLASSPATH_PREFIX = "classpath:";
13 +
    public static final String ZIP_PREFIX = "zip:";
14 +
15 +
    /**
16 +
     * Get the resource from the file or the jar package
17 +
     * From jar file has to be used prefix "classpath:"
18 +
     * Zip file resource with prefix: "zip:"
19 +
     *
20 +
     * @param source resource
21 +
     * @return input stream
22 +
     * @throws FileNotFoundException - if the file not found
23 +
     */
24 +
    public static InputStream getInputStream(String source) throws FileNotFoundException {
25 +
        InputStream is;
26 +
        if (source.startsWith(CLASSPATH_PREFIX)) {
27 +
            String resource = source.substring(CLASSPATH_PREFIX.length());
28 +
            is = StreamUtils.class.getClassLoader().getResourceAsStream(resource);
29 +
30 +
        } else if (source.startsWith(ZIP_PREFIX)) {
31 +
            String resource = source.substring(ZIP_PREFIX.length());
32 +
            int zipSuffixIdx = resource.toLowerCase().indexOf(".zip") + 4;
33 +
            String resourceInZip = resource.substring(zipSuffixIdx);
34 +
            if (resourceInZip.startsWith("/")) {
35 +
                resourceInZip = resourceInZip.substring(1);
36 +
            }
37 +
            resource = resource.substring(0, zipSuffixIdx);
38 +
            try (ZipFile zipFile = new ZipFile(resource)) {
39 +
                // do not close zip, it is closed by input stream!
40 +
                is = zipFile.getInputStream(zipFile.getEntry(resourceInZip));
41 +
            } catch (IOException e) {
42 +
                throw new FileNotFoundException(
43 +
                        "Zip resource not found for " + source + " IOException=" + e.getMessage());
44 +
            }
45 +
        } else {
46 +
            is = new FileInputStream(new File(source));
47 +
        }
48 +
        if (is == null) {
49 +
            logger.error("The resource is not found: " + source);
50 +
        }
51 +
        return is;
52 +
    }
53 +
}

@@ -0,0 +1,139 @@
Loading
1 +
package de.gsi.dataset.spi.financial;
2 +
3 +
import java.util.Iterator;
4 +
5 +
import de.gsi.dataset.DataSet;
6 +
import de.gsi.dataset.spi.AbstractDataSet;
7 +
import de.gsi.dataset.spi.financial.api.attrs.AttributeModel;
8 +
import de.gsi.dataset.spi.financial.api.attrs.AttributeModelAware;
9 +
import de.gsi.dataset.spi.financial.api.ohlcv.IOhlcv;
10 +
import de.gsi.dataset.spi.financial.api.ohlcv.IOhlcvItem;
11 +
import de.gsi.dataset.spi.financial.api.ohlcv.IOhlcvItemAware;
12 +
13 +
/**
14 +
 * Dataset Domain Object for encapsulation of IOhlcv API structure.
15 +
 *
16 +
 * @see IOhlcv API provides OHLCV domain object access.
17 +
 * @see IOhlcvItem API provides OHLCV item domain object access.
18 +
 */
19 +
public class OhlcvDataSet extends AbstractDataSet<OhlcvDataSet> implements Iterable<IOhlcvItem>, IOhlcvItemAware, AttributeModelAware {
20 +
    public static final int DIM_Y_OPEN = 1;
21 +
    public static final int DIM_Y_HIGH = 2;
22 +
    public static final int DIM_Y_LOW = 3;
23 +
    public static final int DIM_Y_CLOSE = 4;
24 +
    public static final int DIM_Y_VOLUME = 5;
25 +
    public static final int DIM_Y_OI = 6;
26 +
27 +
    private IOhlcv ohlcv;
28 +
29 +
    private boolean isCategoryBased = false;
30 +
31 +
    public OhlcvDataSet(String title) {
32 +
        super(title, 7);
33 +
    }
34 +
35 +
    /**
36 +
     * Set the domain object with OHLCV data.
37 +
     *
38 +
     * @param ohlcv connection with OHLCV data from financial framework.
39 +
     */
40 +
    public void setData(IOhlcv ohlcv) {
41 +
        this.ohlcv = ohlcv;
42 +
    }
43 +
44 +
    /**
45 +
     * @return get domain object
46 +
     */
47 +
    public IOhlcv getData() {
48 +
        return ohlcv;
49 +
    }
50 +
51 +
    /**
52 +
     * @return is true for a category axis support
53 +
     */
54 +
    public boolean isCategoryBased() {
55 +
        return isCategoryBased;
56 +
    }
57 +
58 +
    /**
59 +
     * Change category or time based axis handling.
60 +
     *
61 +
     * @param categoryBased if true; category based indexes are used. Default false.
62 +
     */
63 +
    public void setCategoryBased(boolean categoryBased) {
64 +
        isCategoryBased = categoryBased;
65 +
    }
66 +
67 +
    @Override
68 +
    public double get(int dimIndex, int index) {
69 +
        IOhlcvItem ohlcvItem = ohlcv.getOhlcvItem(index);
70 +
        switch (dimIndex) {
71 +
        case DIM_X:
72 +
            return isCategoryBased ? index : ohlcvItem.getTimeStamp().getTime() / 1000.0;
73 +
        case DIM_Y_OPEN:
74 +
            return ohlcvItem.getOpen();
75 +
        case DIM_Y_HIGH:
76 +
            return ohlcvItem.getHigh();
77 +
        case DIM_Y_LOW:
78 +
            return ohlcvItem.getLow();
79 +
        case DIM_Y_CLOSE:
80 +
            return ohlcvItem.getClose();
81 +
        case DIM_Y_VOLUME:
82 +
            return ohlcvItem.getVolume();
83 +
        case DIM_Y_OI:
84 +
            return ohlcvItem.getOpenInterest();
85 +
        default:
86 +
            throw new IllegalArgumentException("Dimension not allowed! dimIndex=" + dimIndex);
87 +
        }
88 +
    }
89 +
90 +
    @Override
91 +
    public Iterator<IOhlcvItem> iterator() {
92 +
        return ohlcv.iterator();
93 +
    }
94 +
95 +
    @Override
96 +
    public int getDataCount() {
97 +
        return ohlcv.size();
98 +
    }
99 +
100 +
    @Override
101 +
    public DataSet set(DataSet other, boolean copy) {
102 +
        this.ohlcv = ((OhlcvDataSet) other).ohlcv;
103 +
        return this;
104 +
    }
105 +
106 +
    /**
107 +
     * Gets the index of the data point closest to the given x coordinate.
108 +
     * If the x coordinate lies outside the range of the data set, the index of the first/last point is returned.
109 +
     *
110 +
     * @param x the x position of the data point
111 +
     * @return the index of the data point
112 +
     */
113 +
    public int getXIndex(double x) {
114 +
        return getIndex(DIM_X, x);
115 +
    }
116 +
117 +
    @Override
118 +
    public AttributeModel getAddon() {
119 +
        return ohlcv.getAddon();
120 +
    }
121 +
122 +
    @Override
123 +
    public AttributeModel getAddonOrCreate() {
124 +
        return ohlcv.getAddonOrCreate();
125 +
    }
126 +
127 +
    @Override
128 +
    public IOhlcvItem getItem(int index) {
129 +
        return ohlcv.getOhlcvItem(index);
130 +
    }
131 +
132 +
    public IOhlcvItem getLastItem() {
133 +
        int size = ohlcv.size();
134 +
        if (size == 0) {
135 +
            return null;
136 +
        }
137 +
        return ohlcv.getOhlcvItem(size - 1);
138 +
    }
139 +
}

@@ -0,0 +1,138 @@
Loading
1 +
package de.gsi.chart.renderer.spi.financial;
2 +
3 +
import javafx.scene.canvas.GraphicsContext;
4 +
import javafx.scene.paint.Color;
5 +
import javafx.scene.paint.Paint;
6 +
7 +
import de.gsi.chart.axes.Axis;
8 +
import de.gsi.chart.renderer.Renderer;
9 +
import de.gsi.chart.renderer.spi.AbstractDataSetManagement;
10 +
import de.gsi.chart.renderer.spi.financial.service.OhlcvRendererEpData;
11 +
import de.gsi.chart.renderer.spi.financial.service.PaintBarMarker;
12 +
import de.gsi.dataset.DataSet;
13 +
import de.gsi.dataset.spi.financial.OhlcvDataSet;
14 +
15 +
/**
16 +
 * The ancestor for common financial renderers.
17 +
 * If you use this parent for your financial renderers you can use general features:
18 +
 * <ul>
19 +
 *     <li>PaintBar - coloring and painting changes of specific bars/candles/lines/dots</li>
20 +
 *     <li>Shadows - specific fast shadow paintings without fx-effects</li>
21 +
 *     <li>Extension-point before/after painting - extend specific renderers by your changes to add EP rules.</li>
22 +
 * </ul>
23 +
 */
24 +
@SuppressWarnings({ "PMD.ExcessiveParameterList" })
25 +
public abstract class AbstractFinancialRenderer<R extends Renderer> extends AbstractDataSetManagement<R> implements Renderer {
26 +
    protected PaintBarMarker paintBarMarker;
27 +
28 +
    /**
29 +
     * Inject PaintBar Marker service
30 +
     *
31 +
     * @param paintBarMarker service implementation
32 +
     */
33 +
    public void setPaintBarMarker(PaintBarMarker paintBarMarker) {
34 +
        this.paintBarMarker = paintBarMarker;
35 +
    }
36 +
37 +
    /**
38 +
     * Simple algorithmic solution to calculate required chart area distances.
39 +
     *
40 +
     * @param findAreaDistances service for calculation of find chart area distances.
41 +
     * @param dataset           includes data for processing
42 +
     * @param xAxis             X-Axis DO
43 +
     * @param yAxis             Y-Axis DO
44 +
     * @param xmin              minimal value of X
45 +
     * @param xmax              maximal value of X
46 +
     * @return the calculated distances
47 +
     */
48 +
    protected double[] findAreaDistances(FindAreaDistances findAreaDistances,
49 +
            DataSet dataset, Axis xAxis, Axis yAxis, double xmin, double xmax) {
50 +
        return findAreaDistances.findAreaDistances(dataset, xAxis, yAxis, xmin, xmax);
51 +
    }
52 +
53 +
    /**
54 +
     * Specific painting/coloring of the OHLCV/Candle Bars.
55 +
     * If you need specific bar selection visualization - implement this service and write your selection.
56 +
     *
57 +
     * @param data domain object for Renderer Extension Points
58 +
     * @return the specific paint bar Paint
59 +
     */
60 +
    protected Paint getPaintBarColor(OhlcvRendererEpData data) {
61 +
        if (paintBarMarker != null) {
62 +
            return paintBarMarker.getPaintBy(data);
63 +
        }
64 +
        return null;
65 +
    }
66 +
67 +
    /**
68 +
     * Possibility paint volume to financial renderers
69 +
     *
70 +
     * @param gc               GraphicsContext
71 +
     * @param ds               DataSet domain object which contains volume data
72 +
     * @param index            actual index which is rendered
73 +
     * @param volumeLongColor  volume color for Long Uptick OHLC
74 +
     * @param volumeShortColor volume color for Short Uptick OHLC
75 +
     * @param yAxis            Y-Axis DO
76 +
     * @param distances        distances estimated from finding service
77 +
     * @param barWidth         width of bar
78 +
     * @param barWidthHalf     half width of bar
79 +
     * @param x0               the center of the bar for X coordination
80 +
     */
81 +
    protected void paintVolume(GraphicsContext gc, DataSet ds, int index, Color volumeLongColor, Color volumeShortColor, Axis yAxis, double[] distances, double barWidth,
82 +
            double barWidthHalf, double x0) {
83 +
        double volume = ds.get(OhlcvDataSet.DIM_Y_VOLUME, index);
84 +
        double open = ds.get(OhlcvDataSet.DIM_Y_OPEN, index);
85 +
        double close = ds.get(OhlcvDataSet.DIM_Y_CLOSE, index);
86 +
        double maxVolume = distances[1];
87 +
        double volumeHeight = (volume / maxVolume) * 0.3;
88 +
        double min = yAxis.getDisplayPosition(yAxis.getMin());
89 +
        double max = yAxis.getDisplayPosition(yAxis.getMax());
90 +
        double zzVolume = volumeHeight * (max - min);
91 +
92 +
        gc.setFill(open < close ? volumeLongColor : volumeShortColor);
93 +
        gc.fillRect(x0 - barWidthHalf, min + zzVolume, barWidth, -zzVolume);
94 +
    }
95 +
96 +
    // services --------------------------------------------------------
97 +
98 +
    @FunctionalInterface
99 +
    protected interface FindAreaDistances {
100 +
        double[] findAreaDistances(DataSet dataset, Axis xAxis, Axis yAxis, double xmin, double xmax);
101 +
    }
102 +
103 +
    protected static class XMinAreaDistances implements FindAreaDistances {
104 +
        @Override
105 +
        public double[] findAreaDistances(DataSet dataset, Axis xAxis, Axis yAxis, double xmin, double xmax) {
106 +
            double minDistance = Integer.MAX_VALUE;
107 +
            for (int i = dataset.getIndex(DataSet.DIM_X, xmin) + 1; i < Math.min(dataset.getIndex(DataSet.DIM_X, xmax) + 1, dataset.getDataCount()); i++) {
108 +
                final double param0 = xAxis.getDisplayPosition(dataset.get(DataSet.DIM_X, i - 1));
109 +
                final double param1 = xAxis.getDisplayPosition(dataset.get(DataSet.DIM_X, i));
110 +
111 +
                if (param0 != param1) {
112 +
                    minDistance = Math.min(minDistance, Math.abs(param1 - param0));
113 +
                }
114 +
            }
115 +
            return new double[] { minDistance };
116 +
        }
117 +
    }
118 +
119 +
    protected static class XMinVolumeMaxAreaDistances implements FindAreaDistances {
120 +
        @Override
121 +
        public double[] findAreaDistances(DataSet dataset, Axis xAxis, Axis yAxis, double xmin, double xmax) {
122 +
            double minDistance = Integer.MAX_VALUE;
123 +
            double maxVolume = Integer.MIN_VALUE;
124 +
            for (int i = dataset.getIndex(DataSet.DIM_X, xmin) + 1; i < Math.min(dataset.getIndex(DataSet.DIM_X, xmax) + 1, dataset.getDataCount()); i++) {
125 +
                final double param0 = xAxis.getDisplayPosition(dataset.get(DataSet.DIM_X, i - 1));
126 +
                final double param1 = xAxis.getDisplayPosition(dataset.get(DataSet.DIM_X, i));
127 +
                double volume = dataset.get(OhlcvDataSet.DIM_Y_VOLUME, i);
128 +
                if (maxVolume < volume) {
129 +
                    maxVolume = volume;
130 +
                }
131 +
                if (param0 != param1) {
132 +
                    minDistance = Math.min(minDistance, Math.abs(param1 - param0));
133 +
                }
134 +
            }
135 +
            return new double[] { minDistance, maxVolume };
136 +
        }
137 +
    }
138 +
}

@@ -27,6 +27,7 @@
Loading
27 27
import javafx.scene.control.Control;
28 28
import javafx.scene.control.Label;
29 29
import javafx.scene.layout.*;
30 +
import javafx.scene.paint.Paint;
30 31
import javafx.stage.Window;
31 32
import javafx.util.Duration;
32 33
@@ -843,6 +844,10 @@
Loading
843 844
        titleSide.set(value);
844 845
    }
845 846
847 +
    public final void setTitlePaint(final Paint paint) {
848 +
        titleLabel.setTextFill(paint);
849 +
    }
850 +
846 851
    public Chart setToolBarPinned(boolean value) {
847 852
        toolBarPinned.set(value);
848 853
        return this;

@@ -0,0 +1,49 @@
Loading
1 +
package de.gsi.dataset.spi.financial.api.attrs;
2 +
3 +
public abstract class TypeKey<T> implements Comparable<TypeKey<T>> {
4 +
    private final Class<T> type;
5 +
    private final String name;
6 +
7 +
    protected TypeKey(Class<T> type, String name) {
8 +
        this.type = type;
9 +
        this.name = name;
10 +
    }
11 +
12 +
    public Class<T> getType() {
13 +
        return type;
14 +
    }
15 +
16 +
    public String getName() {
17 +
        return name;
18 +
    }
19 +
20 +
    @Override
21 +
    public boolean equals(Object obj) {
22 +
        if (obj == this) {
23 +
            return true;
24 +
        }
25 +
        if (!(obj instanceof TypeKey<?>) ) {
26 +
            return false;
27 +
        }
28 +
        TypeKey<?> other = (TypeKey<?>) obj;
29 +
        /* equality is defined only by name */
30 +
        return getName().equals(other.getName());
31 +
    }
32 +
33 +
    @Override
34 +
    public int hashCode() {
35 +
        int _hashCode = 17;
36 +
        _hashCode += getName().hashCode();
37 +
        return _hashCode;
38 +
    }
39 +
40 +
    @Override
41 +
    public String toString() {
42 +
        return getName();
43 +
    }
44 +
45 +
    @Override
46 +
    public int compareTo(TypeKey<T> typeKey) {
47 +
        return this.toString().compareTo(typeKey.toString());
48 +
    }
49 +
}

@@ -0,0 +1,11 @@
Loading
1 +
package de.gsi.dataset.spi.financial.api.attrs;
2 +
3 +
public class AttributeKey<T> extends TypeKey<T> {
4 +
    protected AttributeKey(Class<T> type, String name) {
5 +
        super(type, name);
6 +
    }
7 +
8 +
    public static <T> AttributeKey<T> create(Class<T> type, String name) {
9 +
        return new AttributeKey<T>(type, name);
10 +
    }
11 +
}

@@ -0,0 +1,45 @@
Loading
1 +
package de.gsi.chart.renderer.spi.financial.css;
2 +
3 +
import java.lang.reflect.Field;
4 +
import java.util.ArrayList;
5 +
import java.util.List;
6 +
7 +
/**
8 +
 * Default Financial Color Schemes implemented by Chart library.
9 +
 * The color schemes IDs are String values not enum.
10 +
 * User API extension: Create your scheme class with new color schemes and implement or
11 +
 * inherit interface FinancialColorSchemeAware, FinancialColorSchemeConfig.
12 +
 *
13 +
 * @see FinancialColorSchemeAware whole extension if your injected configuration service.
14 +
 * @see FinancialColorSchemeConfig possibility to inherit your configuration extension.
15 +
 */
16 +
public class FinancialColorSchemeConstants {
17 +
    public static final String CLASSIC = "CLASSIC";
18 +
19 +
    public static final String CLEARLOOK = "CLEARLOOK";
20 +
21 +
    public static final String SAND = "SAND";
22 +
23 +
    public static final String BLACKBERRY = "BLACKBERRY";
24 +
25 +
    public static final String DARK = "DARK";
26 +
27 +
    //--------------------------------------------------------
28 +
29 +
    /**
30 +
     * @return default color schemes information
31 +
     */
32 +
    public static String[] getDefaultColorSchemes() {
33 +
        Field[] declaredFields = FinancialColorSchemeConstants.class.getDeclaredFields();
34 +
        List<String> staticFields = new ArrayList<>();
35 +
        for (Field field : declaredFields) {
36 +
            if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
37 +
                staticFields.add(field.getName());
38 +
            }
39 +
        }
40 +
        return staticFields.toArray(new String[0]);
41 +
    }
42 +
43 +
    private FinancialColorSchemeConstants() {
44 +
    }
45 +
}
Files Complexity Coverage
chartfx-acc/src/main/java/de/gsi/acc 0.00% 0.00%
chartfx-chart/src/main/java/de/gsi/chart 52.24% 61.04%
chartfx-dataset/src/main/java/de/gsi/dataset 57.25% 56.53%
chartfx-math/src/main/java/de/gsi/math 31.90% 31.00%
microservice/src/main/java/de/gsi/serializer 59.65% 70.47%
Project Totals (393 files) 47.96% 51.59%
2926.1
openjdk11=
TRAVIS_OS_NAME=linux
1
ignore:
2
  - "*/src/test/**/*"
3
  - "chartfx-samples/**/*"
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading