Bug 576333 - Simplify ConcurrentNeutralValueMap
Fixed and added test for toString, hashCode, equals just in case it
would ever be used.
Change-Id: I4a55101530c3ce813dd80ebdba05d4b2dd867c6f
Signed-off-by: Joerg Kubitz <jkubitz-eclipse@gmx.de>
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.runtime/+/186070
Reviewed-by: Sebastian Zarnekow <sebastian.zarnekow@gmail.com>
Tested-by: Platform Bot <platform-bot@eclipse.org>
diff --git a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ConcurrentNeutralValueMap.java b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ConcurrentNeutralValueMap.java
index f588fac..01e898e 100644
--- a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ConcurrentNeutralValueMap.java
+++ b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ConcurrentNeutralValueMap.java
@@ -27,6 +27,7 @@
final ConcurrentHashMap<K, V> delegate = new ConcurrentHashMap<>();
/** a value that is used for null elements **/
final private V neutralValue;
+ final private static NullValue NULL = new NullValue();
/**
* @param neutralValue a Element that does not equal any other Value
@@ -36,6 +37,11 @@
this.neutralValue = neutralValue;
}
+ @SuppressWarnings("unchecked")
+ public ConcurrentNeutralValueMap() {
+ this((V) NULL);
+ }
+
private V wrapValue(V value) {
return value == null ? neutralValue : value;
}
@@ -49,6 +55,7 @@
}
private static final class NullValue {
+ /** for {@link ConcurrentNeutralValueMap#toString()} **/
@Override
public String toString() {
return "null"; //$NON-NLS-1$
@@ -56,34 +63,42 @@
@Override
public boolean equals(Object obj) {
- return false;
+ // currently not called since ConcurrentHashMap checks for value1==value2
+ // but just in case the Implementation changes:
+ // in means of ConcurrentNeutralValueMap#equals() every NullValue equals
+ return obj instanceof NullValue;
+ // like java.util.Objects.equals(null, null) would return true,
}
+ /** for {@link ConcurrentNeutralValueMap#hashCode()} **/
@Override
public int hashCode() {
+ // in means of ConcurrentNeutralValueMap#hashCode() we need a hashCode
+ // even tough null.hashCode() would normally throw NullPointerException.
return 0x55555555;
+ // like java.util.Objects.hashCode(null) would return an int,
}
}
- /** returns a neutral Element that does not equals any other **/
- public static Object neutralObject() {
- return new NullValue();
- }
-
/*
* Method overloads from java.lang.Object:
*/
+ /** return uses hashCode() of the neutralValue **/
@Override
public int hashCode() {
return delegate.hashCode();
}
+ /** return does NOT use equals() of the neutralValue **/
@Override
public boolean equals(Object obj) {
- return delegate.equals(obj);
+ if (!(obj instanceof ConcurrentNeutralValueMap))
+ return false;
+ return delegate.equals(((ConcurrentNeutralValueMap<?, ?>) obj).delegate);
}
+ /** return uses toString() of the neutralValue **/
@Override
public String toString() {
return delegate.toString();
@@ -186,9 +201,12 @@
boolean isPresent();
/**
- * @return the result of map.get(key)
+ * Callers are supposed to check #isPresent first to determine whether a
+ * returned null was a null value or the key was not present.
+ *
+ * @return the unwrapped result of map.get(key). May return null.
*/
- V unwraped();
+ V unwrapped();
}
private final class Wrapped implements Value<V> {
@@ -204,7 +222,7 @@
}
@Override
- public V unwraped() {
+ public V unwrapped() {
return unwrapValue(wrappedValue);
}
}
@@ -212,7 +230,7 @@
/**
* @return a value that is wrapped into a Value Object.
* @see Value#isPresent()
- * @see Value#unwraped()
+ * @see Value#unwrapped()
*/
public Value<V> getValue(K key) {
return new Wrapped(delegate.get(key));
diff --git a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/EclipseContext.java b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/EclipseContext.java
index e8b7311..c777db8 100644
--- a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/EclipseContext.java
+++ b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/EclipseContext.java
@@ -95,7 +95,7 @@
private Map<String, ValueComputation> localValueComputations = new ConcurrentHashMap<>();
final protected ConcurrentNeutralValueMap<String, Object> localValues = // null values allowed
- new ConcurrentNeutralValueMap<>(ConcurrentNeutralValueMap.neutralObject());
+ new ConcurrentNeutralValueMap<>();
private Set<String> modifiable;
@@ -249,7 +249,7 @@
// 1. try for local value
Value<Object> value = localValues.getValue(name);
if (value.isPresent()) {
- result = value.unwraped();
+ result = value.unwrapped();
if (result == null)
return null;
} else
@@ -314,7 +314,7 @@
Value<Object> value = localValues.getValue(name);
if (!value.isPresent())
return false;
- return (value.unwraped() == newValue);
+ return (value.unwrapped() == newValue);
}
private boolean isSetLocally(String name) {
@@ -365,7 +365,7 @@
}
Value<Object> old = localValues.putAndGetOld(name, value);
boolean containsKey = old.isPresent();
- Object oldValue = old.unwraped();
+ Object oldValue = old.unwrapped();
if (!containsKey || oldValue != value) {
Set<Scheduled> scheduled = new LinkedHashSet<>();
invalidate(name, ContextChangeEvent.ADDED, oldValue, value, scheduled);
@@ -400,7 +400,7 @@
String tmp = "Variable " + name + " is not modifiable in the context " + this; //$NON-NLS-1$ //$NON-NLS-2$
throw new IllegalArgumentException(tmp);
}
- Object oldValue = localValues.putAndGetOld(name, value).unwraped();
+ Object oldValue = localValues.putAndGetOld(name, value).unwrapped();
if (oldValue != value)
invalidate(name, ContextChangeEvent.ADDED, oldValue, value, scheduled);
return true;
diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/NeutralValueTest.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/NeutralValueTest.java
index dccb2c3..0217012 100644
--- a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/NeutralValueTest.java
+++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/NeutralValueTest.java
@@ -28,14 +28,6 @@
public class NeutralValueTest {
@Test
- public void testNeutralObject() {
- assertFalse(new Object().equals(ConcurrentNeutralValueMap.neutralObject()));
- assertFalse(ConcurrentNeutralValueMap.neutralObject().equals(new Object()));
- assertNotEquals(ConcurrentNeutralValueMap.neutralObject(), ConcurrentNeutralValueMap.neutralObject());
- assertEquals("" + null, ConcurrentNeutralValueMap.neutralObject().toString());
- }
-
- @Test
public void testConcurrentNeutralValueMap() {
ConcurrentNeutralValueMap<String, Double> map = new ConcurrentNeutralValueMap<>(Double.NaN);
map.put("nix", null); // modify
@@ -78,29 +70,29 @@
{
Value<Double> v = map.getValue("nix");
assertTrue(v.isPresent());
- assertEquals(null, v.unwraped());
+ assertEquals(null, v.unwrapped());
}
{
Value<Double> v = map.getValue("1");
assertFalse(v.isPresent());
- assertEquals(null, v.unwraped());
+ assertEquals(null, v.unwrapped());
}
{
Value<Double> v = map.getValue("2");
assertTrue(v.isPresent());
- assertEquals(Double.valueOf(2.0), v.unwraped());
+ assertEquals(Double.valueOf(2.0), v.unwrapped());
}
{
Value<Double> v = map.putAndGetOld("5", 5555.0); // modify
assertFalse(v.isPresent());
- assertEquals(null, v.unwraped());
+ assertEquals(null, v.unwrapped());
assertEquals(Double.valueOf(5555.0), map.get("5"));
}
{
Value<Double> v = map.putAndGetOld("5", 5.0); // modify
assertTrue(v.isPresent());
- assertEquals(Double.valueOf(5555.0), v.unwraped());
+ assertEquals(Double.valueOf(5555.0), v.unwrapped());
assertEquals(Double.valueOf(5.0), map.get("5"));
}
map.putIfAbsent("5", 5555.0); // modify
@@ -111,27 +103,27 @@
{
Value<Double> v = map.putAndGetOld("five", null); // modify
assertFalse(v.isPresent());
- assertEquals(null, v.unwraped());
+ assertEquals(null, v.unwrapped());
assertEquals(null, map.get("five"));
}
{
Value<Double> v = map.putAndGetOld("five", 5.0); // modify
assertTrue(v.isPresent());
- assertEquals(null, v.unwraped());
+ assertEquals(null, v.unwrapped());
assertEquals(Double.valueOf(5.0), map.get("five"));
}
{
map.putIfAbsent("five", null); // modify
Value<Double> v = map.getValue("five");
assertTrue(v.isPresent());
- assertEquals(Double.valueOf(5.0), v.unwraped());
+ assertEquals(Double.valueOf(5.0), v.unwrapped());
}
map.remove("five"); // modify
{
map.putIfAbsent("five", null); // modify
Value<Double> v = map.getValue("five");
assertTrue(v.isPresent());
- assertEquals(null, v.unwraped());
+ assertEquals(null, v.unwrapped());
}
map.remove("five"); // modify
assertFalse(map.containsKey("five"));
@@ -141,4 +133,73 @@
assertTrue(map.isEmpty());
}
+ @Test
+ public void testToString() {
+ ConcurrentNeutralValueMap<String, Float> map1 = new ConcurrentNeutralValueMap<>();
+ map1.put("0", 0f);
+ map1.put("NULL", null);
+ map1.put("nothing", null);
+ map1.put("1", 1f);
+ map1.put("~2", 2.1f);
+ assertTrue(map1.toString().contains("0=0.0"));
+ assertTrue(map1.toString().contains("1=1.0"));
+ assertTrue(map1.toString().contains("~2=2.1"));
+ assertTrue(map1.toString().contains("NULL=null"));
+ assertTrue(map1.toString().contains("nothing=null"));
+ }
+
+ @Test
+ public void testCustomToString() {
+ ConcurrentNeutralValueMap<String, Float> map1 = new ConcurrentNeutralValueMap<>(Float.NaN);
+ map1.put("0", 0f);
+ map1.put("NULL", null);
+ assertTrue(map1.toString().contains("0=0.0"));
+ assertTrue(map1.toString().contains("NULL=NaN"));
+ }
+
+ @Test
+ public void testEquals() {
+ {
+ ConcurrentNeutralValueMap<String, Float> map1 = new ConcurrentNeutralValueMap<>();
+ ConcurrentNeutralValueMap<String, Float> map2 = new ConcurrentNeutralValueMap<>();
+ map1.put("0", 0f);
+ map1.put("NULL", null);
+ map2.put("NULL", null);
+ map2.put("0", 0f);
+ assertEquals(map1.hashCode(), map2.hashCode());
+ assertEquals(map1, map2);
+ }
+ {
+ ConcurrentNeutralValueMap<String, Float> map1 = new ConcurrentNeutralValueMap<>();
+ ConcurrentNeutralValueMap<String, Float> map2 = new ConcurrentNeutralValueMap<>();
+ map1.put("0", 0f);
+ map1.put("1", 1f);
+ map1.put("NULL", null);
+ map2.put("NULL", null);
+ map2.put("0", 0f);
+ assertNotEquals(map1.hashCode(), map2.hashCode());
+ assertNotEquals(map1, map2);
+ }
+ {
+ ConcurrentNeutralValueMap<String, Float> map1 = new ConcurrentNeutralValueMap<>(Float.NaN);
+ ConcurrentNeutralValueMap<String, Float> map2 = new ConcurrentNeutralValueMap<>(Float.NaN);
+ map1.put("0", 0f);
+ map1.put("NULL", null);
+ map2.put("NULL", null);
+ map2.put("0", 0f);
+ assertEquals(map1.hashCode(), map2.hashCode());
+ assertEquals(map1, map2);
+ }
+ {
+ ConcurrentNeutralValueMap<String, Float> map1 = new ConcurrentNeutralValueMap<>(Float.NaN);
+ ConcurrentNeutralValueMap<String, Float> map2 = new ConcurrentNeutralValueMap<>(Float.NaN);
+ map1.put("0", 0f);
+ map1.put("1", 1f);
+ map1.put("NULL", null);
+ map2.put("NULL", null);
+ map2.put("0", 0f);
+ assertNotEquals(map1.hashCode(), map2.hashCode());
+ assertNotEquals(map1, map2);
+ }
+ }
}