Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8274043

ArrayIndexOutOfBoundsException SortedList when list is modified from ComboBox listener

XMLWordPrintable

    • x86_64
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      OpenJFX 13, 14, 15 and 17.

      openjdk version "11.0.12" 2021-07-20
      OpenJDK Runtime Environment (build 11.0.12+7-post-Debian-2)
      OpenJDK 64-Bit Server VM (build 11.0.12+7-post-Debian-2, mixed mode, sharing)

      Linux 5.10.0-8-amd64


      A DESCRIPTION OF THE PROBLEM :
      I am not the original author of the bug report but I created a minimal reproduction example since I am also experiencing this bug.

      It happens when the the observable list that a sorted list is based upon is changed a second time as a result of the original change.
      Important: This is also a problem if the original list is already empty when calling the second "clear()" it will remove the items a second time for some reason.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the code example as junit test (test-fx)

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Passes without exception – at the clear step to be precise.
      ACTUAL -
      Exception:

      value handler start
      value end
      clearing list
      value handler start
      value null, clearing source list: []
      --- Exception in Async Thread ---
      java.lang.ArrayIndexOutOfBoundsException: arraycopy: length -6 is negative
      java.base/java.lang.System.arraycopy(Native Method)
      javafx.collections.transformation.SortedList.removeFromMapping(SortedList.java:364)
      javafx.collections.transformation.SortedList.addRemove(SortedList.java:398)
      javafx.collections.transformation.SortedList.sourceChanged(SortedList.java:108)
      javafx.collections.transformation.TransformationList.lambda$getListener$0(TransformationList.java:106)
      javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
      com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
      com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
      javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:239)
      javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
      javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
      javafx.collections.ObservableListBase.endChange(ObservableListBase.java:211)
      com.sun.javafx.collections.ObservableListWrapper.clear(ObservableListWrapper.java:157)
      de.gsi.bpeter.experiments.fxplain.sortedlistbug.SortedListBugTest.lambda$2(SortedListBugTest.java:49)
      com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)
      com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
      javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:106)
      javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:113)
      javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
      javafx.scene.control.ComboBoxBase.setValue(ComboBoxBase.java:151)
      javafx.scene.control.ComboBox.updateValue(ComboBox.java:516)
      javafx.scene.control.ComboBox$3.changed(ComboBox.java:499)
      com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)
      com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
      javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:80)
      javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102)
      javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:113)
      javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
      javafx.scene.control.SelectionModel.setSelectedItem(SelectionModel.java:105)
      javafx.scene.control.ComboBox$ComboBoxSelectionModel.lambda$new$0(ComboBox.java:556)
      com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:136)
      com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
      javafx.beans.property.ReadOnlyIntegerPropertyBase.fireValueChangedEvent(ReadOnlyIntegerPropertyBase.java:78)
      javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:102)
      javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:114)
      javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:148)
      javafx.scene.control.SelectionModel.setSelectedIndex(SelectionModel.java:69)
      javafx.scene.control.ComboBox$ComboBoxSelectionModel$2.onChanged(ComboBox.java:587)
      javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
      com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
      com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
      javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:239)
      javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
      javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
      javafx.collections.ObservableListBase.endChange(ObservableListBase.java:211)
      javafx.collections.transformation.SortedList.sourceChanged(SortedList.java:111)
      javafx.collections.transformation.TransformationList.lambda$getListener$0(TransformationList.java:106)
      javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
      com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
      com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
      javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:239)
      javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
      javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
      javafx.collections.ObservableListBase.endChange(ObservableListBase.java:211)
      com.sun.javafx.collections.ObservableListWrapper.clear(ObservableListWrapper.java:157)
      de.gsi.bpeter.experiments.fxplain.sortedlistbug.SortedListBugTest.lambda$5(SortedListBugTest.java:69)
      java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
      java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
      com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
      java.base/java.security.AccessController.doPrivileged(Native Method)
      com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
      com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
      com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
      com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:290)
      java.base/java.lang.Thread.run(Thread.java:829)


      ---------- BEGIN SOURCE ----------
      package de.gsi.bpeter.experiments.fxplain.sortedlistbug;

      import org.hamcrest.Matchers;
      import org.junit.jupiter.api.Test;
      import org.junit.jupiter.api.extension.ExtendWith;
      import org.testfx.api.FxAssert;
      import org.testfx.api.FxRobot;
      import org.testfx.framework.junit5.ApplicationExtension;
      import org.testfx.framework.junit5.Start;
      import org.testfx.matcher.base.NodeMatchers;

      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.collections.transformation.SortedList;
      import javafx.scene.Scene;
      import javafx.scene.control.ComboBox;
      import javafx.scene.layout.StackPane;
      import javafx.stage.Stage;

      /*
       * Minimal bug example intended for:
       *
       * https://bugs.openjdk.java.net/browse/JDK-8162592
       */
      @ExtendWith(ApplicationExtension.class)
      class SortedListBugTest {

          private ComboBox<Integer> comboBox;

          @Start
          private void start(Stage stage) {
              comboBox = new ComboBox<>();
              stage.setScene(new Scene(new StackPane(comboBox), 100, 100));
              stage.show();
          }

          @Test
          void testThatListIsSorted(FxRobot robot) {
              ObservableList<Integer> observableList = FXCollections.observableArrayList();
              SortedList<Integer> sortedList = new SortedList<>(observableList, Integer::compare);

              robot.interact(() -> {
                  comboBox.valueProperty().addListener((obs, old, value) -> {
                      System.out.println("value handler start");
                      if (value == null) {
                          System.out.println("value null, clearing source list: " + observableList.toString());
                          // list is empty so actually nothing should happen
                          // bpeter: Test works without the following line
                          observableList.clear();
                      }
                      System.out.println("value end");
                  });

                  comboBox.setItems(sortedList);
              });

              robot.interact(() -> {
                  observableList.setAll(10, 9, 20, -5, 100, -3, 0, 4);
              });

              robot.interact(() -> {
                  comboBox.getSelectionModel().select((Integer) 20);
              });

              FxAssert.verifyThat(robot.from(comboBox), NodeMatchers.hasChild("20"));

              robot.interact(() -> {
                  System.out.println("clearing list");
                  observableList.clear();
                  System.out.println("clearing list done");
              });

              FxAssert.verifyThat(robot.from(comboBox), Matchers.not(NodeMatchers.hasChild("20")));
          }
      }

      /**
       * Dependencies
       *
       * <pre>
              <dependency>
                  <groupId>org.junit.jupiter</groupId>
                  <artifactId>junit-jupiter-api</artifactId>
                  <version>5.5.1</version>
                  <scope>test</scope>
              </dependency>
              <dependency>
                  <groupId>org.hamcrest</groupId>
                  <artifactId>hamcrest-core</artifactId>
                  <version>2.2</version>
                  <scope>test</scope>
              </dependency>
              <dependency>
                  <groupId>org.testfx</groupId>
                  <artifactId>testfx-junit5</artifactId>
                  <version>4.0.16-alpha</version>
                  <scope>test</scope>
              </dependency>
       * </pre>
       */

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Try not to change the original list maybe, but might be hard for larger constructs.

            kcr Kevin Rushforth
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: