-
Bug
-
Resolution: Duplicate
-
P4
-
None
-
8
-
x86_64
-
generic
FULL PRODUCT VERSION :
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Windows 10, Linux 3.0.101-63-default
A DESCRIPTION OF THE PROBLEM :
Bindings.bindContent for an Observable map will treat the case where the value for a key is being replaced by a new value as a removal.
The following was debugged into com.sun.javafx.binding.ContentBinding:
@Override
public void onChanged(Change<? extends K, ? extends V> change) {
final Map<K, V> map = mapRef.get();
if (map == null) {
change.getMap().removeListener(this);
} else {
if (change.wasRemoved()) {
map.remove(change.getKey());
} else {
map.put(change.getKey(), change.getValueAdded());
}
}
}
The changed.wasRemoved() check does not also see if wasAdded() returns true to see if the key was changed and will assume that it was removed.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Make a source and destination map
2. Use Bindings.bindContent to bind them together
3. Add content to the source map, then replace a value for a key
4. See that the destination map does not match the source map
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Adding things
Added: 1 -> Thing 1
Added: 2 -> Thing 2
Added: 3 -> Thing 3
Added: 4 -> Thing 4
Added: 5 -> Thing 5
Destination: {1=Thing 1, 2=Thing 2, 3=Thing 3, 4=Thing 4, 5=Thing 5}
Removing things
Removed: 3 ( was Thing 3 )
Removed: 5 ( was Thing 5 )
Destination: {1=Thing 1, 2=Thing 2, 4=Thing 4}
Adding/modifying things
Modified: 1 from Thing 1 to Thing 1-1
Modified: 2 from Thing 2 to Thing 2-2
Added: 3 -> Thing 3-3
Modified: 4 from Thing 4 to Thing 4-4
Added: 5 -> Thing 5-5
Destination: {1=Thing 1-1, 2=Thing 2-2, 3=Thing 3-3, 4=Thing 4-4, 5=Thing 5-5}
Batch remove
Removed: 1 ( was Thing 1-1 )
Removed: 5 ( was Thing 5-5 )
Destination: {2=Thing 2-2, 3=Thing 3-3, 4=Thing 4-4}
Batch add/modify
Added: 1 -> Thing 1-1-1
Modified: 3 from Thing 3-3 to Thing 3-3-3
Added: 5 -> Thing 5-5-5
Added: 6 -> Thing 6-6-6
Destination: {1=Thing 1-1-1, 2=Thing 2-2, 3=Thing 3-3-3, 4=Thing 4-4, 5=Thing 5-5-5, 6=Thing 6-6-6}
ACTUAL -
Adding things
Added: 1 -> Thing 1
Added: 2 -> Thing 2
Added: 3 -> Thing 3
Added: 4 -> Thing 4
Added: 5 -> Thing 5
Destination: {1=Thing 1, 2=Thing 2, 3=Thing 3, 4=Thing 4, 5=Thing 5}
Removing things
Removed: 3 ( was Thing 3 )
Removed: 5 ( was Thing 5 )
Destination: {1=Thing 1, 2=Thing 2, 4=Thing 4}
Adding/modifying things
Removed: 1 ( was Thing 1 )
Removed: 2 ( was Thing 2 )
Added: 3 -> Thing 3-3
Removed: 4 ( was Thing 4 )
Added: 5 -> Thing 5-5
Destination: {3=Thing 3-3, 5=Thing 5-5}
Batch remove
Removed: 5 ( was Thing 5-5 )
Destination: {3=Thing 3-3}
Batch add/modify
Added: 1 -> Thing 1-1-1
Removed: 3 ( was Thing 3-3 )
Added: 5 -> Thing 5-5-5
Added: 6 -> Thing 6-6-6
Destination: {1=Thing 1-1-1, 5=Thing 5-5-5, 6=Thing 6-6-6}
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public static void main(String[] arguments)
{
// Set up the content binding and listeners.
ObservableMap<Integer, String> source = FXCollections.observableHashMap();
ObservableMap<Integer, String> destination = FXCollections.observableHashMap();
Bindings.bindContent(destination, source);
destination.addListener(s_destinationMapListener);
// First set, add some things.
System.out.println("Adding things");
source.put(1, "Thing 1");
source.put(2, "Thing 2");
source.put(3, "Thing 3");
source.put(4, "Thing 4");
source.put(5, "Thing 5");
System.out.println("Destination: " + destination);
System.out.println();
// Next, remove some things.
System.out.println("Removing things");
source.remove(3);
source.remove(5);
System.out.println("Destination: " + destination);
System.out.println();
// Next, add and modify.
System.out.println("Adding/modifying things");
source.put(1, "Thing 1-1");
source.put(2, "Thing 2-2");
source.put(3, "Thing 3-3");
source.put(4, "Thing 4-4");
source.put(5, "Thing 5-5");
System.out.println("Destination: " + destination);
System.out.println();
// Next, remove a bunch.
System.out.println("Batch remove");
source.keySet().removeAll(Arrays.asList(new Integer[]{0, 1, 5, 6, 7}));
System.out.println("Destination: " + destination);
System.out.println();
// Next, add a bunch at once.
System.out.println("Batch add/modify");
Map<Integer, String> next = new HashMap<>();
next.put(1, "Thing 1-1-1");
next.put(3, "Thing 3-3-3");
next.put(5, "Thing 5-5-5");
next.put(6, "Thing 6-6-6");
source.putAll(next);
System.out.println("Destination: " + destination);
System.out.println();
}
private static MapChangeListener<Integer, String> s_destinationMapListener =
new MapChangeListener<Integer, String>()
{
@Override
public void onChanged(Change<? extends Integer, ? extends String> change)
{
if (change.wasAdded())
{
if (change.wasRemoved())
{
System.out.println("Modified: " + change.getKey() + " from " + change.getValueRemoved()
+ " to " + change.getValueAdded());
}
else
{
System.out.println("Added: " + change.getKey() + " -> " + change.getValueAdded());
}
}
else if (change.wasRemoved())
{
System.out.println(
"Removed: " + change.getKey() + " ( was " + change.getValueRemoved() + " )");
}
}
};
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Set up a listener on the source map and manually update the destination map.
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Windows 10, Linux 3.0.101-63-default
A DESCRIPTION OF THE PROBLEM :
Bindings.bindContent for an Observable map will treat the case where the value for a key is being replaced by a new value as a removal.
The following was debugged into com.sun.javafx.binding.ContentBinding:
@Override
public void onChanged(Change<? extends K, ? extends V> change) {
final Map<K, V> map = mapRef.get();
if (map == null) {
change.getMap().removeListener(this);
} else {
if (change.wasRemoved()) {
map.remove(change.getKey());
} else {
map.put(change.getKey(), change.getValueAdded());
}
}
}
The changed.wasRemoved() check does not also see if wasAdded() returns true to see if the key was changed and will assume that it was removed.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Make a source and destination map
2. Use Bindings.bindContent to bind them together
3. Add content to the source map, then replace a value for a key
4. See that the destination map does not match the source map
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Adding things
Added: 1 -> Thing 1
Added: 2 -> Thing 2
Added: 3 -> Thing 3
Added: 4 -> Thing 4
Added: 5 -> Thing 5
Destination: {1=Thing 1, 2=Thing 2, 3=Thing 3, 4=Thing 4, 5=Thing 5}
Removing things
Removed: 3 ( was Thing 3 )
Removed: 5 ( was Thing 5 )
Destination: {1=Thing 1, 2=Thing 2, 4=Thing 4}
Adding/modifying things
Modified: 1 from Thing 1 to Thing 1-1
Modified: 2 from Thing 2 to Thing 2-2
Added: 3 -> Thing 3-3
Modified: 4 from Thing 4 to Thing 4-4
Added: 5 -> Thing 5-5
Destination: {1=Thing 1-1, 2=Thing 2-2, 3=Thing 3-3, 4=Thing 4-4, 5=Thing 5-5}
Batch remove
Removed: 1 ( was Thing 1-1 )
Removed: 5 ( was Thing 5-5 )
Destination: {2=Thing 2-2, 3=Thing 3-3, 4=Thing 4-4}
Batch add/modify
Added: 1 -> Thing 1-1-1
Modified: 3 from Thing 3-3 to Thing 3-3-3
Added: 5 -> Thing 5-5-5
Added: 6 -> Thing 6-6-6
Destination: {1=Thing 1-1-1, 2=Thing 2-2, 3=Thing 3-3-3, 4=Thing 4-4, 5=Thing 5-5-5, 6=Thing 6-6-6}
ACTUAL -
Adding things
Added: 1 -> Thing 1
Added: 2 -> Thing 2
Added: 3 -> Thing 3
Added: 4 -> Thing 4
Added: 5 -> Thing 5
Destination: {1=Thing 1, 2=Thing 2, 3=Thing 3, 4=Thing 4, 5=Thing 5}
Removing things
Removed: 3 ( was Thing 3 )
Removed: 5 ( was Thing 5 )
Destination: {1=Thing 1, 2=Thing 2, 4=Thing 4}
Adding/modifying things
Removed: 1 ( was Thing 1 )
Removed: 2 ( was Thing 2 )
Added: 3 -> Thing 3-3
Removed: 4 ( was Thing 4 )
Added: 5 -> Thing 5-5
Destination: {3=Thing 3-3, 5=Thing 5-5}
Batch remove
Removed: 5 ( was Thing 5-5 )
Destination: {3=Thing 3-3}
Batch add/modify
Added: 1 -> Thing 1-1-1
Removed: 3 ( was Thing 3-3 )
Added: 5 -> Thing 5-5-5
Added: 6 -> Thing 6-6-6
Destination: {1=Thing 1-1-1, 5=Thing 5-5-5, 6=Thing 6-6-6}
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public static void main(String[] arguments)
{
// Set up the content binding and listeners.
ObservableMap<Integer, String> source = FXCollections.observableHashMap();
ObservableMap<Integer, String> destination = FXCollections.observableHashMap();
Bindings.bindContent(destination, source);
destination.addListener(s_destinationMapListener);
// First set, add some things.
System.out.println("Adding things");
source.put(1, "Thing 1");
source.put(2, "Thing 2");
source.put(3, "Thing 3");
source.put(4, "Thing 4");
source.put(5, "Thing 5");
System.out.println("Destination: " + destination);
System.out.println();
// Next, remove some things.
System.out.println("Removing things");
source.remove(3);
source.remove(5);
System.out.println("Destination: " + destination);
System.out.println();
// Next, add and modify.
System.out.println("Adding/modifying things");
source.put(1, "Thing 1-1");
source.put(2, "Thing 2-2");
source.put(3, "Thing 3-3");
source.put(4, "Thing 4-4");
source.put(5, "Thing 5-5");
System.out.println("Destination: " + destination);
System.out.println();
// Next, remove a bunch.
System.out.println("Batch remove");
source.keySet().removeAll(Arrays.asList(new Integer[]{0, 1, 5, 6, 7}));
System.out.println("Destination: " + destination);
System.out.println();
// Next, add a bunch at once.
System.out.println("Batch add/modify");
Map<Integer, String> next = new HashMap<>();
next.put(1, "Thing 1-1-1");
next.put(3, "Thing 3-3-3");
next.put(5, "Thing 5-5-5");
next.put(6, "Thing 6-6-6");
source.putAll(next);
System.out.println("Destination: " + destination);
System.out.println();
}
private static MapChangeListener<Integer, String> s_destinationMapListener =
new MapChangeListener<Integer, String>()
{
@Override
public void onChanged(Change<? extends Integer, ? extends String> change)
{
if (change.wasAdded())
{
if (change.wasRemoved())
{
System.out.println("Modified: " + change.getKey() + " from " + change.getValueRemoved()
+ " to " + change.getValueAdded());
}
else
{
System.out.println("Added: " + change.getKey() + " -> " + change.getValueAdded());
}
}
else if (change.wasRemoved())
{
System.out.println(
"Removed: " + change.getKey() + " ( was " + change.getValueRemoved() + " )");
}
}
};
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Set up a listener on the source map and manually update the destination map.
- duplicates
-
JDK-8134934 Bindings.bindContent doesn't reflect the value changes in the ObservableMap
-
- Resolved
-