diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/jaxrs/api/CompareServersTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/jaxrs/api/CompareServersTest.java index f6faa281528..d05666853ab 100755 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/jaxrs/api/CompareServersTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/jaxrs/api/CompareServersTest.java @@ -4,6 +4,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -82,6 +83,7 @@ public class CompareServersTest { b.put("array", bArr); bArr.add("123"); bArr.add("234"); + assertTrue(resource.equalsWithArrayOrderIgnored(a, b)); final Pair result = resource.removeDuplicateEntries(a, b); assertNull(result.getA()); assertNull(result.getB()); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompareServersResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompareServersResource.java index 41b7133e863..cfe133223af 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompareServersResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/CompareServersResource.java @@ -36,6 +36,7 @@ import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; import com.sap.sailing.server.gateway.serialization.LeaderboardGroupConstants; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; +import com.sap.sse.concurrent.ConcurrentHashBag; import com.sap.sse.security.util.RemoteServerUtil; import com.sap.sse.util.HttpUrlConnectionHelper; @@ -332,7 +333,7 @@ public class CompareServersResource extends AbstractSailingServerResource { */ Pair removeDuplicateEntries(Object lg1, Object lg2) { final Pair result; - if (lg1.equals(lg2)) { + if (equalsWithArrayOrderIgnored(lg1, lg2)) { result = new Pair(null, null); } else { if (lg1 instanceof JSONObject && lg2 instanceof JSONObject) { @@ -345,8 +346,51 @@ public class CompareServersResource extends AbstractSailingServerResource { return result; } + /** + * The two objects {@code a} and {@code b} are considered equal if they are the same, or if {@code a.equals(b)}, + * or if {@code a} and {@code b} are both of type {@link JSONArray} and their elements, when thrown into a "Bag" + * (ignoring order but keeping track of the count of equal objects), lead to two bags comparing equals, or if + * {@code a} and {@code b} are both of type {@link JSONObject} and the values for all equal keys pass this test.

+ * + * Note that this works recursively along {@link JSONArray} and {@link JSONObject} hierarchies. + */ + boolean equalsWithArrayOrderIgnored(Object a, Object b) { + final boolean result; + if (a == b || Util.equalsWithNull(a, b)) { + result = true; + } else { + if (a instanceof JSONArray && b instanceof JSONArray) { + final ConcurrentHashBag aBag = new ConcurrentHashBag<>(); + for (final Object aObject : (JSONArray) a) { + aBag.add(aObject); + } + final ConcurrentHashBag bBag = new ConcurrentHashBag<>(); + for (final Object bObject : (JSONArray) b) { + bBag.add(bObject); + } + result = aBag.equals(bBag); + } else if (a instanceof JSONObject && b instanceof JSONObject) { + if (((JSONObject) a).keySet().equals(((JSONObject) b).keySet())) { + boolean allValuesRecursivelyEqual = true; + for (final Object aKey : ((JSONObject) a).keySet()) { + if (!equalsWithArrayOrderIgnored(((JSONObject) a).get(aKey), ((JSONObject) b).get(aKey))) { + allValuesRecursivelyEqual = false; + break; + } + } + result = allValuesRecursivelyEqual; + } else { + result = false; + } + } else { + result = false; + } + } + return result; + } private void removeDuplicateEntries(JSONObject lg1, JSONObject lg2) { + // TODO note that we assume that the LeaderboardGroupConstants.NAME attribute name is used throughout the hierarchy to identify object "names" which is used as key during object comparison if ((!lg1.containsKey(LeaderboardGroupConstants.NAME) && !lg2.containsKey(LeaderboardGroupConstants.NAME)) || Util.equalsWithNull(lg1.get(LeaderboardGroupConstants.NAME), lg2.get(LeaderboardGroupConstants.NAME))) { final Iterator iter1 = lg1.keySet().iterator(); @@ -355,9 +399,9 @@ public class CompareServersResource extends AbstractSailingServerResource { if (lg2.containsKey(key)) { Object value1 = lg1.get(key); Object value2 = lg2.get(key); - if (KEYSETTOPRINT.contains(key) && Util.equalsWithNull(value1, value2)) { + if (KEYSETTOPRINT.contains(key) && equalsWithArrayOrderIgnored(value1, value2)) { continue; - } else if (Util.equalsWithNull(value1, value2) && KEYSETTOCOMPARE.contains(key)) { + } else if (equalsWithArrayOrderIgnored(value1, value2) && KEYSETTOCOMPARE.contains(key)) { // keys which are to be compared and whose values are equal are removed from both sides; // in particular, this affects JSONArray values comparing equal; this requires equal element order, too iter1.remove(); @@ -371,20 +415,20 @@ public class CompareServersResource extends AbstractSailingServerResource { } private void removeDuplicateEntries(JSONArray json1, JSONArray json2) { - if (json1.equals(json2)) { - ((JSONArray) json1).clear(); - ((JSONArray) json2).clear(); + if (equalsWithArrayOrderIgnored(json1, json2)) { + json1.clear(); + json2.clear(); } else { - final Iterator iter1 = ((JSONArray) json1).iterator(); + final Iterator iter1 = json1.iterator(); while (iter1.hasNext()) { Object item = iter1.next(); - if (((JSONArray) json2).contains(item)) { + if (containsEqualWithArrayOrderIgnored(json2, item)) { // if an equal element is found in json2, remove item from json1 and the first match from json2 - ((JSONArray) json2).remove(item); + json2.remove(item); iter1.remove(); } else { // json2 does not contain any element that equals item; recursively remove anything from all elements in json2 that has a structural match in item (???) - final Iterator iter2 = ((JSONArray) json2).iterator(); + final Iterator iter2 = json2.iterator(); while (iter2.hasNext()) { // FIXME this is "comparing" item from array json1 against all elements of json2 and removing duplicate entries; is this what we want? removeDuplicateEntries(item, iter2.next()); } @@ -392,4 +436,13 @@ public class CompareServersResource extends AbstractSailingServerResource { } } } + + private boolean containsEqualWithArrayOrderIgnored(JSONArray jsonArray, Object item) { + for (final Object o : jsonArray) { + if (equalsWithArrayOrderIgnored(o, item)) { + return true; + } + } + return false; + } } diff --git a/java/com.sap.sse.shared.android/src/com/sap/sse/concurrent/ConcurrentHashBag.java b/java/com.sap.sse.shared.android/src/com/sap/sse/concurrent/ConcurrentHashBag.java index aedd9a45784..d0b5919678b 100755 --- a/java/com.sap.sse.shared.android/src/com/sap/sse/concurrent/ConcurrentHashBag.java +++ b/java/com.sap.sse.shared.android/src/com/sap/sse/concurrent/ConcurrentHashBag.java @@ -52,6 +52,16 @@ public class ConcurrentHashBag extends AbstractCollection { public java.util.Iterator iterator() { return new Iterator(); } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public boolean equals(Object o) { + return o instanceof ConcurrentHashBag && map.equals(((ConcurrentHashBag) o).map); + } @Override public int size() {