Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Peculiar ClassCastException in removing data from LineChart<String,String>

Tags:

java

javafx

Consider the following application:

enter image description here

I expect, by each time clicking on BUTTON, the first data of the LineChart be removed. The output after the clicks must be as follows:

After 1st click

enter image description here

After 2nd click

enter image description here

After 3rd click

enter image description here

However, after the first click, I get the following ClassCastException:

Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Number (java.lang.String and java.lang.Number are in module java.base of loader 'bootstrap')
    at javafx.controls@18/javafx.scene.chart.LineChart.createDataRemoveTimeline(LineChart.java:503)
    at javafx.controls@18/javafx.scene.chart.LineChart.dataItemRemoved(LineChart.java:359)
    at javafx.controls@18/javafx.scene.chart.XYChart.dataItemsChanged(XYChart.java:505)
    at javafx.controls@18/javafx.scene.chart.XYChart$Series$1.onChanged(XYChart.java:1474)
    at javafx.base@18/com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
    at javafx.base@18/com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at javafx.base@18/javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:239)
    at javafx.base@18/javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
    at javafx.base@18/javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
    at javafx.base@18/javafx.collections.ObservableListBase.endChange(ObservableListBase.java:211)
    at javafx.base@18/javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:190)
    at com.example.demo/com.example.demo.MyApplication.lambda$start$0(MyApplication.java:21)
    at javafx.base@18/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base@18/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@18/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base@18/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@18/javafx.scene.Node.fireEvent(Node.java:8797)
    at javafx.controls@18/javafx.scene.control.Button.fire(Button.java:203)
    at javafx.controls@18/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:208)
    at javafx.controls@18/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base@18/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
    at javafx.base@18/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base@18/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base@18/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@18/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@18/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@18/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base@18/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@18/javafx.scene.Scene$MouseHandler.process(Scene.java:3881)
    at javafx.graphics@18/javafx.scene.Scene.processMouseEvent(Scene.java:1874)
    at javafx.graphics@18/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2607)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics@18/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
    at javafx.graphics@18/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
    at javafx.graphics@18/com.sun.glass.ui.View.notifyMouse(View.java:937)
    at javafx.graphics@18/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics@18/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:833)

The code is as follows:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;

public class MyApplication extends Application {
    @Override
    public void start(Stage stage) {

        LineChart<String,String> lineChart=createLineChart();

        Button button = new Button("BUTTON");

        button.setOnAction(event -> lineChart.getData().get(0).getData().remove(0));

        VBox vBox=new VBox(button,lineChart);

        Scene scene = new Scene(vBox);

        stage.setScene(scene);

        stage.show();

    }

    LineChart<String,String> createLineChart(){
        LineChart<String, String> lineChart = new LineChart<>(new CategoryAxis(), new CategoryAxis());
        lineChart.getData().add(new XYChart.Series<>());
        for (int i = 0; i < 3; i++) {
            lineChart.getData().get(0).getData().add(new XYChart.Data<>("X string %d".formatted(i+1), "Y string %d".formatted(i+1)));
        }
        lineChart.getXAxis().setTickLabelFont(new Font(20));
        lineChart.getYAxis().setTickLabelFont(new Font(20));
        return lineChart;
    }

    public static void main(String[] args) {
        launch();
    }

}

Question

Why does ClassCastException raise in conversion of String to Number when I have no Number type data?

like image 978
Keen Teen Avatar asked Oct 31 '25 08:10

Keen Teen


1 Answers

This is a bug in the implementation of LineChart. I cannot find an active bug report for it at https://bugs.openjdk.org/secure/Dashboard.jspa.

The issue arises in the call to the method createDataRemoveTimeline(...) (found here in the current implementation) which is implemented as:

    private Timeline createDataRemoveTimeline(final Data<X,Y> item, final Node symbol, final Series<X,Y> series) {
        Timeline t = new Timeline();
        // save data values in case the same data item gets added immediately.
        XYValueMap.put(item, ((Number)item.getYValue()).doubleValue());

        t.getKeyFrames().addAll(new KeyFrame(Duration.ZERO, new KeyValue(item.currentYProperty(),
                item.getCurrentY()), new KeyValue(item.currentXProperty(),
                item.getCurrentX())),
                new KeyFrame(Duration.millis(500), actionEvent -> {
                    if (symbol != null) getPlotChildren().remove(symbol);
                    removeDataItemFromDisplay(series, item);
                    XYValueMap.clear();
                },
                new KeyValue(item.currentYProperty(),
                item.getYValue(), Interpolator.EASE_BOTH),
                new KeyValue(item.currentXProperty(),
                item.getXValue(), Interpolator.EASE_BOTH))
        );
        return t;
    }

The implementation caches values that are removed (as I understand it this is needed in case the same values are added back while the animation for their removal is progressing) in a Map<Data<X,Y>, Double>. This assumes the y-value is a Number (note the cast in the second line of the method) which of course is not necessarily true. I think this should be implemented with a Map<Data<X,Y>, Y>, though I have not fully analyzed the code.

The createDataRemoveTimeline(...) method is called from the dataItemRemoved(...) method (invoked when an item is removed from one of the data series) but only if animated is set to true. Hence one work around is to turn off animation:

lineChart.setAnimated(false);
like image 149
James_D Avatar answered Nov 03 '25 00:11

James_D