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; + } +}