Workaround bogus TreeMap Comparators.
Try our best not to compare elements against themselves.
**** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****
bug: 26336181
Change-Id: I67e846159c1be9e9a5595ece5a32a00ff8eacccd
diff --git a/luni/src/test/java/libcore/java/util/TreeMapTest.java b/luni/src/test/java/libcore/java/util/TreeMapTest.java
index 1518e25..6864379 100644
--- a/luni/src/test/java/libcore/java/util/TreeMapTest.java
+++ b/luni/src/test/java/libcore/java/util/TreeMapTest.java
@@ -17,6 +17,7 @@
package libcore.java.util;
import java.util.AbstractMap.SimpleEntry;
+import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
@@ -434,4 +435,31 @@
}
}.test();
}
+
+ // http://b//26336181
+ //
+ // Note that this is only worth working around because these bogus comparators worked
+ // somewhat-fine on M and below provided that :
+ //
+ // (1) put was called with distinct elements (i.e, with no two elements equal() to each other)
+ // (2) get or get-like methods are never called
+ //
+ // These comparators are clearly bogus but are somewhat common.
+ public void testTreeMapWithBogusComparator() {
+ TreeMap<String, String> treeMap = new TreeMap<String, String>(
+ new Comparator<String>() {
+ @Override
+ public int compare(String o1, String o2) {
+ if (o1.equals(o2)) {
+ throw new IllegalArgumentException("Expected unequal elements");
+ }
+
+ return o1.compareTo(o2);
+ }
+ }
+ );
+
+ treeMap.put("candy", "floss");
+ treeMap.put("cheddar", "cheese");
+ }
}
diff --git a/ojluni/src/main/java/java/util/TreeMap.java b/ojluni/src/main/java/java/util/TreeMap.java
index e57c5a9..f25e5af 100755
--- a/ojluni/src/main/java/java/util/TreeMap.java
+++ b/ojluni/src/main/java/java/util/TreeMap.java
@@ -529,7 +529,31 @@
public V put(K key, V value) {
TreeMapEntry<K,V> t = root;
if (t == null) {
- compare(key, key); // type (and possibly null) check
+ // We could just call compare(key, key) for its side effect of checking the type and
+ // nullness of the input key. However, several applications seem to have written comparators
+ // that only expect to be called on elements that aren't equal to each other (after
+ // making assumptions about the domain of the map). Clearly, such comparators are bogus
+ // because get() would never work, but TreeSets are frequently used for sorting a set
+ // of distinct elements.
+ //
+ // As a temporary work around, we perform the null & instanceof checks by hand so that
+ // we can guarantee that elements are never compared against themselves.
+ //
+ // compare(key, key);
+ //
+ // **** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****
+ if (comparator != null) {
+ if (key == null) {
+ comparator.compare(key, key);
+ }
+ } else {
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ } else if (!(key instanceof Comparable)) {
+ throw new ClassCastException(
+ "Cannot cast" + key.getClass().getName() + " to Comparable.");
+ }
+ }
root = new TreeMapEntry<>(key, value, null);
size = 1;