There are typically three valid y-values for a x-value in a data-series:
1) Regular value: y-value for a specific x-value is a regular number
2) Erroneous value: y-value for a specific x-value is an erroneous value
3) Not existing value: y-value for a specific x-value does not exist
It seems to be natural that 2) is represented by y-value = Double.NaN and 3) by y-value = null.
The current implementation of e.g. LineChart and NumberAxis does not visualizes a Series meaningful if other y-values then 1) (regular y-values) are present. But after all it does not throw an Exception if 2) (Double.NaN y-values) are present.
But the current implementation does not accept 3) (null y-values)... a NullPointerException is thrown (see example below).
Due to this behaviour it is impossible to write a custom chart based on XYChart and ValueAxis which can handle null y-values.
Anyway, I would like to suggest the improvement that XYChart and ValueAxis are robust to the use of null as values.
=============================================
Example Use-Case:
=============================================
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
public class Test extends Application {
@SuppressWarnings("unchecked")
@Override
public void start(Stage stage) throws Exception {
Data<Number, Number> element1 = new Data<Number, Number>(1, 1);
Data<Number, Number> element2 = new Data<Number, Number>(2, Double.NaN);
Data<Number, Number> element3 = new Data<Number, Number>(3, 3);
Data<Number, Number> element4 = new Data<Number, Number>(4, null);
Data<Number, Number> element5 = new Data<Number, Number>(5, 5);
ObservableList<Data<Number, Number>> dataList = FXCollections.observableArrayList(element1, element2, element3, element4, element5);
Series<Number, Number> series = new Series<>(dataList);
ObservableList<Series<Number, Number>> seriesList = FXCollections.observableArrayList(series);
LineChart<Number, Number> root = new LineChart<Number, Number>(new NumberAxis(), new NumberAxis(), seriesList);
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
=============================================
Exception:
=============================================
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:363)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:875)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$147(LauncherImpl.java:157)
at com.sun.javafx.application.LauncherImpl$$Lambda$48/128893786.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at javafx.scene.chart.ValueAxis.invalidateRange(ValueAxis.java:437)
at javafx.scene.chart.LineChart.updateAxisRange(LineChart.java:176)
at javafx.scene.chart.XYChart.layoutChartChildren(XYChart.java:663)
at javafx.scene.chart.Chart$1.layoutChildren(Chart.java:88)
at javafx.scene.Parent.layout(Parent.java:1074)
at javafx.scene.Parent.layout(Parent.java:1080)
at javafx.scene.Scene.doLayoutPass(Scene.java:532)
at javafx.scene.Scene.preferredSize(Scene.java:1612)
at javafx.scene.Scene.impl_preferredSize(Scene.java:1686)
at javafx.stage.Window$9.invalidated(Window.java:765)
at javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:109)
at javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:143)
at javafx.stage.Window.setShowing(Window.java:841)
at javafx.stage.Window.show(Window.java:856)
at javafx.stage.Stage.show(Stage.java:255)
at de.lit.ribbonfx.Test.start(Test.java:29)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$153(LauncherImpl.java:821)
at com.sun.javafx.application.LauncherImpl$$Lambda$51/583498922.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$166(PlatformImpl.java:323)
at com.sun.javafx.application.PlatformImpl$$Lambda$45/1051754451.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$164(PlatformImpl.java:292)
at com.sun.javafx.application.PlatformImpl$$Lambda$47/879507389.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$165(PlatformImpl.java:291)
at com.sun.javafx.application.PlatformImpl$$Lambda$46/1775282465.run(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$141(WinApplication.java:102)
at com.sun.glass.ui.win.WinApplication$$Lambda$37/1109371569.run(Unknown Source)
... 1 more
Exception running application de.lit.ribbonfx.Test
1) Regular value: y-value for a specific x-value is a regular number
2) Erroneous value: y-value for a specific x-value is an erroneous value
3) Not existing value: y-value for a specific x-value does not exist
It seems to be natural that 2) is represented by y-value = Double.NaN and 3) by y-value = null.
The current implementation of e.g. LineChart and NumberAxis does not visualizes a Series meaningful if other y-values then 1) (regular y-values) are present. But after all it does not throw an Exception if 2) (Double.NaN y-values) are present.
But the current implementation does not accept 3) (null y-values)... a NullPointerException is thrown (see example below).
Due to this behaviour it is impossible to write a custom chart based on XYChart and ValueAxis which can handle null y-values.
Anyway, I would like to suggest the improvement that XYChart and ValueAxis are robust to the use of null as values.
=============================================
Example Use-Case:
=============================================
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
public class Test extends Application {
@SuppressWarnings("unchecked")
@Override
public void start(Stage stage) throws Exception {
Data<Number, Number> element1 = new Data<Number, Number>(1, 1);
Data<Number, Number> element2 = new Data<Number, Number>(2, Double.NaN);
Data<Number, Number> element3 = new Data<Number, Number>(3, 3);
Data<Number, Number> element4 = new Data<Number, Number>(4, null);
Data<Number, Number> element5 = new Data<Number, Number>(5, 5);
ObservableList<Data<Number, Number>> dataList = FXCollections.observableArrayList(element1, element2, element3, element4, element5);
Series<Number, Number> series = new Series<>(dataList);
ObservableList<Series<Number, Number>> seriesList = FXCollections.observableArrayList(series);
LineChart<Number, Number> root = new LineChart<Number, Number>(new NumberAxis(), new NumberAxis(), seriesList);
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
=============================================
Exception:
=============================================
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:363)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:875)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$147(LauncherImpl.java:157)
at com.sun.javafx.application.LauncherImpl$$Lambda$48/128893786.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at javafx.scene.chart.ValueAxis.invalidateRange(ValueAxis.java:437)
at javafx.scene.chart.LineChart.updateAxisRange(LineChart.java:176)
at javafx.scene.chart.XYChart.layoutChartChildren(XYChart.java:663)
at javafx.scene.chart.Chart$1.layoutChildren(Chart.java:88)
at javafx.scene.Parent.layout(Parent.java:1074)
at javafx.scene.Parent.layout(Parent.java:1080)
at javafx.scene.Scene.doLayoutPass(Scene.java:532)
at javafx.scene.Scene.preferredSize(Scene.java:1612)
at javafx.scene.Scene.impl_preferredSize(Scene.java:1686)
at javafx.stage.Window$9.invalidated(Window.java:765)
at javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:109)
at javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:143)
at javafx.stage.Window.setShowing(Window.java:841)
at javafx.stage.Window.show(Window.java:856)
at javafx.stage.Stage.show(Stage.java:255)
at de.lit.ribbonfx.Test.start(Test.java:29)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$153(LauncherImpl.java:821)
at com.sun.javafx.application.LauncherImpl$$Lambda$51/583498922.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$166(PlatformImpl.java:323)
at com.sun.javafx.application.PlatformImpl$$Lambda$45/1051754451.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$164(PlatformImpl.java:292)
at com.sun.javafx.application.PlatformImpl$$Lambda$47/879507389.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$165(PlatformImpl.java:291)
at com.sun.javafx.application.PlatformImpl$$Lambda$46/1775282465.run(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$141(WinApplication.java:102)
at com.sun.glass.ui.win.WinApplication$$Lambda$37/1109371569.run(Unknown Source)
... 1 more
Exception running application de.lit.ribbonfx.Test