TreeMap is now implemented.
Patch by: jat, rovrevik
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2324 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/util/AbstractMap.java b/user/super/com/google/gwt/emul/java/util/AbstractMap.java
index e85ba13..fffd70b 100644
--- a/user/super/com/google/gwt/emul/java/util/AbstractMap.java
+++ b/user/super/com/google/gwt/emul/java/util/AbstractMap.java
@@ -87,6 +87,7 @@
int hashCode = 0;
for (Entry<K, V> entry : entrySet()) {
hashCode += entry.hashCode();
+ hashCode = ~~hashCode;
}
return hashCode;
}
diff --git a/user/super/com/google/gwt/emul/java/util/TreeMap.java b/user/super/com/google/gwt/emul/java/util/TreeMap.java
index 090016c..bc6c293 100644
--- a/user/super/com/google/gwt/emul/java/util/TreeMap.java
+++ b/user/super/com/google/gwt/emul/java/util/TreeMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -15,106 +15,965 @@
*/
package java.util;
-/**
- * Sorted map implementation, guarantees log(n) complexity for containsKey, get,
- * put, and remove. <a
- * href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/TreeMap.html">[Sun
- * docs]</a>
- *
- * @param <K> key type.
- * @param <V> value type.
- */
-public class TreeMap<K, V> extends AbstractMap<K, V> implements
- SortedMap<K, V>, Cloneable {
+import java.io.Serializable;
+/**
+ * Implements a TreeMap using a red-black tree. This guarantees O(log n)
+ * performance on lookups, inserts, and deletes while maintaining linear
+ * in-order traversal time. Null keys and values are fully supported if the
+ * comparator supports them (the default comparator does not).
+ *
+ * @param <K> key type
+ * @param <V> value type
+ */
+public class TreeMap<K extends Comparable<K>, V> extends AbstractMap<K, V>
+ implements SortedMap<K, V>, Serializable {
+ /*
+ * Implementation derived from public domain C implementation as of 5
+ * September 2007 at:
+ * http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx
+ * written by Julienne Walker.
+ *
+ * This version does not require a parent pointer kept in each node.
+ */
+
+ /**
+ * An iterator for entries kept in a TreeMap.
+ */
+ private class EntryIterator implements Iterator<Entry<K, V>> {
+
+ // The most recent node returned from next(); null if it hasn't been called
+ // or has been deleted.
+ private Node<K, V> current;
+
+ // Lower, inclusive bound on returned keys.
+ private K fromKey;
+
+ /*
+ * Since we don't keep parent pointers, we maintain our position in the tree
+ * via the stack which would be used for iterative in-order traversal. The
+ * top of the stack is always the next node to visit.
+ */
+ private Stack<Node<K, V>> inorderStack;
+
+ // Upper, exclusive bound on returned keys, or null if there is none --
+ // used for iterators on submaps.
+ private K toKey;
+
+ // The type of range bounds on this iterator.
+ private SubMapType type;
+
+ // true if the iterator hasn't been initialized yet.
+ private boolean uninitialized;
+
+ /**
+ * Create a new iterator with no restrictions on the returned values.
+ */
+ public EntryIterator() {
+ this(SubMapType.All, null, null);
+ }
+
+ /**
+ * Create an iterator which may return only a restricted range.
+ *
+ * @param fromKey the first key to return in the iterator.
+ * @param toKey the upper bound of keys to return.
+ */
+ public EntryIterator(SubMapType type, K fromKey, K toKey) {
+ inorderStack = new Stack<Node<K, V>>();
+ uninitialized = true;
+ this.type = type;
+ this.fromKey = fromKey;
+ this.toKey = toKey;
+ }
+
+ public boolean hasNext() {
+ if (uninitialized) {
+ initialize();
+ }
+ if (inorderStack.isEmpty()) {
+ return false;
+ }
+ return inRange(inorderStack.peek().key);
+ }
+
+ public Entry<K, V> next() {
+ if (uninitialized) {
+ initialize();
+ }
+ /*
+ * After returning the current node, push the left children of its right
+ * child on the stack.
+ */
+ current = inorderStack.pop();
+ if (current == null || !inRange(current.key)) {
+ throw new NoSuchElementException("No more elements");
+ }
+ pushLeftChildren(current.child[RIGHT]);
+ return current;
+ }
+
+ public void remove() {
+ if (current == null) {
+ throw new IllegalStateException("No current element");
+ }
+ TreeMap.this.remove(current.getKey());
+ current = null;
+ }
+
+ private void initialize() {
+ uninitialized = false;
+ pushLeftChildren(root);
+ current = null;
+ if (type == SubMapType.Tail || type == SubMapType.Range) {
+ // TODO(jat): rewrite this similar to getAdjacentEntry()
+ while (!inorderStack.isEmpty()) {
+ current = inorderStack.pop();
+ if (inRange(current.key)) {
+ // we found the starting point, so push it back on the stack
+ inorderStack.push(current);
+ current = null;
+ break;
+ }
+ pushLeftChildren(current.child[RIGHT]);
+ }
+ }
+ }
+
+ private boolean inRange(K key) {
+ if (type == SubMapType.Head || type == SubMapType.Range) {
+ if (cmp.compare(key, toKey) >= 0) {
+ return false;
+ }
+ }
+ if (type == SubMapType.Tail || type == SubMapType.Range) {
+ if (cmp.compare(key, fromKey) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Follow the left children of the specified node, pushing them on the stack
+ * as we go.
+ *
+ * @param node parent node to follow left children from.
+ */
+ private void pushLeftChildren(Node<K, V> node) {
+ while (node != null) {
+ inorderStack.push(node);
+ node = node.child[LEFT];
+ }
+ }
+ }
+
+ private final class EntrySet extends AbstractSet<Entry<K, V>> {
+ @Override
+ public void clear() {
+ TreeMap.this.clear();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean contains(Object o) {
+ if (!(o instanceof Map.Entry)) {
+ return false;
+ }
+ Map.Entry<K, V> entry = (Entry<K, V>) o; // suppress unchecked
+ Entry<K, V> lookupEntry = getEntry(entry.getKey());
+ return lookupEntry != null
+ && Utility.equalsWithNullCheck(lookupEntry.getValue(),
+ entry.getValue());
+ }
+
+ @Override
+ public Iterator<Entry<K, V>> iterator() {
+ return new EntryIterator();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean remove(Object o) {
+ /*
+ * TODO(jat): is this safe since we can copy a predecessor's data to an
+ * interior node when it is deleted? I think so since we can only go
+ * through the iterator in ascending order, so we will have passed the one
+ * that was copied by the time we can delete a node that will make that
+ * copy.
+ */
+ if (!(o instanceof Map.Entry)) {
+ return false;
+ }
+ Map.Entry<K, V> entry = (Map.Entry<K, V>) o; // suppress unchecked
+ State<V> state = new State<V>();
+ state.matchValue = true;
+ state.value = entry.getValue();
+ return removeWithState(entry.getKey(), state);
+ }
+
+ @Override
+ public int size() {
+ return TreeMap.this.size();
+ }
+ };
+
+ /**
+ * Tree node.
+ *
+ * @param <K> key type
+ * @param <V> value type
+ */
+ private static class Node<K, V> implements Entry<K, V> {
+ /*
+ * The children are kept in an array to minimize the normal duplication of
+ * code.
+ */
+ protected Node<K, V>[] child;
+ protected boolean isRed;
+ protected K key;
+ protected V value;
+
+ /**
+ * Create a red node.
+ *
+ * @param key
+ * @param value
+ */
+ public Node(K key, V value) {
+ this(key, value, true);
+ }
+
+ /**
+ * Create a node of the specified color.
+ *
+ * @param key
+ * @param value
+ * @param isRed true if this should be a red node, false for black
+ */
+ @SuppressWarnings("unchecked")
+ // create array of generic elements
+ public Node(K key, V value, boolean isRed) {
+ this.key = key;
+ this.value = value;
+ child = new Node[2]; // suppress unchecked
+ this.isRed = isRed;
+ }
+
+ @SuppressWarnings("unchecked")
+ // generic cast
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Node)) {
+ return false;
+ }
+ Node<K, V> other = (Node<K, V>) o; // suppress unchecked
+ return Utility.equalsWithNullCheck(key, other.key)
+ && Utility.equalsWithNullCheck(value, other.value);
+ }
+
+ public K getKey() {
+ return key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public int hashCode() {
+ int keyHash = (key != null ? key.hashCode() : 0);
+ int valueHash = (value != null ? value.hashCode() : 0);
+ return keyHash ^ valueHash;
+ }
+
+ public V setValue(V value) {
+ V old = this.value;
+ this.value = value;
+ return old;
+ }
+
+ @Override
+ public String toString() {
+ return (isRed ? "R: " : "B: ") + key + "=" + value;
+ }
+ }
+
+ /**
+ * A state object which is passed down the tree for both insert and remove.
+ * All uses make use of the done flag to indicate when no further rebalancing
+ * of the tree is required. Remove methods use the found flag to indicate when
+ * the desired key has been found. value is used both to return the value of a
+ * removed node as well as to pass in a value which must match (used for
+ * entrySet().remove(entry)), and the matchValue flag is used to request this
+ * behavior.
+ *
+ * @param <V> value type
+ */
+ private static class State<V> {
+ public boolean done;
+ public boolean found;
+ public boolean matchValue;
+ public V value;
+
+ @Override
+ public String toString() {
+ return "State: mv=" + matchValue + " value=" + value + " done=" + done
+ + " found=" + found;
+ }
+ }
+
+ private class SubMap extends AbstractMap<K, V> implements SortedMap<K, V> {
+
+ // valid only if type is Range or Tail
+ public final K fromKey;
+
+ // valid only if type is Range or Head
+ public final K toKey;
+
+ public final SubMapType type;
+
+ SubMap(SubMapType type, K fromKey, K toKey) {
+ switch (type) {
+ case Range:
+ if (cmp.compare(toKey, fromKey) < 0) {
+ throw new IllegalArgumentException("subMap: " + toKey
+ + " less than " + fromKey);
+ }
+ break;
+ case Head:
+ // check key for compatibility with comparator
+ cmp.compare(toKey, toKey);
+ break;
+ case Tail:
+ // check key for compatibility with comparator
+ cmp.compare(fromKey, fromKey);
+ break;
+ }
+ this.type = type;
+ this.fromKey = fromKey;
+ this.toKey = toKey;
+ }
+
+ public Comparator<? super K> comparator() {
+ return TreeMap.this.comparator();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean containsKey(Object k) {
+ K key = (K) k; // suppress unchecked
+ if (!inRange(key)) {
+ return false;
+ }
+ return TreeMap.this.containsKey(k);
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<K, V>> entrySet() {
+ return new AbstractSet<Entry<K, V>>() {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean contains(Object o) {
+ if (!(o instanceof Map.Entry)) {
+ return false;
+ }
+ Map.Entry<K, V> entry = (Entry<K, V>) o; // suppress unchecked
+ K key = entry.getKey();
+ if (!inRange(key)) {
+ return false;
+ }
+ Entry<K, V> lookupEntry = getEntry(key);
+ return lookupEntry != null
+ && Utility.equalsWithNullCheck(lookupEntry.getValue(),
+ entry.getValue());
+ }
+
+ @Override
+ public Iterator<Entry<K, V>> iterator() {
+ return new EntryIterator(type, fromKey, toKey);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean remove(Object o) {
+ if (!(o instanceof Map.Entry)) {
+ return false;
+ }
+ Map.Entry<K, V> entry = (Map.Entry<K, V>) o; // suppress unchecked
+ if (!inRange(entry.getKey())) {
+ return false;
+ }
+ State<V> state = new State<V>();
+ state.matchValue = true;
+ state.value = entry.getValue();
+ return removeWithState(entry.getKey(), state);
+ }
+
+ @Override
+ public int size() {
+ // TODO(jat): more efficient way to do this?
+ int n = 0;
+ Iterator<Entry<K, V>> it = iterator();
+ while (it.hasNext()) {
+ it.next();
+ n++;
+ }
+ return n;
+ }
+ };
+ }
+
+ public K firstKey() {
+ Node<K, V> node = getNodeAtOrAfter(fromKey);
+ if (node == null || cmp.compare(node.key, toKey) > 0) {
+ throw new NoSuchElementException();
+ }
+ return node.key;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public V get(Object k) {
+ K key = (K) k; // suppress unchecked
+ if (!inRange(key)) {
+ return null;
+ }
+ return TreeMap.this.get(key);
+ }
+
+ public SortedMap<K, V> headMap(K toKey) {
+ if (cmp.compare(toKey, this.toKey) > 0) {
+ throw new IllegalArgumentException("subMap: " + toKey
+ + " greater than " + this.toKey);
+ }
+ if (type == SubMapType.Range || type == SubMapType.Tail) {
+ return TreeMap.this.subMap(fromKey, toKey);
+ } else {
+ return TreeMap.this.headMap(toKey);
+ }
+ }
+
+ public K lastKey() {
+ Node<K, V> node = getNodeBefore(toKey);
+ if (node == null || cmp.compare(node.key, fromKey) < 0) {
+ throw new NoSuchElementException();
+ }
+ return node.key;
+ }
+
+ @Override
+ public V put(K key, V value) {
+ if (!inRange(key)) {
+ throw new IllegalArgumentException(key + " outside the range "
+ + fromKey + " to " + toKey);
+ }
+ return TreeMap.this.put(key, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public V remove(Object k) {
+ K key = (K) k; // suppress unchecked
+ if (!inRange(key)) {
+ return null;
+ }
+ return TreeMap.this.remove(key);
+ }
+
+ public SortedMap<K, V> subMap(K newFromKey, K newToKey) {
+ if ((type == SubMapType.Range || type == SubMapType.Tail)
+ && cmp.compare(newFromKey, fromKey) < 0) {
+ throw new IllegalArgumentException("subMap: " + newFromKey
+ + " less than " + fromKey);
+ }
+ if ((type == SubMapType.Range || type == SubMapType.Head)
+ && cmp.compare(newToKey, toKey) > 0) {
+ throw new IllegalArgumentException("subMap: " + newToKey
+ + " greater than " + toKey);
+ }
+ return TreeMap.this.subMap(newFromKey, newToKey);
+ }
+
+ public SortedMap<K, V> tailMap(K fromKey) {
+ if ((type == SubMapType.Range || type == SubMapType.Tail)
+ && cmp.compare(fromKey, this.fromKey) < 0) {
+ throw new IllegalArgumentException("subMap: " + fromKey + " less than "
+ + this.fromKey);
+ }
+ if (type == SubMapType.Range || type == SubMapType.Head) {
+ return TreeMap.this.subMap(fromKey, toKey);
+ } else {
+ return TreeMap.this.tailMap(fromKey);
+ }
+ }
+
+ private boolean inRange(K key) {
+ if (type == SubMapType.Head || type == SubMapType.Range) {
+ if (cmp.compare(key, toKey) >= 0) {
+ return false;
+ }
+ }
+ if (type == SubMapType.Tail || type == SubMapType.Range) {
+ if (cmp.compare(key, fromKey) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private enum SubMapType {
+ All, Head, Range, Tail,
+ }
+
+ /**
+ * Default comparator, requires the key type implement Comparable. Will fail
+ * on null values.
+ */
+ @SuppressWarnings("unchecked")
+ private static Comparator<?> DEFAULT_COMPARATOR = new Comparator<Comparable>() {
+ public int compare(Comparable a, Comparable b) {
+ // Explicit null check to match JRE specs
+ if (a == null || b == null) {
+ throw new NullPointerException();
+ }
+ return a.compareTo(b);
+ }
+ };
+
+ private static final int LEFT = 0;
+ private static final int RIGHT = 1;
+
+ private static int otherChild(int child) {
+ assert (child == 0 || child == 1);
+ return 1 - child;
+ }
+
+ // The comparator to use.
+ private Comparator<? super K> cmp;
+
+ // The root of the tree.
+ private Node<K, V> root;
+
+ // The number of nodes in the tree.
+ private int size = 0;
+
+ @SuppressWarnings("unchecked")
public TreeMap() {
- this(Comparators.natural());
+ this((Comparator<? super K>) DEFAULT_COMPARATOR);
}
public TreeMap(Comparator<? super K> c) {
- // TODO(jat): implement
- throw new UnsupportedOperationException("TreeMap not implemented");
+ root = null;
+ cmp = c;
}
- public TreeMap(Map<? extends K, ? extends V> m) {
+ public TreeMap(Map<? extends K, ? extends V> map) {
this();
- putAll(m);
+ putAll(map);
}
- public TreeMap(SortedMap<? extends K, ? extends V> m) {
- // TODO(jat): optimize
- this((Map<? extends K, ? extends V>) m);
+ @SuppressWarnings("unchecked")
+ public TreeMap(SortedMap<K, ? extends V> map) {
+ cmp = map.comparator();
+ if (cmp == null) {
+ cmp = (Comparator<? super K>) DEFAULT_COMPARATOR;
+ }
+ putAll(map); // TODO(jat): more efficient init from sorted map
}
@Override
public void clear() {
- // TODO(jat): implement
+ root = null;
+ size = 0;
}
public Comparator<? super K> comparator() {
- // TODO(jat): implement
- return null;
+ if (cmp == DEFAULT_COMPARATOR) {
+ return null;
+ }
+ return cmp;
}
+ @SuppressWarnings("unchecked")
@Override
public boolean containsKey(Object key) {
- // TODO(jat): implement
- return false;
+ return getEntry((K) key) != null; // suppress unchecked cast
}
@Override
- public Set<Map.Entry<K, V>> entrySet() {
- // TODO(jat): implement
- return null;
+ public Set<Entry<K, V>> entrySet() {
+ return new EntrySet();
}
public K firstKey() {
- // TODO(jat): implement
- return null;
+ if (root == null) {
+ throw new NoSuchElementException();
+ }
+ Node<K, V> node = root;
+ while (node.child[LEFT] != null) {
+ node = node.child[LEFT];
+ }
+ return node.key;
}
+ @SuppressWarnings("unchecked")
@Override
- public V get(Object key) {
- // TODO(jat): implement
- return null;
+ public V get(Object k) {
+ K key = (K) k; // suppress unchecked
+
+ /*
+ * Don't bother validating the key as getEntry does that internally if the
+ * map is non-empty. This is against the spec but matches JRE 1.5 behavior.
+ */
+
+ Node<K, V> entry = getEntry(key);
+ return entry != null ? entry.getValue() : null;
}
public SortedMap<K, V> headMap(K toKey) {
- // TODO(jat): implement
- return null;
+ return new SubMap(SubMapType.Head, null, toKey);
}
public K lastKey() {
- // TODO(jat): implement
- return null;
+ if (root == null) {
+ throw new NoSuchElementException();
+ }
+ Node<K, V> node = root;
+ while (node.child[RIGHT] != null) {
+ node = node.child[RIGHT];
+ }
+ return node.key;
}
@Override
public V put(K key, V value) {
- // TODO(jat): implement
- return null;
+ Node<K, V> node = new Node<K, V>(key, value);
+ State<V> state = new State<V>();
+ root = insert(root, node, state);
+ if (!state.found) {
+ ++size;
+ }
+ root.isRed = false;
+ return state.value;
}
- @Override
- public V remove(Object key) {
- // TODO(jat): implement
- return null;
+ @SuppressWarnings("unchecked")
+ public V remove(Object keyObj) {
+ K key = (K) keyObj; // suppress unchecked cast
+ State<V> state = new State<V>();
+ removeWithState(key, state);
+ return state.value;
}
@Override
public int size() {
- // TODO(jat): implement
- return 0;
+ return size;
}
- public SortedMap<K, V> subMap(K fromKey, K toKey) {
- // TODO(jat): implement
- return null;
+ public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
+ return new SubMap(SubMapType.Range, fromKey, toKey);
}
public SortedMap<K, V> tailMap(K fromKey) {
- // TODO(jat): implement
+ return new SubMap(SubMapType.Tail, fromKey, null);
+ }
+
+ /**
+ * Return the first node which compares equal to or greater than the given
+ * key.
+ *
+ * @param key the key to search for
+ * @return the next node, or null if there is none.
+ */
+ protected Node<K, V> getNodeAtOrAfter(K key) {
+ if (root == null) {
+ return null;
+ }
+ Node<K, V> foundNode = null;
+ Node<K, V> node = root;
+ while (true) {
+ int c = cmp.compare(key, node.key);
+ if (c == 0) {
+ return node;
+ } else if (c > 0) {
+ node = node.child[RIGHT];
+ if (node == null) {
+ // ran off the tree and we are past this node, so return best one
+ return foundNode;
+ }
+ } else {
+ foundNode = node;
+ Node<K, V> nextNode = node.child[LEFT];
+ if (nextNode == null) {
+ // ran off the tree and we are before this node, so return it
+ return node;
+ }
+ node = nextNode;
+ }
+ }
+ }
+
+ /**
+ * Return the last node which is strictly less than the given key.
+ *
+ * @param key the key to search for
+ * @return the previous node, or null if there is none.
+ */
+ protected Node<K, V> getNodeBefore(K key) {
+ if (root == null) {
+ return null;
+ }
+ Node<K, V> foundNode = null;
+ Node<K, V> node = root;
+ while (true) {
+ int c = cmp.compare(key, node.key);
+ if (c <= 0) {
+ node = node.child[LEFT];
+ if (node == null) {
+ // ran off the tree and we are past this node, so return best one
+ return foundNode;
+ }
+ } else {
+ foundNode = node;
+ Node<K, V> nextNode = node.child[RIGHT];
+ if (nextNode == null) {
+ // ran off the tree and we are before this node, so return it
+ return node;
+ }
+ node = nextNode;
+ }
+ }
+ }
+
+ /**
+ * Used for testing. Validate that the tree meets all red-black correctness
+ * requirements. These include:
+ *
+ * <pre>
+ * - root is black
+ * - no children of a red node may be red
+ * - the black height of every path through the three to a leaf is exactly the same
+ * </pre>
+ *
+ * @throws RuntimeException if any correctness errors are detected.
+ */
+ void assertCorrectness() {
+ assertCorrectness(root, true);
+ }
+
+ /**
+ * Internal helper function for public (@see assertCorrectness()).
+ *
+ * @param tree the subtree to validate.
+ * @param isRed true if the parent of this node is red.
+ * @return the black height of this subtree.
+ * @throws RuntimeException if this RB-tree is not valid.
+ */
+ private int assertCorrectness(Node<K, V> tree, boolean isRed) {
+ if (tree == null) {
+ return 0;
+ }
+ if (isRed && tree.isRed) {
+ throw new RuntimeException("Two red nodes adjacent");
+ }
+
+ if (tree.child[LEFT] != null
+ && cmp.compare(tree.child[LEFT].key, tree.key) > 0) {
+ throw new RuntimeException("Left child " + tree.child[LEFT]
+ + " larger than " + tree);
+ }
+ if (tree.child[RIGHT] != null
+ && cmp.compare(tree.child[RIGHT].key, tree.key) < 0) {
+ throw new RuntimeException("Right child " + tree.child[RIGHT]
+ + " smaller than " + tree);
+ }
+
+ int leftHeight = assertCorrectness(tree.child[LEFT], tree.isRed);
+ int rightHeight = assertCorrectness(tree.child[RIGHT], tree.isRed);
+ if (leftHeight != 0 && rightHeight != 0 && leftHeight != rightHeight) {
+ throw new RuntimeException("Black heights don't match");
+ }
+ return tree.isRed ? leftHeight : leftHeight + 1;
+ }
+
+ /**
+ * Finds an entry given a key and returns the node.
+ *
+ * @param key the search key
+ * @return the node matching the key or null
+ */
+ private Node<K, V> getEntry(K key) {
+ Node<K, V> tree = root;
+ while (tree != null) {
+ int c = cmp.compare(key, tree.key);
+ if (c == 0) {
+ return tree;
+ }
+ if (c < 0) {
+ tree = tree.child[LEFT];
+ } else {
+ tree = tree.child[RIGHT];
+ }
+ }
return null;
}
+ private Node<K, V> insert(Node<K, V> tree, Node<K, V> newNode, State<V> state) {
+ if (tree == null) {
+ return newNode;
+ } else {
+ int c = cmp.compare(tree.key, newNode.key);
+ if (c == 0) {
+ state.value = tree.value;
+ state.found = true;
+ tree.value = newNode.value;
+ return tree;
+ }
+ int childNum = (c > 0) ? 0 : 1;
+ tree.child[childNum] = insert(tree.child[childNum], newNode, state);
+ if (isRed(tree.child[childNum])) {
+ if (isRed(tree.child[otherChild(childNum)])) {
+ // both children are red (nulls are black), make both black and me red
+ tree.isRed = true;
+ tree.child[LEFT].isRed = false;
+ tree.child[RIGHT].isRed = false;
+ } else {
+ //
+ if (isRed(tree.child[childNum].child[childNum])) {
+ tree = rotateSingle(tree, otherChild(childNum));
+ } else if (isRed(tree.child[childNum].child[otherChild(childNum)])) {
+ tree = rotateDouble(tree, otherChild(childNum));
+ }
+ }
+ }
+ }
+ return tree;
+ }
+
+ /**
+ * Return true if <code>node</code> is red. Note that null pointers are
+ * considered black.
+ */
+ private boolean isRed(Node<K, V> node) {
+ return node != null && node.isRed;
+ }
+
+ private boolean removeWithState(K key, State<V> state) {
+ if (root == null) {
+ return false;
+ }
+ Node<K, V> node;
+ Node<K, V> found = null;
+ Node<K, V> parent = null;
+ Node<K, V> grandparent = null;
+
+ // create a fake tree root to minimize special cases for changing the root
+ Node<K, V> head = new Node<K, V>(null, null);
+ int dir = 1;
+ head.child[RIGHT] = root;
+
+ node = head;
+ while (node.child[dir] != null) {
+ int last = dir;
+ grandparent = parent;
+ parent = node;
+ node = node.child[dir];
+ int c = cmp.compare(node.key, key);
+ dir = c < 0 ? RIGHT : LEFT;
+ if (c == 0 && (!state.matchValue || node.value.equals(state.value))) {
+ found = node;
+ }
+ if (!isRed(node) && !isRed(node.child[dir])) {
+ if (isRed(node.child[otherChild(dir)])) {
+ parent = parent.child[last] = rotateSingle(node, dir);
+ } else if (!isRed(node.child[otherChild(dir)])) {
+ Node<K, V> sibling = parent.child[otherChild(last)];
+ if (sibling != null) {
+ if (!isRed(sibling.child[otherChild(last)]) && !isRed(sibling.child[last])) {
+ parent.isRed = false;
+ sibling.isRed = true;
+ node.isRed = true;
+ } else {
+ int dir2 = grandparent.child[RIGHT] == parent ? RIGHT : LEFT;
+ if (isRed(sibling.child[last])) {
+ grandparent.child[dir2] = rotateDouble(parent, last);
+ } else if (isRed(sibling.child[otherChild(last)])) {
+ grandparent.child[dir2] = rotateSingle(parent, last);
+ }
+ node.isRed = grandparent.child[dir2].isRed = true;
+ grandparent.child[dir2].child[LEFT].isRed = false;
+ grandparent.child[dir2].child[RIGHT].isRed = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (found != null) {
+ if (state != null) {
+ state.found = true;
+ state.value = found.value;
+ }
+ found.key = node.key;
+ found.value = node.value;
+ parent.child[parent.child[RIGHT] == node ? RIGHT : LEFT] = node.child[node.child[LEFT] == null
+ ? RIGHT : LEFT];
+ size--;
+ }
+
+ root = head.child[RIGHT];
+ if (root != null) {
+ root.isRed = false;
+ }
+ return state.found;
+ }
+
+ /**
+ * Perform a double rotation, first rotating the child which will become the
+ * root in the opposite direction, then rotating the root in the specified
+ * direction.
+ *
+ * <pre>
+ * A F
+ * B C becomes (with rotateDirection=0) A C
+ * D E F G B E G
+ * D
+ * </pre>
+ *
+ * @param tree root of the subtree to rotate
+ * @param rotateDirection the direction to rotate: 0=left, 1=right
+ * @return the new root of the rotated subtree
+ */
+ private Node<K, V> rotateDouble(Node<K, V> tree, int rotateDirection) {
+ // free the pointer of the new root
+ tree.child[otherChild(rotateDirection)] = rotateSingle(
+ tree.child[otherChild(rotateDirection)], otherChild(rotateDirection));
+ return rotateSingle(tree, rotateDirection);
+ }
+
+ /**
+ * Perform a single rotation, pushing the root of the subtree to the specified
+ * direction.
+ *
+ * <pre>
+ * A B
+ * B C becomes (with rotateDirection=1) D A
+ * D E E C
+ * </pre>
+ *
+ * @param tree the root of the subtree to rotate
+ * @param rotateDirection the direction to rotate: 0=left rotation, 1=right
+ * @return the new root of the rotated subtree
+ */
+ private Node<K, V> rotateSingle(Node<K, V> tree, int rotateDirection) {
+ Node<K, V> save = tree.child[otherChild(rotateDirection)];
+ tree.child[otherChild(rotateDirection)] = save.child[rotateDirection];
+ save.child[rotateDirection] = tree;
+ tree.isRed = true;
+ save.isRed = false;
+ return save;
+ }
}
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java
index 002d560..462edbb 100644
--- a/user/test/com/google/gwt/emultest/EmulSuite.java
+++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -80,6 +80,7 @@
suite.addTestSuite(HashSetTest.class);
suite.addTestSuite(IdentityHashMapTest.class);
suite.addTestSuite(StackTest.class);
+ suite.addTest(TreeMapSuite.suite());
// $JUnit-END$
return suite;
diff --git a/user/test/com/google/gwt/emultest/TreeMapSuite.java b/user/test/com/google/gwt/emultest/TreeMapSuite.java
new file mode 100644
index 0000000..ca4ff4e
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/TreeMapSuite.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest;
+
+import com.google.gwt.emultest.java.util.TreeMapIntegerDoubleTest;
+import com.google.gwt.emultest.java.util.TreeMapIntegerDoubleWithComparatorTest;
+import com.google.gwt.emultest.java.util.TreeMapStringStringTest;
+import com.google.gwt.emultest.java.util.TreeMapStringStringWithComparatorTest;
+import com.google.gwt.junit.tools.GWTTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * Tests <code>TreeMap</code>.
+ */
+public class TreeMapSuite {
+ public static Test suite() {
+ GWTTestSuite suite = new GWTTestSuite("Tests for com.google.gwt.emul.java.util.TreeMap");
+ suite.addTestSuite(TreeMapStringStringTest.class);
+ suite.addTestSuite(TreeMapStringStringWithComparatorTest.class);
+ suite.addTestSuite(TreeMapIntegerDoubleTest.class);
+ suite.addTestSuite(TreeMapIntegerDoubleWithComparatorTest.class);
+ return suite;
+ }
+}
diff --git a/user/test/com/google/gwt/emultest/java/util/TreeMapIntegerDoubleTest.java b/user/test/com/google/gwt/emultest/java/util/TreeMapIntegerDoubleTest.java
new file mode 100644
index 0000000..9dc4909
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/TreeMapIntegerDoubleTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java.util;
+
+/**
+ * Tests <code>TreeMap</code> with a <code>Comparator</code>.
+ */
+public class TreeMapIntegerDoubleTest extends TreeMapTest<Integer, Double> {
+
+ @Override
+ Integer getGreaterThanMaximumKey() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ Integer[] getKeys() {
+ return new Integer[] {1, 2, 3, 4};
+ }
+
+ @Override
+ Integer[] getKeys2() {
+ return new Integer[] {5, 6, 7, 8};
+ }
+
+ @Override
+ Integer getLessThanMinimumKey() {
+ return Integer.MIN_VALUE;
+ }
+
+ @Override
+ Double[] getValues() {
+ return new Double[] {0.1, 0.2, 0.3, 0.4};
+ }
+
+ @Override
+ Double[] getValues2() {
+ return new Double[] {1.1, 1.2, 1.3, 1.4};
+ }
+
+ @Override
+ protected Object getConflictingKey() {
+ return "key";
+ }
+
+ @Override
+ protected Object getConflictingValue() {
+ return "value";
+ }
+
+ @Override
+ protected Object[] getOtherKeys() {
+ return getKeys2();
+ }
+
+ @Override
+ protected Object[] getOtherValues() {
+ return getValues2();
+ }
+
+ @Override
+ protected Object[] getSampleKeys() {
+ return getKeys();
+ }
+
+ @Override
+ protected Object[] getSampleValues() {
+ return getValues();
+ }
+
+ @Override
+ protected Object[] getNewSampleValues() {
+ return getValues2();
+ }
+}
diff --git a/user/test/com/google/gwt/emultest/java/util/TreeMapIntegerDoubleWithComparatorTest.java b/user/test/com/google/gwt/emultest/java/util/TreeMapIntegerDoubleWithComparatorTest.java
new file mode 100644
index 0000000..83788f7
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/TreeMapIntegerDoubleWithComparatorTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java.util;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.SortedMap;
+
+/**
+ * Tests <code>TreeMap</code> with a <code>Comparator</code>.
+ */
+public class TreeMapIntegerDoubleWithComparatorTest extends
+ TreeMapIntegerDoubleTest {
+ @Override
+ protected SortedMap<Integer, Double> createSortedMap() {
+ setComparator(new Comparator<Integer>() {
+ public int compare(Integer o1, Integer o2) {
+ if (o1 == null) {
+ return o2 == null ? 0 : -1;
+ }
+ if (o2 == null) {
+ return 1;
+ }
+ return o1.compareTo(o2);
+ }
+ });
+ return super.createSortedMap();
+ }
+
+ @Override
+ protected Map<Integer, Double> makeEmptyMap() {
+ return createSortedMap();
+ }
+
+ @Override
+ public boolean useNullKey() {
+ return true;
+ }
+}
diff --git a/user/test/com/google/gwt/emultest/java/util/TreeMapStringStringTest.java b/user/test/com/google/gwt/emultest/java/util/TreeMapStringStringTest.java
new file mode 100644
index 0000000..55a49fc
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/TreeMapStringStringTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java.util;
+
+/**
+ * Tests <code>TreeMap</code> with Strings and the natural comparator.
+ */
+public class TreeMapStringStringTest extends TreeMapTest<String, String> {
+
+ @Override
+ String getGreaterThanMaximumKey() {
+ return "z";
+ }
+
+ @Override
+ String[] getKeys() {
+ return convertToStringArray(getSampleKeys());
+ }
+
+ private String[] convertToStringArray(Object[] objArray) {
+ String[] strArray = new String[objArray.length];
+ System.arraycopy(objArray, 0, strArray, 0, objArray.length);
+ return strArray;
+ }
+
+ @Override
+ String[] getKeys2() {
+ return convertToStringArray(getOtherKeys());
+ }
+
+ @Override
+ String getLessThanMinimumKey() {
+ return "a";
+ }
+
+ @Override
+ String[] getValues() {
+ return convertToStringArray(getSampleValues());
+ }
+
+ @Override
+ String[] getValues2() {
+ return convertToStringArray(getOtherValues());
+ }
+
+ @Override
+ protected Object getConflictingKey() {
+ return new Integer(1);
+ }
+
+ @Override
+ protected Object getConflictingValue() {
+ return new Long(42);
+ }
+}
diff --git a/user/test/com/google/gwt/emultest/java/util/TreeMapStringStringWithComparatorTest.java b/user/test/com/google/gwt/emultest/java/util/TreeMapStringStringWithComparatorTest.java
new file mode 100644
index 0000000..51101e0
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/TreeMapStringStringWithComparatorTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java.util;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.SortedMap;
+
+/**
+ * Tests <code>TreeMap</code> with a <code>Comparator</code>.
+ */
+public class TreeMapStringStringWithComparatorTest extends
+ TreeMapStringStringTest {
+ @Override
+ protected SortedMap<String, String> createSortedMap() {
+ setComparator(new Comparator<String>() {
+ public int compare(String o1, String o2) {
+ if (o1 == null) {
+ return o2 == null ? 0 : -1;
+ }
+ if (o2 == null) {
+ return 1;
+ }
+ return o1.compareTo(o2);
+ }
+ });
+ return super.createSortedMap();
+ }
+
+ @Override
+ protected Map<String, String> makeEmptyMap() {
+ return createSortedMap();
+ }
+
+ @Override
+ public boolean useNullKey() {
+ return true;
+ }
+}
diff --git a/user/test/com/google/gwt/emultest/java/util/TreeMapTest.java b/user/test/com/google/gwt/emultest/java/util/TreeMapTest.java
new file mode 100644
index 0000000..7e2d760
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/TreeMapTest.java
@@ -0,0 +1,1861 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java.util;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptException;
+
+import org.apache.commons.collections.TestMap;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+/**
+ * Tests <code>TreeMap</code>.
+ *
+ * @param <K> The key type for the underlying TreeMap
+ * @param <V> The value type for the underlying TreeMap
+ *
+ * TODO(jat): this whole structure needs work. Ideally we would port a new
+ * Apache collections test to GWT, but that is not an insignificant amount of
+ * work.
+ */
+public abstract class TreeMapTest<K extends Comparable<K>, V> extends TestMap {
+ /**
+ * Verify a Collection is explicitly and implicitly empty.
+ *
+ * @param collection
+ */
+ @SuppressWarnings("unchecked")
+ private static void _assertEmpty(Collection collection) {
+ assertNotNull(collection);
+ assertTrue(collection.isEmpty());
+ assertEquals(0, collection.size());
+ assertNotNull(collection.iterator());
+ assertFalse(collection.iterator().hasNext());
+ }
+
+ /**
+ * Verify a Map is explicitly and implicitly empty.
+ *
+ * @param map
+ */
+ private static <K, V> void _assertEmpty(Map<K, V> map) {
+ assertNotNull(map);
+ assertTrue(map.isEmpty());
+ assertEquals(0, map.size());
+
+ _assertEmpty(map.values());
+ _assertEmpty(map.keySet());
+ _assertEmpty(map.entrySet());
+ }
+
+ /**
+ * Verify that two Collections are deeply equivalent. Some of the Sets that
+ * need to be verified do not implement a sensible equals method
+ * (TreeMap.values for example).
+ *
+ * @param expected
+ * @param actual
+ */
+ private static <T> void _assertEquals(Collection<T> expected,
+ Collection<T> actual) {
+ // verify equivalence using collection interface
+ assertEquals(expected.isEmpty(), actual.isEmpty());
+ assertEquals(expected.size(), actual.size());
+ assertTrue(expected.containsAll(actual));
+ assertTrue(actual.containsAll(expected));
+ for (T expectedValue : expected) {
+ assertTrue(actual.contains(expectedValue));
+ }
+ for (T actualValue : actual) {
+ assertTrue(expected.contains(actualValue));
+ }
+ }
+
+ /**
+ * Verify that two Maps are deeply equivalent.
+ *
+ * @param expected
+ * @param actual
+ */
+ private static <K, V> void _assertEquals(Map<K, V> expected, Map<K, V> actual) {
+ assertEquals(expected.isEmpty(), actual.isEmpty());
+ assertEquals(expected.size(), actual.size());
+
+ _assertEquals(expected.keySet(), actual.keySet());
+ _assertEquals(expected.entrySet(), actual.entrySet());
+
+ // One might think that the following would true:
+ // assertEquals(expected.values(), actual.values());
+ // The following verifies what i would perceive as a bug in the jre
+ // (rlo). The implementation of the Collection returned by the values method
+ // does not implement equals sensibly.
+ assertFalse(expected.values().equals(actual.values()));
+ _assertEquals(expected.values(), actual.values());
+ }
+
+ /**
+ * Verify that two SortedMaps are deeply equivalent.
+ *
+ * @param expected
+ * @param actual
+ */
+ private static <K, V> void _assertEquals(SortedMap<K, V> expected,
+ SortedMap<K, V> actual) {
+ _assertEquals((Map<K, V>) expected, (Map<K, V>) actual);
+
+ // verify the order of the associated collections
+ assertEquals(expected.keySet().toArray(), actual.keySet().toArray());
+ assertEquals(expected.entrySet().toArray(), actual.entrySet().toArray());
+ assertEquals(expected.values().toArray(), actual.values().toArray());
+ }
+
+ /**
+ * Create the expected return of toString for a Map containing only the passed
+ * key and value.
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ private static <K, V> String makeEntryString(K key, V value) {
+ return "{" + key + "=" + value + "}";
+ }
+
+ /**
+ * comparator used when creating the SortedMap.
+ */
+ private Comparator<K> comparator = null;
+ private boolean isClearSupported = true;
+ private boolean isNullKeySupported = true;
+ private boolean isNullValueSupported = true;
+ private boolean isPutAllSupported = true;
+ private boolean isPutSupported = true;
+ private boolean isRemoveSupported = true;
+
+ public String getModuleName() {
+ return "com.google.gwt.emultest.EmulSuite";
+ }
+
+ /**
+ * Test method for 'java.util.Map.clear()'.
+ *
+ * @see java.util.Map#clear()
+ */
+ public void testClear() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isClearSupported) {
+ // Execute this test only if supported.
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ assertFalse(map.isEmpty());
+ map.clear();
+ _assertEmpty(map);
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.clear()'.
+ *
+ * @see java.util.Map#clear()
+ */
+ public void testClear_throwsUnsupportedOperationException() {
+ Map<K, V> map = createMap();
+ if (!isClearSupported) {
+ try {
+ map.clear();
+ fail("expected exception");
+ } catch (UnsupportedOperationException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.lang.Object.clone()'.
+ */
+ public void testClone() {
+ // Map<K, V> map = createMap();
+ // Check empty clone behavior
+ // TODO (rlo) having .clone() in the code kills the test
+ // SortedMap<K, V> clone = (SortedMap<K, V>)
+ // map.clone();
+ // assertNotNull(clone);
+ // testEquivalent(map, clone);
+ //
+ // // Check non-empty clone behavior
+ // map.put(KEY_1, getValues()[0]);
+ // map.put(KEY_2, getValues()[1]);
+ // map.put(KEY_3, getValues()[2]);
+ // clone = (SortedMap<K, V>) map.clone();
+ // assertNotNull(clone);
+ // testEquivalent(map, clone);
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.comparator()'.
+ *
+ * @see java.util.SortedMap#comparator()
+ */
+ public void testComparator() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ if (isNaturalOrder()) {
+ assertEquals(null, sortedMap.comparator());
+ } else {
+ assertEquals(getComparator(), sortedMap.comparator());
+ }
+ }
+
+ /**
+ * Test method for default constructor.
+ *
+ * @see java.util.TreeMap#TreeMap()
+ */
+ public void testConstructor() {
+ TreeMap<K, V> treeMap = new TreeMap<K, V>();
+ _assertEmpty(treeMap);
+ }
+
+ /**
+ * Test method for 'java.util.TreeMap.TreeMap(Comparator)'.
+ *
+ * @see java.util.TreeMap#TreeMap(Comparator)
+ */
+ public void testConstructor_comparator() {
+ TreeMap<K, V> treeMap = new TreeMap<K, V>(getComparator());
+ _assertEmpty(treeMap);
+ if (isNaturalOrder()) {
+ assertNull(treeMap.comparator());
+ } else {
+ assertSame(getComparator(), treeMap.comparator());
+ }
+ }
+
+ /**
+ * Test method for 'java.util.TreeMap.TreeMap(Map)'.
+ *
+ * @see java.util.TreeMap#TreeMap(Map)
+ */
+ public void testConstructor_Map() {
+ // The source map should be just a Map. Not a sorted map.
+ Map<K, V> sourceMap = new HashMap<K, V>();
+
+ // populate the source map
+ sourceMap.put(getKeys()[0], getValues()[0]);
+ sourceMap.put(getKeys()[1], getValues()[1]);
+ sourceMap.put(getKeys()[2], getValues()[2]);
+
+ TreeMap<K, V> copyConstructed = new TreeMap<K, V>(sourceMap);
+ _assertEquals(sourceMap, copyConstructed);
+ }
+
+ /**
+ * Test method for 'java.util.TreeMap.TreeMap(Map)'.
+ *
+ * @see java.util.TreeMap#TreeMap(Map)
+ */
+ @SuppressWarnings("unchecked")
+ public void testConstructor_Map_throwsClassCastException() {
+ Map sourceMap = createMap();
+ sourceMap.put(getConflictingKey(), getConflictingValue());
+ new TreeMap<K, V>(sourceMap);
+
+ // This does not fail as might be expected.
+ // TODO I don't know of any case where this could happen.
+ // try {
+ // new TreeMap<K, V>(sourceMap);
+ // fail("expected exception");
+ // } catch (ClassCastException e) {
+ // // expected outcome
+ // }
+ }
+
+ /**
+ * Test method for 'java.util.TreeMap.TreeMap(Map)'.
+ *
+ * @see java.util.TreeMap#TreeMap(Map)
+ */
+ public void testConstructor_Map_throwsNullPointerException() {
+ try {
+ new TreeMap<K, V>((Map<K, V>) null);
+ fail("expected exception");
+ } catch (NullPointerException e) {
+ // expected outcome
+ } catch (JavaScriptException e) {
+ // in web mode we don't actually do null checks, so we get a JS exception
+ }
+ }
+
+ /**
+ * Test method for 'java.util.TreeMap.TreeMap(SortedMap)'.
+ *
+ * @see java.util.TreeMap#TreeMap(SortedMap)
+ */
+ public void testConstructor_SortedMap() {
+ SortedMap<K, V> sourceMap = new TreeMap<K, V>();
+ _assertEmpty(sourceMap);
+
+ // populate the source map
+ sourceMap.put(getKeys()[0], getValues()[0]);
+ sourceMap.put(getKeys()[1], getValues()[1]);
+ sourceMap.put(getKeys()[2], getValues()[2]);
+
+ TreeMap<K, V> copyConstructed = new TreeMap<K, V>(sourceMap);
+ _assertEquals(sourceMap, copyConstructed);
+ }
+
+ /**
+ * Test method for 'java.util.TreeMap.TreeMap(SortedMap).
+ *
+ * @see java.util.TreeMap#TreeMap(SortedMap)
+ */
+ public void testConstructor_SortedMap_throwsNullPointerException() {
+ try {
+ new TreeMap<K, V>((SortedMap<K, V>) null);
+ fail("expected exception");
+ } catch (NullPointerException e) {
+ // expected outcome
+ } catch (JavaScriptException e) {
+ // in web mode we don't actually do null checks, so we get a JS exception
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.containsKey(Object)'. *
+ *
+ * @see java.util.Map#containsKey(Object)
+ */
+ public void testContainsKey() {
+ Map<K, V> map = createMap();
+ assertFalse(map.containsKey(getKeys()[0]));
+ assertNull(map.put(getKeys()[0], getValues()[0]));
+ assertEquals(1, map.keySet().size());
+ assertTrue(map.containsKey(getKeys()[0]));
+ assertFalse(map.containsKey(getKeys()[1]));
+ }
+
+ /**
+ * Test method for 'java.util.Map.containsKey(Object)'.
+ *
+ * @see java.util.Map#containsKey(Object)
+ */
+ public void testContainsKey_throwsClassCastException() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ try {
+ map.containsKey(getConflictingKey());
+ assertTrue("CCE expected in hosted mode", GWT.isScript());
+ } catch (ClassCastException e) {
+ // expected outcome
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.containsKey(Object)'.
+ *
+ * @see java.util.Map#containsKey(Object)
+ */
+ public void testContainsKey_throwsNullPointerException() {
+ Map<K, V> map = createMap();
+ if (isNaturalOrder() && !isNullKeySupported) {
+ try {
+ map.containsKey(null);
+ fail("expected exception");
+ } catch (NullPointerException e) {
+ // expected outcome
+ } catch (JavaScriptException e) {
+ // in web mode we don't actually do null checks, so we get a JS
+ // exception
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.containsValue(Object)'.
+ *
+ * @see java.util.Map#containsValue(Object)
+ */
+ public void testContainsValue() {
+ Map<K, V> map = createMap();
+ assertFalse(map.containsValue(getValues()[0]));
+ map.put(getKeys()[0], getValues()[0]);
+ assertEquals(1, map.values().size());
+ assertTrue(map.containsValue(getValues()[0]));
+ assertFalse(map.containsValue(getKeys()[0]));
+ assertFalse(map.containsValue(getValues()[1]));
+ assertFalse(map.containsValue(null));
+ map.put(getKeys()[0], null);
+ assertTrue(map.containsValue(null));
+ }
+
+ /**
+ * Test method for 'java.util.Map.containsValue(Object)'.
+ *
+ * @see java.util.Map#containsValue(Object)
+ */
+ public void testContainsValue_throwsClassCastExcption() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ map.containsValue(getConflictingValue());
+
+ // You might think this should throw an exception here but, no. Makes
+ // sense since the class cast is attributed to comparability of the
+ // keys... generics really have nothing to do with it .
+
+ // try {
+ // map.containsValue(getConflictingValue());
+ // fail("expected exception");
+ // } catch (ClassCastException e) {
+ // // expected outcome
+ // }
+ }
+
+ /**
+ * Test method for 'java.util.Map.containsValue(Object)'.
+ *
+ * @see java.util.Map#containsValue(Object)
+ */
+ public void testContainsValue_throwsNullPointerException() {
+ Map<K, V> map = createMap();
+ if (!isNullValueSupported) {
+ try {
+ map.containsValue(null);
+ fail("expected exception");
+ } catch (NullPointerException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.entrySet().remove(Object)'.
+ *
+ * @see java.util.Map#entrySet()
+ */
+ public void testEntrySet_add_throwsUnsupportedOperationException() {
+ Map<K, V> map = createMap();
+ try {
+ map.entrySet().add(new Entry<K, V>() {
+ public K getKey() {
+ return null;
+ }
+
+ public V getValue() {
+ return null;
+ }
+
+ public V setValue(V value) {
+ return null;
+ }
+ });
+ fail("expected exception");
+ } catch (UnsupportedOperationException e) {
+ // expected outcome
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.entrySet()'.
+ *
+ * @see java.util.Map#entrySet()
+ */
+ public void testEntrySet_entries0() {
+ Map<K, V> map = createMap();
+ Set<Entry<K, V>> entrySet = map.entrySet();
+ _assertEmpty(entrySet);
+ }
+
+ /**
+ * Test method for 'java.util.Map.entrySet()'.
+ *
+ * @see java.util.Map#entrySet()
+ */
+ public void testEntrySet_entries1() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+
+ // Verify the view correctly represents the map
+ Set<Entry<K, V>> entrySet = map.entrySet();
+ assertNotNull(entrySet);
+ Iterator<Entry<K, V>> iter = entrySet.iterator();
+ assertNotNull(iter);
+ assertTrue(iter.hasNext());
+ Entry<K, V> entry = iter.next();
+ assertNotNull(entry);
+
+ assertEquals(entry.getKey(), getKeys()[0]);
+ assertEquals(entry.getValue(), getValues()[0]);
+ }
+
+ /**
+ * Test method for 'java.util.Map.entrySet()'.
+ *
+ * @see java.util.Map#entrySet()
+ */
+ public void testEntrySet_entries1_view() {
+ Map<K, V> map = createMap();
+ // Get a view of the entry set before modifying the underlying map.
+ Set<Entry<K, V>> entrySet = map.entrySet();
+ map.put(getKeys()[0], getValues()[0]);
+
+ // Verify that the entries view reflects updates to the map.
+ assertEquals(entrySet.iterator().next().getKey(), getKeys()[0]);
+ assertEquals(entrySet.iterator().next().getValue(), getValues()[0]);
+ }
+
+ /**
+ * Test method for 'java.util.Map.entrySet()'.
+ *
+ * @see java.util.Map#entrySet()
+ */
+ public void testEntrySet_entries1_view_modify() {
+ Map<K, V> map = createMap();
+ Set<Entry<K, V>> entrySet = map.entrySet(); // get view before modification
+ map.put(getKeys()[0], getValues()[0]); // put a value
+ map.put(getKeys()[0], getValues()[1]); // overwrite the value
+
+ // Verify that the entries view reflects updates to the map.
+ assertEquals(entrySet.iterator().next().getKey(), getKeys()[0]);
+ assertEquals(entrySet.iterator().next().getValue(), getValues()[1]);
+
+ // Verify that the entries view is updated on removes to the map.
+ map.remove(getKeys()[0]);
+ _assertEmpty(entrySet);
+ }
+
+ public void testEntrySet_entry_setValue() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ map.entrySet().iterator().next().setValue(getValues()[1]);
+ assertTrue(map.containsValue(getValues()[1]));
+ }
+
+ /**
+ * Test method for 'java.util.Map.entrySet().remove(Object)'.
+ *
+ * @see java.util.Map#entrySet()
+ */
+ public void testEntrySet_remove() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+
+ Set<Entry<K, V>> entrySet = map.entrySet();
+ assertTrue(entrySet.remove(entrySet.iterator().next()));
+ assertTrue(entrySet.isEmpty());
+ assertEquals(entrySet.size(), map.size());
+ }
+
+ /**
+ * Test method for 'java.util.Map.entrySet().remove(Object)'.
+ *
+ * @see java.util.Map#entrySet()
+ */
+ public void testEntrySet_remove_equivalentEntry() {
+ Map<K, V> map0 = createMap();
+ map0.put(getKeys()[0], getValues()[0]);
+
+ Map<K, V> map1 = createMap();
+ map1.put(getKeys()[0], getValues()[1]);
+
+ // Verify attempting to remove an equivalent entry from a different map has
+ // no effect.
+ Set<Entry<K, V>> entrySet0 = map0.entrySet();
+ assertFalse(entrySet0.remove(map1.entrySet().iterator().next()));
+ assertFalse(entrySet0.isEmpty());
+ assertEquals(entrySet0.size(), map0.size());
+ }
+
+ /**
+ * Test method for 'java.util.Object.equals(Object)'.
+ *
+ * @see java.util.Map#equals(Object)
+ */
+ public void testEquals() {
+ Map<K, V> map0 = createMap();
+ Map<K, V> map1 = createMap();
+ assertTrue(map0.equals(map1));
+ map0.put(getKeys()[0], getValues()[0]);
+ map1.put(getKeys()[0], getValues()[0]);
+ assertTrue(map0.equals(map0));
+ assertTrue(map0.equals(map1));
+ map0.put(getKeys()[1], getValues()[1]);
+ assertFalse(map0.equals(map1));
+ }
+
+ /**
+ * Test method for 'java.lang.Object.finalize()'.
+ */
+ public void testFinalize() {
+ // TODO no tests for finalize?
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.firstKey()'.
+ *
+ * @see java.util.SortedMap#firstKey()
+ */
+ public void testFirstKey() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ // test with a single entry map
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ assertEquals(getKeys()[0], sortedMap.firstKey());
+ // is it consistent with other methods
+ assertEquals(sortedMap.keySet().toArray()[0], sortedMap.firstKey());
+ assertEquals(getKeys()[0], sortedMap.lastKey());
+ assertEquals(sortedMap.lastKey(), sortedMap.firstKey());
+
+ // test with two entry map
+ sortedMap.put(getKeys()[1], getValues()[1]);
+ assertEquals(getKeys()[0], sortedMap.firstKey());
+ assertFalse(getKeys()[1].equals(sortedMap.firstKey()));
+ // is it consistent with other methods
+ assertEquals(sortedMap.keySet().toArray()[0], sortedMap.firstKey());
+ assertFalse(getKeys()[0].equals(sortedMap.lastKey()));
+ assertFalse(sortedMap.lastKey().equals(sortedMap.firstKey()));
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.firstKey()'.
+ *
+ * @see java.util.SortedMap#firstKey()
+ */
+ public void testFirstKey_throwsNoSuchElementException() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ // test with no entries
+ try {
+ sortedMap.firstKey();
+ fail("expected exception");
+ } catch (NoSuchElementException e) {
+ // expected outcome
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.get(Object)'.
+ *
+ * @see java.util.Map#get(Object)
+ */
+ public void testGet() {
+ Map<K, V> map = createMap();
+ if (useNullKey()) {
+ assertNull(map.get(null));
+ }
+ assertNull(map.get(getKeys()[0]));
+ assertNull(map.put(getKeys()[0], getValues()[0]));
+ assertEquals(getValues()[0], map.get(getKeys()[0]));
+ }
+
+ /**
+ * Test method for 'java.util.Map.get(Object)'.
+ *
+ * @see java.util.Map#get(Object)
+ */
+ public void testGet_throwsClassCastException() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ try {
+ map.get(getConflictingKey());
+ assertTrue("CCE expected in hosted mode", GWT.isScript());
+ } catch (ClassCastException e) {
+ // expected outcome
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.get(Object)'.
+ *
+ * @see java.util.Map#get(Object)
+ */
+ public void testGet_throwsNullPointerException() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ try {
+ map.get(null);
+ assertTrue("expected exception", useNullKey());
+ } catch (NullPointerException e) {
+ assertFalse("unexpected NPE", useNullKey());
+ }
+ }
+
+ /**
+ * Test method for 'java.lang.Object.hashCode()'.
+ *
+ * @see java.util.Map#hashCode()
+ */
+ public void testHashCode() {
+ Map<K, V> map0 = createMap();
+ Map<K, V> map1 = createMap();
+
+ int hashCode0 = map0.hashCode();
+ int hashCode1 = map1.hashCode();
+ assertTrue("empty maps have different hash codes", hashCode0 == hashCode1);
+
+ // Check that hashCode changes
+ map0.put(getKeys()[0], getValues()[0]);
+ hashCode0 = map0.hashCode();
+ assertTrue("hash code didn't change", hashCode0 != hashCode1);
+
+ // The above is actually not a completely dependable test because hash codes
+ // are funky at the edges. The hash code of an abstract map is determined by
+ // accumulating the hash code of the contained Entry(s). The TreeMap Entry
+ // hash code implementation will always result in 0 if the exclusive or of
+ // the key and value for the Entry is 0.
+
+ Map<String, String> map2 = new TreeMap<String, String>();
+ Map<Integer, Integer> map3 = new TreeMap<Integer, Integer>();
+
+ map2.put("", "");
+
+ map3.put(0, Integer.MIN_VALUE);
+ map3.put(Integer.MIN_VALUE, 0);
+
+ int hashCode2 = map2.hashCode();
+ int hashCode3 = map3.hashCode();
+ assertEquals("empty string/0 hash codes not the same", hashCode2, hashCode3);
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.headMap(Object)'.
+ *
+ * @see java.util.SortedMap#headMap(Object)
+ */
+ public void testHeadMap() {
+ // test with no entries
+ assertNotNull(createSortedMap().headMap(getKeys()[0]));
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.headMap(Object)'.
+ *
+ * @see java.util.SortedMap#headMap(Object)
+ */
+ public void testHeadMap_entries0_size() {
+ // test with no entries
+ assertEquals(0, createSortedMap().headMap(getKeys()[0]).size());
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.headMap(Object)'.
+ *
+ * @see java.util.SortedMap#headMap(Object)
+ */
+ public void testHeadMap_entries1() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ // test with a single entry map
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ assertEquals(0, sortedMap.headMap(getKeys()[0]).size());
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.headMap(Object)'.
+ *
+ * @see java.util.SortedMap#headMap(Object)
+ */
+ public void testHeadMap_entries2() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ // test with two entry map
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ sortedMap.put(getKeys()[1], getValues()[1]);
+ assertEquals(0, sortedMap.headMap(getKeys()[0]).size());
+ assertEquals(1, sortedMap.headMap(getKeys()[1]).size());
+ assertEquals(getKeys()[0],
+ sortedMap.tailMap(getKeys()[0]).keySet().toArray()[0]);
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.headMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#headMap(Object)
+ */
+ @SuppressWarnings("unchecked")
+ public void testHeadMap_throwsClassCastException() {
+ SortedMap sortedMap = createSortedMap();
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ if (isNaturalOrder()) {
+ // TODO Why does this succeed with natural ordering when subMap doesn't?
+ sortedMap.headMap(getConflictingKey());
+ } else {
+ try {
+ sortedMap.headMap(getConflictingKey());
+ assertTrue("CCE expected in hosted mode", GWT.isScript());
+ } catch (ClassCastException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.headMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#headMap(Object)
+ */
+ public void testHeadMap_throwsIllegalArgumentException() {
+ // TODO I don't know of any case where this could happen.
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.headMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#headMap(Object)
+ */
+ public void testHeadMap_throwsNullPointerException() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ try {
+ sortedMap.headMap(null);
+ assertTrue(useNullKey() || GWT.isScript());
+ } catch (NullPointerException e) {
+ assertFalse(useNullKey());
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.isEmpty()'. *
+ *
+ * @see java.util.Map#isEmpty()
+ *
+ */
+ public void testIsEmpty() {
+ Map<K, V> sourceMap = createMap();
+ Map<K, V> destMap = createMap();
+
+ destMap.putAll(sourceMap);
+ assertTrue(destMap.isEmpty());
+
+ destMap.put(getKeys()[0], getValues()[0]);
+ assertFalse(destMap.isEmpty());
+
+ destMap.remove(getKeys()[0]);
+ assertTrue(destMap.isEmpty());
+ assertEquals(destMap.size(), 0);
+ }
+
+ /**
+ * Test method for 'java.util.Map.keySet()'.
+ *
+ * @see java.util.Map#clear()
+ */
+ public void testKeySet() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ Set<K> keySet = map.keySet();
+ _assertEquals(keySet, keySet);
+ }
+
+ /**
+ * Test method for 'java.util.Map.keySet()'.
+ *
+ * @see java.util.Map#clear()
+ */
+ public void testKeySet_viewPut() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ Set<K> keySet = map.keySet();
+ assertEquals(1, keySet.size());
+ map.put(getKeys()[1], getValues()[1]);
+ assertEquals(2, keySet.size());
+ }
+
+ /**
+ * Test method for 'java.util.Map.keySet()'.
+ *
+ * @see java.util.Map#clear()
+ */
+ public void testKeySet_viewRemove() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ map.put(getKeys()[1], getValues()[1]);
+ Set<K> keySet = map.keySet();
+ assertEquals(2, keySet.size());
+ map.remove(getKeys()[1]);
+ assertEquals(1, keySet.size());
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.lastKey()'.
+ *
+ * @see java.util.SortedMap#lastKey()
+ */
+ public void testLastKey() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+
+ // test with a single entry map
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ assertEquals(getKeys()[0], sortedMap.lastKey());
+ // is it consistent with other methods
+ assertEquals(sortedMap.keySet().toArray()[0], sortedMap.lastKey());
+ assertEquals(getKeys()[0], sortedMap.firstKey());
+ assertEquals(sortedMap.firstKey(), sortedMap.lastKey());
+
+ // test with two entry map
+ sortedMap.put(getKeys()[1], getValues()[1]);
+ assertEquals(getKeys()[1], sortedMap.lastKey());
+ assertFalse(getKeys()[0].equals(sortedMap.lastKey()));
+ // is it consistent with other methods
+ assertEquals(sortedMap.keySet().toArray()[1], sortedMap.lastKey());
+ assertEquals(getKeys()[0], sortedMap.firstKey());
+ assertFalse(sortedMap.firstKey().equals(sortedMap.lastKey()));
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.lastKey()'.
+ *
+ * @see java.util.SortedMap#lastKey()
+ */
+ public void testLastKey_throwsNoSuchElementException() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ // test with no entries
+ try {
+ sortedMap.lastKey();
+ fail("expected exception");
+ } catch (NoSuchElementException e) {
+ // expected outcome
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.put(Object, Object)'.
+ *
+ * @see java.util.Map#put(Object, Object)
+ */
+ public void testPut() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutSupported) {
+ Map<K, V> map = createMap();
+ assertNull(map.put(getKeys()[0], getValues()[0]));
+ assertFalse(map.isEmpty());
+ assertEquals(1, map.size());
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.put(Object, Object)'.
+ *
+ * @see java.util.Map#put(Object, Object)
+ */
+ public void testPut_entries3() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutSupported) {
+ // populate the map
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ map.put(getKeys()[1], getValues()[1]);
+ map.put(getKeys()[2], getValues()[2]);
+
+ // test contents
+ assertFalse(map.isEmpty());
+ assertEquals(3, map.size());
+ // test contains all values
+ Collection<V> values = map.values();
+ assertTrue(values.contains(getValues()[0]));
+ assertTrue(values.contains(getValues()[1]));
+ assertTrue(values.contains(getValues()[2]));
+ Collection<K> keys = map.keySet();
+ // test contains all keys
+ assertTrue(keys.contains(getKeys()[0]));
+ assertTrue(keys.contains(getKeys()[1]));
+ assertTrue(keys.contains(getKeys()[2]));
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.put(Object, Object)'. This test shows some
+ * bad behavior of the TreeMap class. A mapping with null key can be put in
+ * but several methods are are unusable afterward.
+ *
+ * A SortedMap with natural ordering (no comparator) is supposed to throw a
+ * null pointer exception if a null keys are "not supported". For a natural
+ * ordered TreeMap, a null pointer exception is not thrown. But, the map is
+ * left in a state where any other key based methods result in a null pointer
+ * exception.
+ *
+ * @see java.util.Map#put(Object, Object)
+ */
+ public void testPut_nullKey_poison() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+
+ if (useNullKey()) {
+ assertNull(sortedMap.put(null, getValues()[0]));
+ assertTrue(sortedMap.containsValue(getValues()[0]));
+
+ // the map methods the continue to function
+ sortedMap.containsValue(null);
+ sortedMap.containsValue(getValues()[0]);
+ sortedMap.entrySet();
+ sortedMap.equals(createMap());
+ sortedMap.hashCode();
+ sortedMap.isEmpty();
+ sortedMap.keySet();
+ sortedMap.putAll(createMap());
+ sortedMap.size();
+ sortedMap.values();
+
+ // all of the sorted map methods still function
+ sortedMap.comparator();
+ sortedMap.firstKey();
+ sortedMap.lastKey();
+ sortedMap.subMap(getLessThanMinimumKey(), getGreaterThanMaximumKey());
+ sortedMap.headMap(getLessThanMinimumKey());
+ sortedMap.tailMap(getLessThanMinimumKey());
+ } else {
+ try {
+ assertNull(sortedMap.put(null, getValues()[0]));
+ // note: first null added is not required to throw NPE since no
+ // comparisons are needed
+ } catch (NullPointerException e) {
+ // expected outcome
+ }
+ try {
+ assertNull(sortedMap.put(null, getValues()[1]));
+ fail("expected exception adding second null");
+ } catch (NullPointerException e) {
+ // expected outcome
+ }
+ try {
+ sortedMap.containsKey(null);
+ fail("expected exception on containsKey(null)");
+ } catch (NullPointerException e) {
+ // expected outcome
+ }
+ try {
+ sortedMap.containsKey(getKeys()[0]);
+ fail("expected exception on contains(key)");
+ } catch (NullPointerException e) {
+ // expected outcome
+ }
+ try {
+ sortedMap.get(null);
+ fail("expected exception on get(null)");
+ } catch (NullPointerException e) {
+ // expected outcome
+ }
+ try {
+ sortedMap.get(getKeys()[0]);
+ fail("expected exception on get(key)");
+ } catch (NullPointerException e) {
+ // expected outcome
+ }
+ try {
+ sortedMap.remove(null);
+ fail("expected exception on remove(null)");
+ } catch (NullPointerException e) {
+ // expected outcome
+ }
+ try {
+ sortedMap.remove(getKeys()[0]);
+ fail("expected exception on remove(key)");
+ } catch (NullPointerException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.put(Object, Object)'.
+ *
+ * @see java.util.Map#put(Object, Object)
+ */
+ public void testPut_replace() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutSupported) {
+ Map<K, V> map = createMap();
+ assertNull(map.put(getKeys()[0], getValues()[0]));
+ assertFalse(map.isEmpty());
+ assertEquals(1, map.size());
+
+ assertEquals(map.put(getKeys()[0], getValues()[1]), getValues()[0]);
+ assertEquals(1, map.size());
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.put(Object, Object)'.
+ *
+ * @see java.util.Map#put(Object, Object)
+ */
+ @SuppressWarnings("unchecked")
+ public void testPut_throwsClassCastException_key() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutSupported) {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ try {
+ Map untypedMap = map;
+ untypedMap.put(getConflictingKey(), getValues()[1]);
+ assertTrue("CCE expected in hosted mode", GWT.isScript());
+ } catch (ClassCastException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.put(Object, Object)'.
+ *
+ * @see java.util.Map#put(Object, Object)
+ */
+ @SuppressWarnings("unchecked")
+ public void testPut_throwsClassCastException_value() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutSupported) {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+
+ Map untypedMap = map;
+ untypedMap.put(getKeys()[1], getConflictingValue());
+ // You might think this should throw an exception here but, no. Makes
+ // sense since the class cast is attributed to comparability of the
+ // keys... generics really have nothing to do with it .
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.put(Object, Object)'.
+ *
+ * @see java.util.Map#put(Object, Object)
+ */
+ public void testPut_throwsIllegalArgumentException() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutSupported) {
+ // TODO I don't know of any case where this could happen.
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.put(Object, Object)'.
+ *
+ * @see java.util.Map#put(Object, Object)
+ */
+ public void testPut_throwsNullPointerException() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutSupported) {
+ Map<K, V> map;
+ map = createMap();
+
+ try {
+ map.put(null, getValues()[0]);
+ // first put of a null key is not required to NPE since no comparisons
+ // are needed
+ } catch (NullPointerException e) {
+ assertFalse(useNullKey());
+ }
+
+ try {
+ map.put(null, getValues()[0]);
+ assertTrue(useNullKey());
+ } catch (NullPointerException e) {
+ assertFalse(useNullKey());
+ }
+
+ map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ try {
+ map.put(null, getValues()[0]);
+ assertTrue(useNullKey());
+ } catch (NullPointerException e) {
+ assertFalse(useNullKey());
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.put(Object, Object)'.
+ *
+ * @see java.util.Map#put(Object, Object)
+ */
+ public void testPut_throwsUnsupportedOperationException() {
+ if (!isPutSupported) {
+ Map<K, V> map = createMap();
+ try {
+ map.put(getKeys()[0], getValues()[0]);
+ fail("expected exception");
+ } catch (UnsupportedOperationException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.putAll(Map)'.
+ *
+ * @see java.util.Map#putAll(Map)
+ */
+ public void testPutAll() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutAllSupported) {
+ Map<K, V> sourceMap = createMap();
+ sourceMap.put(getKeys()[0], getValues()[0]);
+ sourceMap.put(getKeys()[1], getValues()[1]);
+ sourceMap.put(getKeys()[2], getValues()[2]);
+
+ Map<K, V> destMap = createMap();
+ destMap.putAll(sourceMap);
+ // Make sure that the data is copied correctly
+ _assertEquals(sourceMap, destMap);
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.putAll(Map)'.
+ *
+ * @see java.util.Map#putAll(Map)
+ */
+ public void testPutAll_addEntries() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutAllSupported) {
+ Map<K, V> sourceMap = createMap();
+ sourceMap.put(getKeys()[0], getValues()[0]);
+
+ Map<K, V> destMap = createMap();
+ destMap.putAll(sourceMap);
+ // Verify that entries get added.
+ sourceMap.put(getKeys()[1], getValues()[1]);
+ destMap.putAll(sourceMap);
+ _assertEquals(sourceMap, destMap);
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.putAll(Map)'.
+ *
+ * @see java.util.Map#putAll(Map)
+ */
+ public void testPutAll_emptyMap() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutAllSupported) {
+ Map<K, V> sourceMap = createMap();
+ sourceMap.put(getKeys()[0], getValues()[0]);
+
+ Map<K, V> destMap = createMap();
+ destMap.putAll(sourceMap);
+ // Verify that putting an empty map does not clear.
+ destMap.putAll(createMap());
+ _assertEquals(sourceMap, destMap);
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.putAll(Map)'.
+ *
+ * @see java.util.Map#putAll(Map)
+ */
+ public void testPutAll_overwrite() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutAllSupported) {
+ Map<K, V> sourceMap = createMap();
+ sourceMap.put(getKeys()[0], getValues()[0]);
+
+ Map<K, V> destMap = createMap();
+ destMap.putAll(sourceMap);
+ // Verify that entries get replaced.
+ sourceMap.put(getKeys()[0], getValues()[1]);
+ destMap.putAll(sourceMap);
+ _assertEquals(sourceMap, destMap);
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.putAll(Map)'.
+ *
+ * @see java.util.Map#putAll(Map)
+ */
+ public void testPutAll_self() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutAllSupported) {
+ Map<K, V> sourceMap = createMap();
+ sourceMap.put(getKeys()[0], getValues()[0]);
+ sourceMap.putAll(sourceMap);
+ // verify putAll with self succeeds and has no effect.
+ assertEquals(1, sourceMap.size());
+ assertEquals(getKeys()[0], sourceMap.keySet().iterator().next());
+ assertEquals(getValues()[0], sourceMap.values().iterator().next());
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.putAll(Map)'.
+ *
+ * @see java.util.Map#putAll(Map)
+ */
+ @SuppressWarnings("unchecked")
+ public void testPutAll_throwsClassCastException() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutAllSupported) {
+ Map sourceMap = createMap();
+ sourceMap.put(getConflictingKey(), getConflictingValue());
+
+ Map<K, V> destMap = createMap();
+ destMap.put(getKeys()[0], getValues()[0]);
+ try {
+ destMap.putAll(sourceMap);
+ assertTrue("CCE expected in hosted mode", GWT.isScript());
+ } catch (ClassCastException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.putAll(Map)'.
+ *
+ * @see java.util.Map#putAll(Map)
+ */
+ public void testPutAll_throwsIllegalOperationException() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutAllSupported) {
+ // TODO I don't know of any case where this could happen.
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.putAll(Map)'.
+ *
+ * @see java.util.Map#putAll(Map)
+ */
+ public void testPutAll_throwsNullPointerException() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isPutAllSupported) {
+ Map<K, V> map = createMap();
+ try {
+ map.putAll((Map<K, V>) null);
+ fail("expected exception");
+ } catch (NullPointerException e) {
+ // expected outcome
+ } catch (JavaScriptException e) {
+ // in web mode we don't actually do null checks, so we get a JS
+ // exception
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.putAll(Map)'.
+ *
+ * @see java.util.Map#putAll(Map)
+ */
+ public void testPutAll_throwsUnsupportedOperationException() {
+ Map<K, V> map = createMap();
+ if (!isPutAllSupported) {
+ try {
+ map.putAll(createMap());
+ fail("expected exception");
+ } catch (UnsupportedOperationException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.remove(Object)'.
+ *
+ * @see java.util.Map#remove(Object)
+ */
+ public void testRemove() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isRemoveSupported) {
+ Map<K, V> map = createMap();
+
+ // null keys are special
+ if (useNullKey()) {
+ assertNull(map.remove(null));
+ }
+
+ assertNull(map.remove(getKeys()[0]));
+ assertNull(map.put(getKeys()[0], getValues()[0]));
+ assertEquals(map.remove(getKeys()[0]), getValues()[0]);
+ assertNull(map.remove(getKeys()[0]));
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.remove(Object)'.
+ *
+ * @see java.util.Map#remove(Object)
+ */
+ public void testRemove_throwsClassCastException() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isRemoveSupported) {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ try {
+ map.remove(getConflictingKey());
+ assertTrue("CCE expected in hosted mode", GWT.isScript());
+ } catch (ClassCastException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.remove(Object)'.
+ *
+ * @see java.util.Map#remove(Object)
+ */
+ public void testRemove_throwsNullPointerException() {
+ // The _throwsUnsupportedOperationException version of this test will
+ // verify that the method is not supported.
+ if (isRemoveSupported) {
+ Map<K, V> map;
+ map = createMap();
+ // test remove null key with map containing a single null key
+ map.put(null, getValues()[0]);
+ try {
+ map.remove(null);
+ assertTrue(useNullKey());
+ } catch (NullPointerException e) {
+ assertFalse(useNullKey());
+ }
+
+ map = createMap();
+ // test remove null key with map containing a single non-null key
+ map.put(getKeys()[0], getValues()[0]);
+ try {
+ map.remove(null);
+ assertTrue(useNullKey());
+ } catch (NullPointerException e) {
+ assertFalse(useNullKey());
+ } catch (JavaScriptException e) {
+ assertFalse(useNullKey());
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.remove(Object)'.
+ *
+ * @see java.util.Map#remove(Object)
+ */
+ public void testRemove_throwsUnsupportedOperationException() {
+ Map<K, V> map = createMap();
+ if (!isRemoveSupported) {
+ try {
+ map.remove(getKeys()[0]);
+ fail("expected exception");
+ } catch (UnsupportedOperationException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.Map.size()'.
+ *
+ * @see java.util.Map#size()
+ */
+ public void testSize() {
+ Map<K, V> map = createMap();
+
+ // Test size behavior on put
+ map.put(getKeys()[0], getValues()[0]);
+ assertEquals(1, map.size());
+ map.put(getKeys()[1], getValues()[1]);
+ assertEquals(2, map.size());
+ map.put(getKeys()[2], getValues()[2]);
+ assertEquals(3, map.size());
+
+ // Test size behavior on remove
+ map.remove(getKeys()[0]);
+ assertEquals(2, map.size());
+ map.remove(getKeys()[1]);
+ assertEquals(1, map.size());
+ map.remove(getKeys()[2]);
+ assertEquals(0, map.size());
+
+ // Test size behavior on putAll
+ map.put(getKeys()[0], getValues()[0]);
+ map.put(getKeys()[1], getValues()[1]);
+ map.put(getKeys()[2], getValues()[2]);
+ assertEquals(3, map.size());
+
+ // Test size behavior on clear
+ map.clear();
+ _assertEmpty(map);
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.subMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#subMap(Object, Object)
+ */
+ public void testSubMap() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ // test with no entries
+ assertEquals(0, sortedMap.subMap(getKeys()[0], getKeys()[0]).size());
+
+ // test with a single entry map
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ assertEquals(0, sortedMap.subMap(getKeys()[0], getKeys()[0]).size());
+ // bounded by a "wide" range
+ assertEquals(1, sortedMap.subMap(getLessThanMinimumKey(),
+ getGreaterThanMaximumKey()).size());
+
+ // test with two entry map
+ sortedMap.put(getKeys()[1], getValues()[1]);
+ assertEquals(1, sortedMap.subMap(getKeys()[0], getKeys()[1]).size());
+ assertEquals(getKeys()[0],
+ sortedMap.subMap(getKeys()[0], getKeys()[1]).keySet().toArray()[0]);
+ // bounded by a "wide" range
+ SortedMap<K, V> subMap = sortedMap.subMap(getLessThanMinimumKey(),
+ getGreaterThanMaximumKey());
+ assertEquals(2, subMap.size());
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.subMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#subMap(Object, Object)
+ */
+ @SuppressWarnings("unchecked")
+ public void testSubMap_throwsClassCastException() {
+ SortedMap sortedMap = createSortedMap();
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ try {
+ sortedMap.subMap(getConflictingKey(), getKeys()[0]);
+ assertTrue("CCE expected in hosted mode", GWT.isScript());
+ } catch (IllegalArgumentException e) {
+ // since we can't ensure CCEs in web mode, we may get IAE
+ assertTrue("IllegalArgumentException in hosted mode", GWT.isScript());
+ } catch (ClassCastException e) {
+ // expected outcome
+ }
+ try {
+ sortedMap.subMap(getKeys()[0], getConflictingKey());
+ assertTrue("CCE expected in hosted mode", GWT.isScript());
+ } catch (IllegalArgumentException e) {
+ // since we can't ensure CCEs in web mode, we may get IAE
+ assertTrue("IllegalArgumentException in hosted mode", GWT.isScript());
+ } catch (ClassCastException e) {
+ // expected outcome
+ }
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.subMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#subMap(Object, Object)
+ */
+ public void testSubMap_throwsIllegalArgumentException() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ try {
+ sortedMap.subMap(getGreaterThanMaximumKey(), getLessThanMinimumKey());
+ fail("expected exception");
+ } catch (IllegalArgumentException e) {
+ // from key is greater than the to key
+ // expected outcome
+ }
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.subMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#subMap(Object, Object)
+ */
+ public void testSubMap_throwsNullPointerException() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ try {
+ sortedMap.subMap(null, getLessThanMinimumKey());
+ assertTrue(useNullKey());
+ } catch (NullPointerException e) {
+ assertFalse(useNullKey());
+ } catch (JavaScriptException e) {
+ assertFalse(useNullKey());
+ }
+ try {
+ sortedMap.subMap(null, getGreaterThanMaximumKey());
+ assertTrue(useNullKey());
+ } catch (NullPointerException e) {
+ assertFalse(useNullKey());
+ } catch (JavaScriptException e) {
+ assertFalse(useNullKey());
+ }
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.tailMap(Object)'.
+ *
+ * @see java.util.SortedMap#tailMap(Object)
+ */
+ public void testTailMap_entries0() {
+ // test with no entries
+ Map<K, V> tailMap = createSortedMap().tailMap(getKeys()[0]);
+ assertNotNull(tailMap);
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.tailMap(Object)'.
+ *
+ * @see java.util.SortedMap#tailMap(Object)
+ */
+ public void testTailMap_entries0_size() {
+ // test with no entries
+ Map<K, V> tailMap = createSortedMap().tailMap(getKeys()[0]);
+ assertNotNull(tailMap);
+ assertEquals(0, tailMap.size());
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.tailMap(Object)'.
+ *
+ * @see java.util.SortedMap#tailMap(Object)
+ */
+ public void testTailMap_entries1_size_keyValue() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ // test with a single entry map
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ Map<K, V> tailMap = sortedMap.tailMap(getKeys()[0]);
+ assertEquals(1, tailMap.size());
+ assertEquals(getKeys()[0], tailMap.keySet().toArray()[0]);
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.tailMap(Object)'.
+ *
+ * @see java.util.SortedMap#tailMap(Object)
+ */
+ public void testTailMap_entries2_size_keyValue() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ // test with two entry map
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ Map<K, V> tailMap = sortedMap.tailMap(getKeys()[0]);
+ assertEquals(1, tailMap.size());
+ sortedMap.put(getKeys()[1], getValues()[1]);
+ tailMap = sortedMap.tailMap(getKeys()[1]);
+ assertEquals(1, tailMap.size());
+ tailMap = sortedMap.tailMap(getKeys()[0]);
+ assertEquals(2, tailMap.size());
+ assertEquals(getKeys()[0], tailMap.keySet().toArray()[0]);
+ assertEquals(getKeys()[1], tailMap.keySet().toArray()[1]);
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.tailMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#tailMap(Object)
+ */
+ @SuppressWarnings("unchecked")
+ public void testTailMap_throwsClassCastException() {
+ SortedMap sortedMap = createSortedMap();
+ sortedMap.put(getKeys()[0], getValues()[0]);
+ if (isNaturalOrder()) {
+ // TODO Why does this succeed with natural ordering when subMap doesn't?
+ sortedMap.tailMap(getConflictingKey());
+ } else {
+ try {
+ sortedMap.tailMap(getConflictingKey());
+ assertTrue("CCE expected in hosted mode", GWT.isScript());
+ } catch (ClassCastException e) {
+ // expected outcome
+ }
+ }
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.tailMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#tailMap(Object)
+ */
+ public void testTailMap_throwsIllegalArgumentException() {
+ // TODO I don't know of any case where this could happen.
+ }
+
+ /**
+ * Test method for 'java.util.SortedMap.tailMap(Object, Object)'.
+ *
+ * @see java.util.SortedMap#tailMap(Object)
+ */
+ public void testTailMap_throwsNullPointerException() {
+ SortedMap<K, V> sortedMap = createSortedMap();
+ try {
+ sortedMap.tailMap(null);
+ assertTrue(useNullKey());
+ } catch (NullPointerException e) {
+ assertFalse(useNullKey());
+ }
+ }
+
+ /**
+ * Test method for 'java.lang.Object.toString()'.
+ */
+ public void testToString() {
+ Map<K, V> map = createMap();
+ map.put(getKeys()[0], getValues()[0]);
+ String entryString = makeEntryString(getKeys()[0], getValues()[0]);
+ assertEquals(entryString, map.toString());
+ }
+
+ /**
+ * Test method for 'java.util.Map.values()'.
+ *
+ * @see java.util.Map#values()
+ */
+ public void testValues() {
+ Map<K, V> map = createMap();
+
+ map.put(getKeys()[0], getValues()[0]);
+
+ Collection<V> values = map.values();
+ assertNotNull(values);
+ assertEquals(1, values.size());
+
+ Iterator<V> valueIter = values.iterator();
+ V value = valueIter.next();
+ assertEquals(value, getValues()[0]);
+ }
+
+ /**
+ * Test method for 'java.util.Map.values()'.
+ *
+ * @see java.util.Map#values()
+ */
+ public void testValues_nullKey() {
+ Map<K, V> map = createMap();
+
+ map.put(getKeys()[0], getValues()[0]);
+
+ Collection<V> values = map.values();
+ assertNotNull(values);
+ assertEquals(1, values.size());
+
+ Iterator<V> valueIter = values.iterator();
+ V value = valueIter.next();
+ assertEquals(value, getValues()[0]);
+ }
+
+ /**
+ * Test method for 'java.util.Map.values()'.
+ *
+ * @see java.util.Map#values()
+ */
+ public void testValues_viewPut() {
+ Map<K, V> map = createMap();
+
+ map.put(getKeys()[0], getValues()[0]);
+
+ Collection<V> values = map.values();
+ assertNotNull(values);
+ assertEquals(1, values.size());
+
+ map.put(getKeys()[1], getValues()[1]);
+ assertEquals(2, values.size());
+ }
+
+ /**
+ * Test method for 'java.util.Map.values()'.
+ *
+ * @see java.util.Map#values()
+ */
+ public void testValues_viewRemove() {
+ Map<K, V> map = createMap();
+
+ map.put(getKeys()[0], getValues()[0]);
+ map.put(getKeys()[1], getValues()[1]);
+
+ Collection<V> values = map.values();
+ assertNotNull(values);
+ assertEquals(2, values.size());
+
+ map.remove(getKeys()[1]);
+ assertEquals(1, values.size());
+ }
+
+ @Override
+ public boolean useNullKey() {
+ return false;
+ }
+
+ protected Comparator<K> getComparator() {
+ return comparator;
+ }
+
+ protected abstract Object getConflictingKey();
+
+ protected abstract Object getConflictingValue();
+
+ protected boolean isNaturalOrder() {
+ return comparator == null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Map makeEmptyMap() {
+ return createTreeMap();
+ }
+
+ protected void setComparator(Comparator<K> comparator) {
+ this.comparator = comparator;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ setComparator(null);
+ super.setUp();
+ }
+
+ Map<K, V> createMap() {
+ return createSortedMap();
+ }
+
+ SortedMap<K, V> createSortedMap() {
+ SortedMap<K, V> map = createTreeMap();
+ return map;
+ }
+
+ TreeMap<K, V> createTreeMap() {
+ if (isNaturalOrder()) {
+ return new TreeMap<K, V>();
+ } else {
+ return new TreeMap<K, V>(getComparator());
+ }
+ }
+
+ abstract K getGreaterThanMaximumKey();
+
+ abstract K[] getKeys();
+
+ abstract K[] getKeys2();
+
+ abstract K getLessThanMinimumKey();
+
+ abstract V[] getValues();
+
+ abstract V[] getValues2();
+}
diff --git a/user/test/org/apache/commons/collections/TestMap.java b/user/test/org/apache/commons/collections/TestMap.java
index 7c7f97e..b679a3d 100644
--- a/user/test/org/apache/commons/collections/TestMap.java
+++ b/user/test/org/apache/commons/collections/TestMap.java
@@ -15,6 +15,8 @@
*/
package org.apache.commons.collections;
+import junit.framework.AssertionFailedError;
+
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
@@ -262,15 +264,20 @@
"if either the key or value is null.",
keys[i] == null || values[i] == null);
- assertTrue("NullPointerException on null key, but " +
- "useNullKey is not overridden to return false.",
- keys[i] == null || !useNullKey());
-
- assertTrue("NullPointerException on null value, but " +
- "useNullValue is not overridden to return false.",
- values[i] == null || !useNullValue());
-
- assertTrue("Unknown reason for NullPointer.", false);
+ if (keys[i] == null) {
+ if (useNullKey()) {
+ throw new Error("NullPointerException on null key, but " +
+ "useNullKey is not overridden to return false.", exception);
+ }
+ } else if (values[i] == null) {
+ if (useNullValue()) {
+ throw new Error("NullPointerException on null value, but " +
+ "useNullValue is not overridden to return false.", exception);
+ }
+ } else {
+ // Unknown reason for NullPointer.
+ throw exception;
+ }
}
}
assertEquals("size must reflect number of mappings added.",
@@ -520,35 +527,35 @@
* Tests Map.get(Object)
**/
public void testMapGet() {
- resetEmpty();
+ resetEmpty();
- Object[] keys = getSampleKeys();
- Object[] values = getSampleValues();
+ Object[] keys = getSampleKeys();
+ Object[] values = getSampleValues();
- for (int i = 0; i < keys.length; i++) {
- assertTrue("Empty map.get() should return null.",
- map.get(keys[i]) == null);
- }
- verify();
+ for (int i = 0; i < keys.length; i++) {
+ assertTrue("Empty map.get() should return null.",
+ map.get(keys[i]) == null);
+ }
+ verify();
- resetFull();
- for (int i = 0; i < keys.length; i++) {
- assertEquals("Full map.get() should return value from mapping.",
- values[i], map.get(keys[i]));
- }
+ resetFull();
+ for (int i = 0; i < keys.length; i++) {
+ assertEquals("Full map.get() should return value from mapping.",
+ values[i], map.get(keys[i]));
+ }
}
/**
* Tests Map.hashCode()
**/
public void testMapHashCode() {
- resetEmpty();
- assertTrue("Empty maps have different hashCodes.",
- map.hashCode() == confirmed.hashCode());
+ resetEmpty();
+ assertTrue("Empty maps have different hashCodes.",
+ map.hashCode() == confirmed.hashCode());
- resetFull();
- assertTrue("Equal maps have different hashCodes.",
- map.hashCode() == confirmed.hashCode());
+ resetFull();
+ assertTrue("Equal maps have different hashCodes.",
+ map.hashCode() == confirmed.hashCode());
}
/**
@@ -561,131 +568,131 @@
* not return null.
**/
public void testMapToString() {
- resetEmpty();
- assertTrue("Empty map toString() should not return null",
- map.toString() != null);
- verify();
+ resetEmpty();
+ assertTrue("Empty map toString() should not return null",
+ map.toString() != null);
+ verify();
- resetFull();
- assertTrue("Empty map toString() should not return null",
- map.toString() != null);
- verify();
+ resetFull();
+ assertTrue("Empty map toString() should not return null",
+ map.toString() != null);
+ verify();
}
-
-
+
+
/**
* Tests Map.put(Object, Object)
**/
public void testMapPut() {
- if (!isAddRemoveModifiable()) return;
+ if (!isAddRemoveModifiable()) return;
- resetEmpty();
+ resetEmpty();
- Object[] keys = getSampleKeys();
- Object[] values = getSampleValues();
- Object[] newValues = getNewSampleValues();
+ Object[] keys = getSampleKeys();
+ Object[] values = getSampleValues();
+ Object[] newValues = getNewSampleValues();
- for(int i = 0; i < keys.length; i++) {
- Object o = map.put(keys[i], values[i]);
- confirmed.put(keys[i], values[i]);
- verify();
- assertTrue("First map.put should return null", o == null);
- assertTrue("Map should contain key after put",
- map.containsKey(keys[i]));
- assertTrue("Map should contain value after put",
- map.containsValue(values[i]));
- }
-
- for(int i = 0; i < keys.length; i++) {
- Object o = map.put(keys[i], newValues[i]);
- confirmed.put(keys[i], newValues[i]);
- verify();
- assertEquals("Second map.put should return previous value",
- values[i], o);
- assertTrue("Map should still contain key after put",
- map.containsKey(keys[i]));
- assertTrue("Map should contain new value after put",
- map.containsValue(newValues[i]));
+ for(int i = 0; i < keys.length; i++) {
+ Object o = map.put(keys[i], values[i]);
+ confirmed.put(keys[i], values[i]);
+ verify();
+ assertTrue("First map.put should return null", o == null);
+ assertTrue("Map should contain key after put",
+ map.containsKey(keys[i]));
+ assertTrue("Map should contain value after put",
+ map.containsValue(values[i]));
+ }
- // if duplicates are allowed, we're not guarunteed that the value
- // no longer exists, so don't try checking that.
- if(!useDuplicateValues()) {
- assertTrue("Map should not contain old value after second put",
- !map.containsValue(values[i]));
- }
- }
+ for(int i = 0; i < keys.length; i++) {
+ Object o = map.put(keys[i], newValues[i]);
+ confirmed.put(keys[i], newValues[i]);
+ verify();
+ assertEquals("Second map.put should return previous value",
+ values[i], o);
+ assertTrue("Map should still contain key after put",
+ map.containsKey(keys[i]));
+ assertTrue("Map should contain new value after put",
+ map.containsValue(newValues[i]));
+
+ // if duplicates are allowed, we're not guarunteed that the value
+ // no longer exists, so don't try checking that.
+ if(!useDuplicateValues()) {
+ assertTrue("Map should not contain old value after second put",
+ !map.containsValue(values[i]));
+ }
+ }
}
/**
* Tests Map.putAll(Collection)
**/
public void testMapPutAll() {
- if (!isAddRemoveModifiable()) return;
+ if (!isAddRemoveModifiable()) return;
- resetEmpty();
+ resetEmpty();
- Map m2 = makeFullMap();
+ Map m2 = makeFullMap();
- map.putAll(m2);
- confirmed.putAll(m2);
- verify();
+ map.putAll(m2);
+ confirmed.putAll(m2);
+ verify();
- resetEmpty();
+ resetEmpty();
- m2 = new HashMap();
- Object[] keys = getSampleKeys();
- Object[] values = getSampleValues();
- for(int i = 0; i < keys.length; i++) {
- m2.put(keys[i], values[i]);
- }
+ m2 = new HashMap();
+ Object[] keys = getSampleKeys();
+ Object[] values = getSampleValues();
+ for(int i = 0; i < keys.length; i++) {
+ m2.put(keys[i], values[i]);
+ }
- map.putAll(m2);
- confirmed.putAll(m2);
- verify();
+ map.putAll(m2);
+ confirmed.putAll(m2);
+ verify();
}
/**
* Tests Map.remove(Object)
**/
public void testMapRemove() {
- if (!isAddRemoveModifiable()) return;
+ if (!isAddRemoveModifiable()) return;
- resetEmpty();
+ resetEmpty();
- Object[] keys = getSampleKeys();
- Object[] values = getSampleValues();
- for(int i = 0; i < keys.length; i++) {
- Object o = map.remove(keys[i]);
- assertTrue("First map.remove should return null", o == null);
- }
+ Object[] keys = getSampleKeys();
+ Object[] values = getSampleValues();
+ for(int i = 0; i < keys.length; i++) {
+ Object o = map.remove(keys[i]);
+ assertTrue("First map.remove should return null", o == null);
+ }
+ verify();
+
+ resetFull();
+
+ for(int i = 0; i < keys.length; i++) {
+ Object o = map.remove(keys[i]);
+ confirmed.remove(keys[i]);
verify();
- resetFull();
+ assertEquals("map.remove with valid key should return value",
+ values[i], o);
+ }
- for(int i = 0; i < keys.length; i++) {
- Object o = map.remove(keys[i]);
- confirmed.remove(keys[i]);
- verify();
+ Object[] other = getOtherKeys();
- assertEquals("map.remove with valid key should return value",
- values[i], o);
- }
-
- Object[] other = getOtherKeys();
-
- resetFull();
- int size = map.size();
- for (int i = 0; i < other.length; i++) {
- Object o = map.remove(other[i]);
- assertEquals("map.remove for nonexistent key should return null",
- o, null);
- assertEquals("map.remove for nonexistent key should not " +
- "shrink map", size, map.size());
- }
- verify();
+ resetFull();
+ int size = map.size();
+ for (int i = 0; i < other.length; i++) {
+ Object o = map.remove(other[i]);
+ assertEquals("map.remove for nonexistent key should return null",
+ o, null);
+ assertEquals("map.remove for nonexistent key should not " +
+ "shrink map", size, map.size());
+ }
+ verify();
}
diff --git a/user/test/org/apache/commons/collections/TestTreeMap.java b/user/test/org/apache/commons/collections/TestTreeMap.java
new file mode 100644
index 0000000..68ff535
--- /dev/null
+++ b/user/test/org/apache/commons/collections/TestTreeMap.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.commons.collections;
+
+import java.util.TreeMap;
+
+/**
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @version $Id: TestTreeMap.java,v 1.6.2.1 2004/05/22 12:14:05 scolebourne Exp $
+ */
+public abstract class TestTreeMap extends TestMap {
+ // public static void main(String args[])
+ // {
+ // String[] testCaseName = { TestTreeMap.class.getName() };
+ // junit.textui.TestRunner.main(testCaseName);
+ // }
+
+ protected TreeMap map = null;
+
+ public void setUp() {
+ map = (TreeMap) makeEmptyMap();
+ }
+
+ public void testNewMap() {
+ assertTrue("New map is empty", map.isEmpty());
+ assertEquals("New map has size zero", map.size(), 0);
+ }
+
+ public void testSearch() {
+ map.put("first", "First Item");
+ map.put("second", "Second Item");
+ assertEquals("Top item is 'Second Item'", map.get("first"), "First Item");
+ assertEquals("Next Item is 'First Item'", map.get("second"), "Second Item");
+ }
+
+ public boolean useNullKey() {
+ return false;
+ }
+}