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

Bindings.bindBidirectional() does not respect read-only wrappers.

    XMLWordPrintable

Details

    • Bug
    • Resolution: Duplicate
    • P4
    • None
    • 8u66
    • javafx
    • x86
    • other

    Description

      FULL PRODUCT VERSION :
      java version "1.8.0_66"
      Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
      Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      The problem is not OS specific.

      A DESCRIPTION OF THE PROBLEM :
      Creating a bidirectional binding for a read-only wrapper (e.g. by using ReadOnlyMapWrapper.bindBidirectional(Property<ObservableMap<K, V>> other)) does not work properly, i.e. the target property value is not properly updated if the source read-only wrapper's value is changed.

      The reason is that the TypedGenericBidirectionalBinding, which is ultimately used to realize the bidirectional binding (through several indirections), performs the following check to infer which property is source and which is target when observing a change:

      if (property1 == sourceProperty) {
        property2.setValue(newValue);
      } else {
        property1.setValue(newValue);
      }

      As all read-only wrappers (like ReadOnlyMapWrapper) use their nested read-only property as source of change notifications (they delegate their fire* methods to it), property1 == sourceProperty will never be true for a read-only wrapper. In case of a ReadOnlyMapWrapper, the check would have to be ((ReadOnlyMapWrapper)property1).getReadOnlyProperty() == sourceProperty() to succeed.

      ADDITIONAL REGRESSION INFORMATION:
      java version "1.8.0_66"
      Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
      Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1) Create two ReadOnlyMapWrapper instances.
      2) Bind first bidirectional to second.
      3) Update value of the first and observe that the second is not updated.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The second property should be updated to hold the same value as the first.
      ACTUAL -
      The second property contains a different value, namely the old/initial one.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      @Test
      public void bidirectionalBinding() {
        MapProperty<Integer, String> property1 = new ReadOnlyMapWrapperEx<>(new ObservableMapWrapper<>(new HashMap<Integer, String>()));
        MapProperty<Integer, String> property2 = new ReadOnlyMapWrapperEx<>(new ObservableMapWrapper<>(new HashMap<Integer, String>()));

        property2.bindBidirectional(property1);

        // change value of first property
        ObservableMap<Integer, String> newValue = new ObservableMapWrapper<>(new HashMap<Integer, String>());
        newValue.put(1, "1-1");
        property1.set(newValue);
        assertEquals(newValue, property1.get());
        assertEquals(newValue, property2.get());
        assertEquals(property1, property2);

        // change value of second property
        newValue = new ObservableMapWrapper<>(new HashMap<Integer, String>());
        newValue.put(2, "2-1");
        property2.set(newValue);
        assertEquals(newValue, property1.get());
        assertEquals(newValue, property2.get());
        assertEquals(property1, property2);
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      There is no direct workaround, except replacing the read-only wrapper classes with own implementations that provide proper bidirectional bindings.

      Attachments

        Issue Links

          Activity

            People

              vadim Vadim Pakhnushev
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: