diff --git a/src/main/java/com/cedarsoftware/util/DeepEquals.java b/src/main/java/com/cedarsoftware/util/DeepEquals.java
index 1685e34e..1d679bf4 100644
--- a/src/main/java/com/cedarsoftware/util/DeepEquals.java
+++ b/src/main/java/com/cedarsoftware/util/DeepEquals.java
@@ -34,12 +34,12 @@
* overide .equals() / .hashCode() to be compared. For example, testing for
* existence in a cache. Relying on an object's identity will not locate an
* equivalent object in a cache.
- *
+ *
* This method will handle cycles correctly, for example A->B->C->A. Suppose a and
* a' are two separate instances of A with the same values for all fields on
* A, B, and C. Then a.deepEquals(a') will return true. It uses cycle detection
* storing visited objects in a Set to prevent endless loops.
- *
+ *
* Numbers will be compared for value. Meaning an int that has the same value
* as a long will match. Similarly, a double that has the same value as a long
* will match. If the flag "ALLOW_STRING_TO_MATCH_NUMBERS" is passed in the options
@@ -64,9 +64,9 @@
* limitations under the License.
*/
@SuppressWarnings("unchecked")
-public class DeepEquals
-{
- private DeepEquals () {}
+public class DeepEquals {
+ private DeepEquals() {
+ }
public static final String IGNORE_CUSTOM_EQUALS = "ignoreCustomEquals";
public static final String ALLOW_STRINGS_TO_MATCH_NUMBERS = "stringsCanMatchNumbers";
@@ -76,8 +76,7 @@ private DeepEquals () {}
private static final double floatEplison = 1e-6;
private static final Set> prims = new HashSet<>();
- static
- {
+ static {
prims.add(Byte.class);
prims.add(Integer.class);
prims.add(Long.class);
@@ -88,21 +87,17 @@ private DeepEquals () {}
prims.add(Short.class);
}
- private final static class ItemsToCompare
- {
+ private final static class ItemsToCompare {
private final Object _key1;
private final Object _key2;
- private ItemsToCompare(Object k1, Object k2)
- {
+ private ItemsToCompare(Object k1, Object k2) {
_key1 = k1;
_key2 = k2;
}
- public boolean equals(Object other)
- {
- if (!(other instanceof ItemsToCompare))
- {
+ public boolean equals(Object other) {
+ if (!(other instanceof ItemsToCompare)) {
return false;
}
@@ -110,17 +105,14 @@ public boolean equals(Object other)
return _key1 == that._key1 && _key2 == that._key2;
}
- public int hashCode()
- {
+ public int hashCode() {
int h1 = _key1 != null ? _key1.hashCode() : 0;
int h2 = _key2 != null ? _key2.hashCode() : 0;
return h1 + h2;
}
- public String toString()
- {
- if (_key1.getClass().isPrimitive() && _key2.getClass().isPrimitive())
- {
+ public String toString() {
+ if (_key1.getClass().isPrimitive() && _key2.getClass().isPrimitive()) {
return _key1 + " | " + _key2;
}
return _key1.getClass().getName() + " | " + _key2.getClass().getName();
@@ -139,11 +131,12 @@ public String toString()
* overide .equals() / .hashCode() to be compared. For example, testing for
* existence in a cache. Relying on an objects identity will not locate an
* object in cache, yet relying on it being equivalent will.
- *
+ *
* This method will handle cycles correctly, for example A->B->C->A. Suppose a and
* a' are two separate instances of the A with the same values for all fields on
* A, B, and C. Then a.deepEquals(a') will return true. It uses cycle detection
* storing visited objects in a Set to prevent endless loops.
+ *
* @param a Object one to compare
* @param b Object two to compare
* @return true if a is equivalent to b, false otherwise. Equivalent means that
@@ -151,8 +144,7 @@ public String toString()
* or via the respectively encountered overridden .equals() methods during
* traversal.
*/
- public static boolean deepEquals(Object a, Object b)
- {
+ public static boolean deepEquals(Object a, Object b) {
return deepEquals(a, b, new HashMap<>());
}
@@ -168,13 +160,14 @@ public static boolean deepEquals(Object a, Object b)
* overide .equals() / .hashCode() to be compared. For example, testing for
* existence in a cache. Relying on an objects identity will not locate an
* object in cache, yet relying on it being equivalent will.
- *
+ *
* This method will handle cycles correctly, for example A->B->C->A. Suppose a and
* a' are two separate instances of the A with the same values for all fields on
* A, B, and C. Then a.deepEquals(a') will return true. It uses cycle detection
* storing visited objects in a Set to prevent endless loops.
- * @param a Object one to compare
- * @param b Object two to compare
+ *
+ * @param a Object one to compare
+ * @param b Object two to compare
* @param options Map options for compare. With no option, if a custom equals()
* method is present, it will be used. If IGNORE_CUSTOM_EQUALS is
* present, it will be expected to be a Set of classes to ignore.
@@ -182,157 +175,120 @@ public static boolean deepEquals(Object a, Object b)
* using .equals() even if the classes have a custom .equals() method
* present. If it is and empty set, then no custom .equals() methods
* will be called.
- *
* @return true if a is equivalent to b, false otherwise. Equivalent means that
* all field values of both subgraphs are the same, either at the field level
* or via the respectively encountered overridden .equals() methods during
* traversal.
*/
- public static boolean deepEquals(Object a, Object b, Map options)
- {
+ public static boolean deepEquals(Object a, Object b, Map options) {
Set visited = new HashSet<>();
return deepEquals(a, b, options, visited);
}
- private static boolean deepEquals(Object a, Object b, Map options, Set visited)
- {
+ private static boolean deepEquals(Object a, Object b, Map options, Set visited) {
Deque stack = new LinkedList<>();
Set> ignoreCustomEquals = (Set>) options.get(IGNORE_CUSTOM_EQUALS);
final boolean allowStringsToMatchNumbers = convert2boolean(options.get(ALLOW_STRINGS_TO_MATCH_NUMBERS));
stack.addFirst(new ItemsToCompare(a, b));
- while (!stack.isEmpty())
- {
+ while (!stack.isEmpty()) {
ItemsToCompare itemsToCompare = stack.removeFirst();
visited.add(itemsToCompare);
final Object key1 = itemsToCompare._key1;
final Object key2 = itemsToCompare._key2;
- if (key1 == key2)
- { // Same instance is always equal to itself.
+ if (key1 == key2) { // Same instance is always equal to itself.
continue;
}
- if (key1 == null || key2 == null)
- { // If either one is null, they are not equal (both can't be null, due to above comparison).
+ if (key1 == null || key2 == null) { // If either one is null, they are not equal (both can't be null, due to above comparison).
return false;
}
- if (key1 instanceof Number && key2 instanceof Number && compareNumbers((Number)key1, (Number)key2))
- {
+ if (key1 instanceof Number && key2 instanceof Number && compareNumbers((Number) key1, (Number) key2)) {
continue;
}
- if (key1 instanceof AtomicBoolean && key2 instanceof AtomicBoolean)
- {
- if (!compareAtomicBoolean((AtomicBoolean)key1, (AtomicBoolean)key2)) {
+ if (key1 instanceof AtomicBoolean && key2 instanceof AtomicBoolean) {
+ if (!compareAtomicBoolean((AtomicBoolean) key1, (AtomicBoolean) key2)) {
return false;
} else {
continue;
}
}
- if (key1 instanceof Number || key2 instanceof Number)
- { // If one is a Number and the other one is not, then optionally compare them as strings, otherwise return false
- if (allowStringsToMatchNumbers)
- {
- try
- {
- if (key1 instanceof String && compareNumbers(convert2BigDecimal(key1), (Number)key2))
- {
+ if (key1 instanceof Number || key2 instanceof Number) { // If one is a Number and the other one is not, then optionally compare them as strings, otherwise return false
+ if (allowStringsToMatchNumbers) {
+ try {
+ if (key1 instanceof String && compareNumbers(convert2BigDecimal(key1), (Number) key2)) {
continue;
- }
- else if (key2 instanceof String && compareNumbers((Number)key1, convert2BigDecimal(key2)))
- {
+ } else if (key2 instanceof String && compareNumbers((Number) key1, convert2BigDecimal(key2))) {
continue;
}
+ } catch (Exception ignore) {
}
- catch (Exception ignore) { }
}
return false;
}
Class> key1Class = key1.getClass();
- if (key1Class.isPrimitive() || prims.contains(key1Class) || key1 instanceof String || key1 instanceof Date || key1 instanceof Class)
- {
- if (!key1.equals(key2))
- {
+ if (key1Class.isPrimitive() || prims.contains(key1Class) || key1 instanceof String || key1 instanceof Date || key1 instanceof Class) {
+ if (!key1.equals(key2)) {
return false;
}
continue; // Nothing further to push on the stack
}
- if (key1 instanceof Set)
- {
- if (!(key2 instanceof Set))
- {
+ if (key1 instanceof Set) {
+ if (!(key2 instanceof Set)) {
return false;
}
- }
- else if (key2 instanceof Set)
- {
+ } else if (key2 instanceof Set) {
return false;
}
- if (key1 instanceof Collection)
- { // If Collections, they both must be Collection
- if (!(key2 instanceof Collection))
- {
+ if (key1 instanceof Collection) { // If Collections, they both must be Collection
+ if (!(key2 instanceof Collection)) {
return false;
}
- }
- else if (key2 instanceof Collection)
- {
+ } else if (key2 instanceof Collection) {
return false;
}
- if (key1 instanceof Map)
- {
- if (!(key2 instanceof Map))
- {
+ if (key1 instanceof Map) {
+ if (!(key2 instanceof Map)) {
return false;
}
- }
- else if (key2 instanceof Map)
- {
+ } else if (key2 instanceof Map) {
return false;
}
Class> key2Class = key2.getClass();
- if (key1Class.isArray())
- {
- if (!key2Class.isArray())
- {
+ if (key1Class.isArray()) {
+ if (!key2Class.isArray()) {
return false;
}
- }
- else if (key2Class.isArray())
- {
+ } else if (key2Class.isArray()) {
return false;
}
- if (!isContainerType(key1) && !isContainerType(key2) && !key1Class.equals(key2.getClass()))
- { // Must be same class
+ if (!isContainerType(key1) && !isContainerType(key2) && !key1Class.equals(key2.getClass())) { // Must be same class
return false;
}
// Special handle Sets - items matter but order does not for equality.
- if (key1 instanceof Set>)
- {
- if (!compareUnorderedCollection((Collection>) key1, (Collection>) key2, stack, visited, options))
- {
+ if (key1 instanceof Set>) {
+ if (!compareUnorderedCollection((Collection>) key1, (Collection>) key2, stack, visited, options)) {
return false;
}
continue;
}
// Collections must match in items and order for equality.
- if (key1 instanceof Collection>)
- {
- if (!compareOrderedCollection((Collection>) key1, (Collection>) key2, stack, visited))
- {
+ if (key1 instanceof Collection>) {
+ if (!compareOrderedCollection((Collection>) key1, (Collection>) key2, stack, visited)) {
return false;
}
continue;
@@ -341,10 +297,8 @@ else if (key2Class.isArray())
// Compare two Maps. This is a slightly more expensive comparison because
// order cannot be assumed, therefore a temporary Map must be created, however the
// comparison still runs in O(N) time.
- if (key1 instanceof Map)
- {
- if (!compareMap((Map, ?>) key1, (Map, ?>) key2, stack, visited, options))
- {
+ if (key1 instanceof Map) {
+ if (!compareMap((Map, ?>) key1, (Map, ?>) key2, stack, visited, options)) {
return false;
}
continue;
@@ -353,10 +307,8 @@ else if (key2Class.isArray())
// Handle all [] types. In order to be equal, the arrays must be the same
// length, be of the same type, be in the same order, and all elements within
// the array must be deeply equivalent.
- if (key1Class.isArray())
- {
- if (!compareArrays(key1, key2, stack, visited))
- {
+ if (key1Class.isArray()) {
+ if (!compareArrays(key1, key2, stack, visited)) {
return false;
}
continue;
@@ -366,12 +318,9 @@ else if (key2Class.isArray())
// the caller has not specified any classes to skip ... OR
// the caller has specified come classes to ignore and this one is not in the list ... THEN
// compare using the custom equals.
- if (hasCustomEquals(key1Class))
- {
- if (ignoreCustomEquals == null || (ignoreCustomEquals.size() > 0 && !ignoreCustomEquals.contains(key1Class)))
- {
- if (!key1.equals(key2))
- {
+ if (hasCustomEquals(key1Class)) {
+ if (ignoreCustomEquals == null || (ignoreCustomEquals.size() > 0 && !ignoreCustomEquals.contains(key1Class))) {
+ if (!key1.equals(key2)) {
return false;
}
continue;
@@ -380,53 +329,45 @@ else if (key2Class.isArray())
Collection fields = ReflectionUtils.getDeepDeclaredFields(key1Class);
- for (Field field : fields)
- {
- try
- {
+ for (Field field : fields) {
+ try {
ItemsToCompare dk = new ItemsToCompare(field.get(key1), field.get(key2));
- if (!visited.contains(dk))
- {
+ if (!visited.contains(dk)) {
stack.addFirst(dk);
}
+ } catch (Exception ignored) {
}
- catch (Exception ignored)
- { }
}
}
return true;
}
- public static boolean isContainerType(Object o)
- {
+ public static boolean isContainerType(Object o) {
return o instanceof Collection || o instanceof Map;
}
/**
* Deeply compare to Arrays []. Both arrays must be of the same type, same length, and all
* elements within the arrays must be deeply equal in order to return true.
- * @param array1 [] type (Object[], String[], etc.)
- * @param array2 [] type (Object[], String[], etc.)
- * @param stack add items to compare to the Stack (Stack versus recursion)
+ *
+ * @param array1 [] type (Object[], String[], etc.)
+ * @param array2 [] type (Object[], String[], etc.)
+ * @param stack add items to compare to the Stack (Stack versus recursion)
* @param visited Set of objects already compared (prevents cycles)
* @return true if the two arrays are the same length and contain deeply equivalent items.
*/
- private static boolean compareArrays(Object array1, Object array2, Deque stack, Set> visited)
- {
+ private static boolean compareArrays(Object array1, Object array2, Deque stack, Set visited) {
// Same instance check already performed...
final int len = Array.getLength(array1);
- if (len != Array.getLength(array2))
- {
+ if (len != Array.getLength(array2)) {
return false;
}
- for (int i = 0; i < len; i++)
- {
+ for (int i = 0; i < len; i++) {
ItemsToCompare dk = new ItemsToCompare(Array.get(array1, i), Array.get(array2, i));
- if (!visited.contains(dk))
- { // push contents for further comparison
+ if (!visited.contains(dk)) { // push contents for further comparison
stack.addFirst(dk);
}
}
@@ -435,30 +376,27 @@ private static boolean compareArrays(Object array1, Object array2, Deque col1, Collection> col2, Deque stack, Set> visited)
- {
+ private static boolean compareOrderedCollection(Collection> col1, Collection> col2, Deque stack, Set visited) {
// Same instance check already performed...
- if (col1.size() != col2.size())
- {
+ if (col1.size() != col2.size()) {
return false;
}
Iterator> i1 = col1.iterator();
Iterator> i2 = col2.iterator();
- while (i1.hasNext())
- {
+ while (i1.hasNext()) {
ItemsToCompare dk = new ItemsToCompare(i1.next(), i2.next());
- if (!visited.contains(dk))
- { // push contents for further comparison
+ if (!visited.contains(dk)) { // push contents for further comparison
stack.addFirst(dk);
}
}
@@ -472,85 +410,71 @@ private static boolean compareOrderedCollection(Collection> col1, Collection
* can walk the other collection and look for each item in the map, which
* runs in O(N) time, rather than an O(N^2) lookup that would occur if each
* item from collection one was scanned for in collection two.
- * @param col1 First collection of items to compare
- * @param col2 Second collection of items to compare
- * @param stack add items to compare to the Stack (Stack versus recursion)
- * @param visited Set containing items that have already been compared,
- * so as to prevent cycles.
+ *
+ * @param col1 First collection of items to compare
+ * @param col2 Second collection of items to compare
+ * @param stack add items to compare to the Stack (Stack versus recursion)
+ * @param visited Set containing items that have already been compared, to prevent cycles.
* @param options the options for comparison (see {@link #deepEquals(Object, Object, Map)}
* @return boolean false if the Collections are for certain not equals. A
* value of 'true' indicates that the Collections may be equal, and the sets
* items will be added to the Stack for further comparison.
*/
- private static boolean compareUnorderedCollection(Collection> col1, Collection> col2, Deque stack, Set visited, Map options)
- {
+ private static boolean compareUnorderedCollection(Collection> col1, Collection> col2, Deque stack, Set visited, Map options) {
// Same instance check already performed...
-
- if (col1.size() != col2.size())
- {
+ if (col1.size() != col2.size()) {
return false;
}
Map> fastLookup = new HashMap<>();
- for (Object o : col2)
- {
+ for (Object o : col2) {
int hash = deepHashCode(o);
- Collection