mirror of
https://github.com/lightbend/config.git
synced 2025-03-23 07:40:25 +08:00
Fix equality between ConfigObject of different types
By pulling equals/hashCode up into AbstractConfigObject
This commit is contained in:
parent
6528ac5d1e
commit
52f34925dc
@ -3,6 +3,7 @@ package com.typesafe.config.impl;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -569,6 +570,58 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean mapEquals(Map<String, ConfigValue> a,
|
||||
Map<String, ConfigValue> b) {
|
||||
Set<String> aKeys = a.keySet();
|
||||
Set<String> bKeys = b.keySet();
|
||||
|
||||
if (!aKeys.equals(bKeys))
|
||||
return false;
|
||||
|
||||
for (String key : aKeys) {
|
||||
if (!a.get(key).equals(b.get(key)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int mapHash(Map<String, ConfigValue> m) {
|
||||
// the keys have to be sorted, otherwise we could be equal
|
||||
// to another map but have a different hashcode.
|
||||
List<String> keys = new ArrayList<String>();
|
||||
keys.addAll(m.keySet());
|
||||
Collections.sort(keys);
|
||||
|
||||
int valuesHash = 0;
|
||||
for (String k : keys) {
|
||||
valuesHash += m.get(k).hashCode();
|
||||
}
|
||||
return 41 * (41 + keys.hashCode()) + valuesHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canEqual(Object other) {
|
||||
return other instanceof ConfigObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
if (other instanceof ConfigObject) {
|
||||
// optimization to avoid unwrapped() for two ConfigObject,
|
||||
// which is what AbstractConfigValue does.
|
||||
return canEqual(other) && mapEquals(this, ((ConfigObject) other));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
return mapHash(this);
|
||||
}
|
||||
|
||||
private static UnsupportedOperationException weAreImmutable(String method) {
|
||||
return new UnsupportedOperationException(
|
||||
"ConfigObject is immutable, you can't call Map.'" + method
|
||||
|
@ -1,17 +1,14 @@
|
||||
package com.typesafe.config.impl;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.typesafe.config.ConfigException;
|
||||
import com.typesafe.config.ConfigObject;
|
||||
import com.typesafe.config.ConfigOrigin;
|
||||
import com.typesafe.config.ConfigValue;
|
||||
|
||||
@ -83,62 +80,6 @@ final class SimpleConfigObject extends AbstractConfigObject {
|
||||
return value.keySet();
|
||||
}
|
||||
|
||||
private static boolean mapEquals(Map<String, AbstractConfigValue> a,
|
||||
Map<String, AbstractConfigValue> b) {
|
||||
Set<String> aKeys = a.keySet();
|
||||
Set<String> bKeys = b.keySet();
|
||||
|
||||
if (!aKeys.equals(bKeys))
|
||||
return false;
|
||||
|
||||
for (String key : aKeys) {
|
||||
if (!a.get(key).equals(b.get(key)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int mapHash(Map<String, AbstractConfigValue> m) {
|
||||
// the keys have to be sorted, otherwise we could be equal
|
||||
// to another map but have a different hashcode.
|
||||
List<String> keys = new ArrayList<String>();
|
||||
keys.addAll(m.keySet());
|
||||
Collections.sort(keys);
|
||||
|
||||
int valuesHash = 0;
|
||||
for (String k : keys) {
|
||||
valuesHash += m.get(k).hashCode();
|
||||
}
|
||||
return 41 * (41 + keys.hashCode()) + valuesHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canEqual(Object other) {
|
||||
return other instanceof ConfigObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
if (other instanceof SimpleConfigObject) {
|
||||
// optimization to avoid unwrapped() for two SimpleConfigObject
|
||||
// note: if this included "transformer" then we could never be
|
||||
// equal to a non-SimpleConfigObject ConfigObject.
|
||||
return canEqual(other)
|
||||
&& mapEquals(value, ((SimpleConfigObject) other).value);
|
||||
} else if (other instanceof ConfigObject) {
|
||||
return super.equals(other);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// note that "origin" is deliberately NOT part of equality
|
||||
return mapHash(this.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object v) {
|
||||
return value.containsValue(v);
|
||||
|
@ -60,13 +60,25 @@ class ConfigValueTest extends TestUtils {
|
||||
val aMap = configMap("a" -> 1, "b" -> 2, "c" -> 3)
|
||||
val sameAsAMap = configMap("a" -> 1, "b" -> 2, "c" -> 3)
|
||||
val bMap = configMap("a" -> 3, "b" -> 4, "c" -> 5)
|
||||
// different keys is a different case in the equals implementation
|
||||
val cMap = configMap("x" -> 3, "y" -> 4, "z" -> 5)
|
||||
val a = new SimpleConfigObject(fakeOrigin(), aMap)
|
||||
val sameAsA = new SimpleConfigObject(fakeOrigin(), sameAsAMap)
|
||||
val b = new SimpleConfigObject(fakeOrigin(), bMap)
|
||||
val c = new SimpleConfigObject(fakeOrigin(), cMap)
|
||||
|
||||
checkEqualObjects(a, a)
|
||||
checkEqualObjects(a, sameAsA)
|
||||
checkEqualObjects(b, b)
|
||||
checkEqualObjects(c, c)
|
||||
checkNotEqualObjects(a, b)
|
||||
checkNotEqualObjects(a, c)
|
||||
checkNotEqualObjects(b, c)
|
||||
|
||||
val root = a.asRoot()
|
||||
checkEqualObjects(a, root)
|
||||
checkNotEqualObjects(root, b)
|
||||
checkNotEqualObjects(root, c)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
Reference in New Issue
Block a user