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

Change occurs in ObjectProperty but change event is not always fired

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P4 P4
    • None
    • 7u21
    • javafx
    • Windows 7 64bit, JDK 7 1.7.0_21 64bit, JavaFX 2.2.21 64bit, NetBeans 7.3

      There seems to be a slight flaw in the change event firing mechanism on properties, which allows a property content to be changed (provoking the invalidation of the property) but not firing a ChangeEvent at the same time under some conditions.

      Consider the following code:

              ObjectProperty<ObservableList> lProperty1 = new SimpleObjectProperty(this, "lProperty1", FXCollections.observableList(new LinkedList<>()));
              ObjectProperty<ObservableList> lProperty2 = new SimpleObjectProperty(this, "lProperty2", FXCollections.observableList(new LinkedList<>()));
              ObjectProperty<ObservableList> lProperty3 = new SimpleObjectProperty(this, "lProperty3", null);
              lProperty1.addListener(new InvalidationListener(){

                  @Override
                  public void invalidated(Observable o) {
                      System.out.printf("lProperty%d invalidated", 1).println();
                  }
              });
              lProperty1.addListener(new ChangeListener<ObservableList>() {
                  @Override
                  public void changed(ObservableValue<? extends ObservableList> ov, ObservableList t, ObservableList t1) {
                      System.out.printf("lProperty%d changed %s => %s", 1, t, t1).println();
                  }
              });
              lProperty2.addListener(new InvalidationListener(){

                  @Override
                  public void invalidated(Observable o) {
                      System.out.printf("lProperty%d invalidated", 2).println();
                  }
              });
              lProperty2.addListener(new ChangeListener<ObservableList>() {
                  @Override
                  public void changed(ObservableValue<? extends ObservableList> ov, ObservableList t, ObservableList t1) {
                      System.out.printf("lProperty%d changed %s => %s", 2, t, t1).println();
                  }
              });
              lProperty3.addListener(new InvalidationListener(){

                  @Override
                  public void invalidated(Observable o) {
                      System.out.printf("lProperty%d invalidated", 3).println();
                  }
              });
              lProperty3.addListener(new ChangeListener<ObservableList>() {
                  @Override
                  public void changed(ObservableValue<? extends ObservableList> ov, ObservableList t, ObservableList t1) {
                      System.out.printf("lProperty%d changed %s => %s", 3, t, t1).println();
                  }
              });
              //
              System.out.printf("\n= %s =", "initialization").println();
              System.out.printf("lProperty%d = %s", 1, lProperty1.getValue()).println();
              System.out.printf("lProperty%d = %s", 2, lProperty2.getValue()).println();
              System.out.printf("lProperty%d = %s", 3, lProperty3.getValue()).println();
              //
              System.out.printf("\n= %s =", "content change without binding").println();
              lProperty1.getValue().setAll(1, 2, 3);
              System.out.printf("lProperty%d = %s", 1, lProperty1.getValue()).println();
              System.out.printf("lProperty%d = %s", 2, lProperty2.getValue()).println();
              System.out.printf("lProperty%d = %s", 3, lProperty3.getValue()).println();
              //
              System.out.printf("\n= %s =", "clear without binding").println();
              lProperty1.getValue().clear();
              System.out.printf("lProperty%d = %s", 1, lProperty1.getValue()).println();
              System.out.printf("lProperty%d = %s", 2, lProperty2.getValue()).println();
              System.out.printf("lProperty%d = %s", 3, lProperty3.getValue()).println();
              //
              System.out.printf("\n= %s =", "binding").println();
              lProperty2.bind(lProperty1);
              lProperty3.bind(lProperty1);
              System.out.printf("lProperty%d = %s", 1, lProperty1.getValue()).println();
              System.out.printf("lProperty%d = %s", 2, lProperty2.getValue()).println(); // Now contains same list ast lProperty1 but no change event fired
              System.out.printf("lProperty%d = %s", 3, lProperty3.getValue()).println(); // Now contains same list ast lProperty1
              //
              System.out.printf("\n= %s =", "content change with binding").println();
              lProperty1.getValue().setAll(1, 2, 3);
              System.out.printf("lProperty%d = %s", 1, lProperty1.getValue()).println();
              System.out.printf("lProperty%d = %s", 2, lProperty2.getValue()).println(); // Still contains same list ast lProperty1
              System.out.printf("lProperty%d = %s", 3, lProperty3.getValue()).println(); // Still contains same list ast lProperty1
              //
              System.out.printf("\n= %s =", "unbinding").println();
              lProperty2.unbind();
              lProperty3.unbind();
              System.out.printf("lProperty%d = %s", 1, lProperty1.getValue()).println();
              System.out.printf("lProperty%d = %s", 2, lProperty2.getValue()).println(); // Still contains same list ast lProperty1
              System.out.printf("lProperty%d = %s", 3, lProperty3.getValue()).println(); // Still contains same list ast lProperty1
              //
              System.out.printf("\n= %s =", "content change after unbinding").println();
              lProperty1.getValue().clear();
              System.out.printf("lProperty%d = %s", 1, lProperty1.getValue()).println();
              System.out.printf("lProperty%d = %s", 2, lProperty2.getValue()).println(); // Still contains same list ast lProperty1
              System.out.printf("lProperty%d = %s", 3, lProperty3.getValue()).println(); // Still contains same list ast lProperty1
              //
              System.out.printf("\n= %s =", "value change after unbinding").println();
              lProperty1.setValue(FXCollections.observableList(Arrays.asList(4, 5, 6)));
              lProperty2.setValue(FXCollections.observableList(Arrays.asList(7, 8, 9)));
              System.out.printf("lProperty%d = %s", 1, lProperty1.getValue()).println(); // Contains new list.
              System.out.printf("lProperty%d = %s", 2, lProperty2.getValue()).println(); // Contains new list.
              System.out.printf("lProperty%d = %s", 3, lProperty3.getValue()).println(); // Still contains old lProperty1 value.

      As of 2.2.21, the output of this code is:

      = initialization =
      lProperty1 = []
      lProperty2 = []
      lProperty3 = null

      = content change without binding =
      lProperty1 = [1, 2, 3]
      lProperty2 = []
      lProperty3 = null

      = clear without binding =
      lProperty1 = []
      lProperty2 = []
      lProperty3 = null

      = binding =
      lProperty2 invalidated
      lProperty3 invalidated
      lProperty3 changed null => []
      lProperty1 = []
      lProperty2 = []
      lProperty3 = []

      = content change with binding =
      lProperty1 = [1, 2, 3]
      lProperty2 = [1, 2, 3]
      lProperty3 = [1, 2, 3]

      = unbinding =
      lProperty1 = [1, 2, 3]
      lProperty2 = [1, 2, 3]
      lProperty3 = [1, 2, 3]

      = content change after unbinding =
      lProperty1 = []
      lProperty2 = []
      lProperty3 = []

      = value change after unbinding =
      lProperty1 invalidated
      lProperty1 changed [] => [4, 5, 6]
      lProperty2 invalidated
      lProperty2 changed [] => [7, 8, 9]
      lProperty1 = [4, 5, 6]
      lProperty2 = [7, 8, 9]
      lProperty3 = []

      When doing lProperty2.bind(lProperty1); lProperty2 becomes invalidated and its content is replaced with the content from lProperty1, but no change event is fired. My guess is that the mechanism that fire change events is using the equals() method of the included list and, in this case, both list being initially empty, the test returns true and thus no event is fired despite the fact that the change occurs.

      May be this is the desired behavior (to minimize event firing) but, while it seems the right thing to do for Number-based properties, String-based properties and Boolean-based properties, it seems somehow an odd behavior when dealing with Object instances, especially List instances (which are used a lot in the controls).

            msladecek Martin Sládeček
            fbouyajfx Fabrice Bouyé (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported: