Implemented IdentityHashMap with tests.
Review by: jat (pair prog)
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2322 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/util/AbstractHashMap.java b/user/super/com/google/gwt/emul/java/util/AbstractHashMap.java
new file mode 100644
index 0000000..f775124
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/util/AbstractHashMap.java
@@ -0,0 +1,470 @@
+/*
+ * 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 java.util;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Implementation of Map interface based on a hash table. <a
+ * href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/HashMap.html">[Sun
+ * docs]</a>
+ *
+ * @param <K> key type
+ * @param <V> value type
+ */
+abstract class AbstractHashMap<K, V> extends AbstractMap<K, V> {
+ /*
+ * Implementation notes:
+ *
+ * String keys are stored in a separate map from non-String keys. String keys
+ * are mapped to their values via a JS associative map, stringMap. String keys
+ * could collide with intrinsic properties (like watch, constructor) so we
+ * prepend each key with a ':' inside of stringMap.
+ *
+ * Integer keys are used to index all non-string keys. A key's hashCode is the
+ * index in hashCodeMap which should contain that key. Since several keys may
+ * have the same hash, each value in hashCodeMap is actually an array
+ * containing all entries whose keys share the same hash.
+ */
+ private final class EntrySet extends AbstractSet<Entry<K, V>> {
+
+ @Override
+ public void clear() {
+ AbstractHashMap.this.clear();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
+ Object key = entry.getKey();
+ if (AbstractHashMap.this.containsKey(key)) {
+ Object value = AbstractHashMap.this.get(key);
+ return AbstractHashMap.this.equals(entry.getValue(), value);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Iterator<Entry<K, V>> iterator() {
+ return new EntrySetIterator();
+ }
+
+ @Override
+ public boolean remove(Object entry) {
+ if (contains(entry)) {
+ Object key = ((Map.Entry<?, ?>) entry).getKey();
+ AbstractHashMap.this.remove(key);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int size() {
+ return AbstractHashMap.this.size();
+ }
+ }
+
+ /**
+ * Iterator for <code>EntrySetImpl</code>.
+ */
+ private final class EntrySetIterator implements Iterator<Entry<K, V>> {
+ private final Iterator<Map.Entry<K, V>> iter;
+ private Map.Entry<K, V> last = null;
+
+ /**
+ * Constructor for <code>EntrySetIterator</code>.
+ */
+ public EntrySetIterator() {
+ List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>();
+ if (nullSlotLive) {
+ MapEntryImpl<K, V> entryImpl = new MapEntryImpl<K, V>(null, nullSlot);
+ list.add(entryImpl);
+ }
+ addAllStringEntries(stringMap, list);
+ addAllHashEntries(hashCodeMap, list);
+ this.iter = list.iterator();
+ }
+
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ public Map.Entry<K, V> next() {
+ return last = iter.next();
+ }
+
+ public void remove() {
+ if (last == null) {
+ throw new IllegalStateException("Must call next() before remove().");
+ } else {
+ iter.remove();
+ AbstractHashMap.this.remove(last.getKey());
+ last = null;
+ }
+ }
+ }
+
+ private static native void addAllHashEntries(JavaScriptObject hashCodeMap,
+ Collection<?> dest) /*-{
+ for (var hashCode in hashCodeMap) {
+ // sanity check that it's really an integer
+ if (hashCode == parseInt(hashCode)) {
+ var array = hashCodeMap[hashCode];
+ for (var i = 0, c = array.length; i < c; ++i) {
+ dest.@java.util.Collection::add(Ljava/lang/Object;)(array[i]);
+ }
+ }
+ }
+ }-*/;
+
+ private static native void addAllStringEntries(JavaScriptObject stringMap,
+ Collection<?> dest) /*-{
+ for (var key in stringMap) {
+ // only keys that start with a colon ':' count
+ if (key.charCodeAt(0) == 58) {
+ var value = stringMap[key];
+ var entry = @java.util.MapEntryImpl::create(Ljava/lang/Object;Ljava/lang/Object;)(key.substring(1), value);
+ dest.@java.util.Collection::add(Ljava/lang/Object;)(entry);
+ }
+ }
+ }-*/;
+
+ /**
+ * A map of integral hashCodes onto entries.
+ */
+ private transient JavaScriptObject hashCodeMap;
+
+ /**
+ * This is the slot that holds the value associated with the "null" key.
+ */
+ private transient V nullSlot;
+
+ private transient boolean nullSlotLive;
+
+ private int size;
+
+ /**
+ * A map of Strings onto values.
+ */
+ private transient JavaScriptObject stringMap;
+
+ {
+ clearImpl();
+ }
+
+ public AbstractHashMap() {
+ }
+
+ public AbstractHashMap(int ignored) {
+ // This implementation of HashMap has no need of initial capacities.
+ this(ignored, 0);
+ }
+
+ public AbstractHashMap(int ignored, float alsoIgnored) {
+ // This implementation of HashMap has no need of load factors or capacities.
+ if (ignored < 0 || alsoIgnored < 0) {
+ throw new IllegalArgumentException(
+ "initial capacity was negative or load factor was non-positive");
+ }
+ }
+
+ public AbstractHashMap(Map<? extends K, ? extends V> toBeCopied) {
+ this.putAll(toBeCopied);
+ }
+
+ @Override
+ public void clear() {
+ clearImpl();
+ }
+
+ public abstract Object clone();
+
+ @Override
+ public boolean containsKey(Object key) {
+ return (key == null) ? nullSlotLive : (!(key instanceof String) ? hasHashValue(
+ key, getHashCode(key)) : hasStringValue((String) key));
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ if (nullSlotLive && equals(nullSlot, value)) {
+ return true;
+ } else if (containsStringValue(value)) {
+ return true;
+ } else if (containsHashValue(value)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Set<Map.Entry<K, V>> entrySet() {
+ return new EntrySet();
+ }
+
+ @Override
+ public V get(Object key) {
+ return (key == null) ? nullSlot : (!(key instanceof String) ? getHashValue(
+ key, getHashCode(key)) : getStringValue((String) key));
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return (key == null) ? putNullSlot(value) : (!(key instanceof String)
+ ? putHashValue(key, value, getHashCode(key)) : putStringValue(
+ (String) key, value));
+ }
+
+ @Override
+ public V remove(Object key) {
+ return (key == null) ? removeNullSlot() : (!(key instanceof String) ? removeHashValue(
+ key, getHashCode(key)) : removeStringValue((String) key));
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Subclasses must override to return a whether or not two keys or values are
+ * equal.
+ */
+ protected abstract boolean equals(Object value1, Object value2);
+
+ /**
+ * Subclasses must override to return a hash code for a given key. The key is
+ * guaranteed to be non-null and not a String.
+ */
+ protected abstract int getHashCode(Object key);
+
+ private void clearImpl() {
+ hashCodeMap = JavaScriptObject.createArray();
+ stringMap = JavaScriptObject.createObject();
+ nullSlotLive = false;
+ nullSlot = null;
+ size = 0;
+ }
+
+ /**
+ * Returns true if hashCodeMap contains any Map.Entry whose value is Object
+ * equal to <code>value</code>.
+ */
+ private native boolean containsHashValue(Object value) /*-{
+ var hashCodeMap = this.@java.util.AbstractHashMap::hashCodeMap;
+ for (var hashCode in hashCodeMap) {
+ // sanity check that it's really one of ours
+ if (hashCode == parseInt(hashCode)) {
+ var array = hashCodeMap[hashCode];
+ for (var i = 0, c = array.length; i < c; ++i) {
+ var entry = array[i];
+ var entryValue = entry.@java.util.Map$Entry::getValue()();
+ if (this.@java.util.AbstractHashMap::equalsBridge(Ljava/lang/Object;Ljava/lang/Object;)(value, entryValue)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }-*/;
+
+ /**
+ * Returns true if stringMap contains any key whose value is Object equal to
+ * <code>value</code>.
+ */
+ private native boolean containsStringValue(Object value) /*-{
+ var stringMap = this.@java.util.AbstractHashMap::stringMap;
+ for (var key in stringMap) {
+ // only keys that start with a colon ':' count
+ if (key.charCodeAt(0) == 58) {
+ var entryValue = stringMap[key];
+ if (this.@java.util.AbstractHashMap::equalsBridge(Ljava/lang/Object;Ljava/lang/Object;)(value, entryValue)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }-*/;
+
+ /**
+ * Bridge method from JSNI that keeps us from having to make polymorphic calls
+ * in JSNI. By putting the polymorphism in Java code, the compiler can do a
+ * better job of optimizing in most cases.
+ */
+ @SuppressWarnings("unused")
+ private boolean equalsBridge(Object value1, Object value2) {
+ return equals(value1, value2);
+ }
+
+ /**
+ * Returns the Map.Entry whose key is Object equal to <code>key</code>,
+ * provided that <code>key</code>'s hash code is <code>hashCode</code>;
+ * or <code>null</code> if no such Map.Entry exists at the specified
+ * hashCode.
+ */
+ private native V getHashValue(Object key, int hashCode) /*-{
+ var array = this.@java.util.AbstractHashMap::hashCodeMap[hashCode];
+ if (array) {
+ for (var i = 0, c = array.length; i < c; ++i) {
+ var entry = array[i];
+ var entryKey = entry.@java.util.Map$Entry::getKey()();
+ if (this.@java.util.AbstractHashMap::equalsBridge(Ljava/lang/Object;Ljava/lang/Object;)(key, entryKey)) {
+ return entry.@java.util.Map$Entry::getValue()();
+ }
+ }
+ }
+ return null;
+ }-*/;
+
+ /**
+ * Returns the value for the given key in the stringMap. Returns
+ * <code>null</code> if the specified key does not exist.
+ */
+ private native V getStringValue(String key) /*-{
+ return (_ = this.@java.util.AbstractHashMap::stringMap[':' + key]) == null ? null : _ ;
+ }-*/;
+
+ /**
+ * Returns true if the a key exists in the hashCodeMap that is Object equal to
+ * <code>key</code>, provided that <code>key</code>'s hash code is
+ * <code>hashCode</code>.
+ */
+ private native boolean hasHashValue(Object key, int hashCode) /*-{
+ var array = this.@java.util.AbstractHashMap::hashCodeMap[hashCode];
+ if (array) {
+ for (var i = 0, c = array.length; i < c; ++i) {
+ var entry = array[i];
+ var entryKey = entry.@java.util.Map$Entry::getKey()();
+ if (this.@java.util.AbstractHashMap::equalsBridge(Ljava/lang/Object;Ljava/lang/Object;)(key, entryKey)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }-*/;
+
+ /**
+ * Returns true if the given key exists in the stringMap.
+ */
+ private native boolean hasStringValue(String key) /*-{
+ return (':' + key) in this.@java.util.AbstractHashMap::stringMap;
+ }-*/;
+
+ /**
+ * Sets the specified key to the specified value in the hashCodeMap. Returns
+ * the value previously at that key. Returns <code>null</code> if the
+ * specified key did not exist.
+ */
+ private native V putHashValue(K key, V value, int hashCode) /*-{
+ var array = this.@java.util.AbstractHashMap::hashCodeMap[hashCode];
+ if (array) {
+ for (var i = 0, c = array.length; i < c; ++i) {
+ var entry = array[i];
+ var entryKey = entry.@java.util.Map$Entry::getKey()();
+ if (this.@java.util.AbstractHashMap::equalsBridge(Ljava/lang/Object;Ljava/lang/Object;)(key, entryKey)) {
+ // Found an exact match, just update the existing entry
+ var previous = entry.@java.util.Map$Entry::getValue()();
+ entry.@java.util.Map$Entry::setValue(Ljava/lang/Object;)(value);
+ return previous;
+ }
+ }
+ } else {
+ array = this.@java.util.AbstractHashMap::hashCodeMap[hashCode] = [];
+ }
+ var entry = @java.util.MapEntryImpl::create(Ljava/lang/Object;Ljava/lang/Object;)(key, value);
+ array.push(entry);
+ ++this.@java.util.AbstractHashMap::size;
+ return null;
+ }-*/;
+
+ private V putNullSlot(V value) {
+ V result = nullSlot;
+ nullSlot = value;
+ if (!nullSlotLive) {
+ nullSlotLive = true;
+ ++size;
+ }
+ return result;
+ }
+
+ /**
+ * Sets the specified key to the specified value in the stringMap. Returns the
+ * value previously at that key. Returns <code>null</code> if the specified
+ * key did not exist.
+ */
+ private native V putStringValue(String key, V value) /*-{
+ key = ':' + key;
+ var result = this.@java.util.AbstractHashMap::stringMap[key];
+ this.@java.util.AbstractHashMap::stringMap[key] = value;
+ return (result === undefined) ?
+ (++this.@java.util.AbstractHashMap::size, null) : result;
+ }-*/;
+
+ /**
+ * Removes the pair whose key is Object equal to <code>key</code> from
+ * <code>hashCodeMap</code>, provided that <code>key</code>'s hash code
+ * is <code>hashCode</code>. Returns the value that was associated with the
+ * removed key, or null if no such key existed.
+ */
+ private native V removeHashValue(Object key, int hashCode) /*-{
+ var array = this.@java.util.AbstractHashMap::hashCodeMap[hashCode];
+ if (array) {
+ for (var i = 0, c = array.length; i < c; ++i) {
+ var entry = array[i];
+ var entryKey = entry.@java.util.Map$Entry::getKey()();
+ if (this.@java.util.AbstractHashMap::equalsBridge(Ljava/lang/Object;Ljava/lang/Object;)(key, entryKey)) {
+ if (array.length == 1) {
+ // remove the whole array
+ delete this.@java.util.AbstractHashMap::hashCodeMap[hashCode];
+ } else {
+ // splice out the entry we're removing
+ array.splice(i, 1);
+ }
+ --this.@java.util.AbstractHashMap::size;
+ return entry.@java.util.Map$Entry::getValue()();
+ }
+ }
+ }
+ return null;
+ }-*/;
+
+ private V removeNullSlot() {
+ V result = nullSlot;
+ nullSlot = null;
+ if (nullSlotLive) {
+ nullSlotLive = false;
+ --size;
+ }
+ return result;
+ }
+
+ /**
+ * Removes the specified key from the stringMap and returns the value that was
+ * previously there. Returns <code>null</code> if the specified key
+ * does not exist.
+ */
+ private native V removeStringValue(String key) /*-{
+ key = ':' + key;
+ var result = this.@java.util.AbstractHashMap::stringMap[key];
+ return (result === undefined) ? null :
+ (--this.@java.util.AbstractHashMap::size,
+ delete this.@java.util.AbstractHashMap::stringMap[key],
+ result);
+ }-*/;
+}
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 7a99dc3..e85ba13 100644
--- a/user/super/com/google/gwt/emul/java/util/AbstractMap.java
+++ b/user/super/com/google/gwt/emul/java/util/AbstractMap.java
@@ -60,16 +60,17 @@
return false;
}
Map<?, ?> otherMap = (Map<?, ?>) obj;
- Set<K> keys = keySet();
- Set<?> otherKeys = otherMap.keySet();
- if (!keys.equals(otherKeys)) {
+ if (size() != otherMap.size()) {
return false;
}
- for (Iterator<K> iter = keys.iterator(); iter.hasNext();) {
- K key = iter.next();
- V value = get(key);
- Object otherValue = otherMap.get(key);
- if (value == null ? otherValue != null : !value.equals(otherValue)) {
+
+ for (Entry<?, ?> entry : otherMap.entrySet()) {
+ Object otherKey = entry.getKey();
+ Object otherValue = entry.getValue();
+ if (!containsKey(otherKey)) {
+ return false;
+ }
+ if (!Utility.equalsWithNullCheck(otherValue, get(otherKey))) {
return false;
}
}
@@ -84,8 +85,7 @@
@Override
public int hashCode() {
int hashCode = 0;
- for (Iterator<Entry<K, V>> iter = entrySet().iterator(); iter.hasNext();) {
- Entry<K, V> entry = iter.next();
+ for (Entry<K, V> entry : entrySet()) {
hashCode += entry.hashCode();
}
return hashCode;
diff --git a/user/super/com/google/gwt/emul/java/util/HashMap.java b/user/super/com/google/gwt/emul/java/util/HashMap.java
index 8cf4abb..713057d 100644
--- a/user/super/com/google/gwt/emul/java/util/HashMap.java
+++ b/user/super/com/google/gwt/emul/java/util/HashMap.java
@@ -1,452 +1,65 @@
-/*
- * Copyright 2007 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 java.util;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-import java.io.Serializable;
-
-/**
- * Implementation of Map interface based on a hash table. <a
- * href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/HashMap.html">[Sun
- * docs]</a>
- *
- * @param <K> key type
- * @param <V> value type
- */
-public class HashMap<K, V> extends AbstractMap<K, V> implements Serializable {
- /*
- * Implementation notes:
- *
- * String keys are stored in a separate map from non-String keys. String keys
- * are mapped to their values via a JS associative map, stringMap. String keys
- * could collide with intrinsic properties (like watch, constructor) so we
- * prepend each key with a ':' inside of stringMap.
- *
- * Integer keys are used to index all non-string keys. A key's hashCode is the
- * index in hashCodeMap which should contain that key. Since several keys may
- * have the same hash, each value in hashCodeMap is actually an array
- * containing all entries whose keys share the same hash.
- */
- private final class EntrySet extends AbstractSet<Entry<K, V>> {
-
- @Override
- public void clear() {
- HashMap.this.clear();
- }
-
- @Override
- public boolean contains(Object o) {
- if (o instanceof Map.Entry) {
- Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
- Object key = entry.getKey();
- if (HashMap.this.containsKey(key)) {
- Object value = HashMap.this.get(key);
- return Utility.equalsWithNullCheck(entry.getValue(), value);
- }
- }
- return false;
- }
-
- @Override
- public Iterator<Entry<K, V>> iterator() {
- return new EntrySetIterator();
- }
-
- @Override
- public boolean remove(Object entry) {
- if (contains(entry)) {
- Object key = ((Map.Entry<?, ?>) entry).getKey();
- HashMap.this.remove(key);
- return true;
- }
- return false;
- }
-
- @Override
- public int size() {
- return HashMap.this.size();
- }
- }
-
- /**
- * Iterator for <code>EntrySetImpl</code>.
- */
- private final class EntrySetIterator implements Iterator<Entry<K, V>> {
- private final Iterator<Map.Entry<K, V>> iter;
- private Map.Entry<K, V> last = null;
-
- /**
- * Constructor for <code>EntrySetIterator</code>.
- */
- public EntrySetIterator() {
- List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>();
- if (nullSlotLive) {
- MapEntryImpl<K, V> entryImpl = new MapEntryImpl<K, V>(null, nullSlot);
- list.add(entryImpl);
- }
- addAllStringEntries(stringMap, list);
- addAllHashEntries(hashCodeMap, list);
- this.iter = list.iterator();
- }
-
- public boolean hasNext() {
- return iter.hasNext();
- }
-
- public Map.Entry<K, V> next() {
- return last = iter.next();
- }
-
- public void remove() {
- if (last == null) {
- throw new IllegalStateException("Must call next() before remove().");
- } else {
- iter.remove();
- HashMap.this.remove(last.getKey());
- last = null;
- }
- }
- }
-
- private static native void addAllHashEntries(JavaScriptObject hashCodeMap,
- Collection<?> dest) /*-{
- for (var hashCode in hashCodeMap) {
- // sanity check that it's really an integer
- if (hashCode == parseInt(hashCode)) {
- var array = hashCodeMap[hashCode];
- for (var i = 0, c = array.length; i < c; ++i) {
- dest.@java.util.Collection::add(Ljava/lang/Object;)(array[i]);
- }
- }
- }
- }-*/;
-
- private static native void addAllStringEntries(JavaScriptObject stringMap,
- Collection<?> dest) /*-{
- for (var key in stringMap) {
- // only keys that start with a colon ':' count
- if (key.charCodeAt(0) == 58) {
- var value = stringMap[key];
- var entry = @java.util.MapEntryImpl::create(Ljava/lang/Object;Ljava/lang/Object;)(key.substring(1), value);
- dest.@java.util.Collection::add(Ljava/lang/Object;)(entry);
- }
- }
- }-*/;
-
- /**
- * Returns true if hashCodeMap contains any Map.Entry whose value is Object
- * equal to <code>value</code>.
- */
- private static native boolean containsHashValue(JavaScriptObject hashCodeMap,
- Object value) /*-{
- for (var hashCode in hashCodeMap) {
- // sanity check that it's really one of ours
- if (hashCode == parseInt(hashCode)) {
- var array = hashCodeMap[hashCode];
- for (var i = 0, c = array.length; i < c; ++i) {
- var entry = array[i];
- var entryValue = entry.@java.util.Map$Entry::getValue()();
- if (@java.util.Utility::equalsWithNullCheck(Ljava/lang/Object;Ljava/lang/Object;)(value, entryValue)) {
- return true;
- }
- }
- }
- }
- return false;
- }-*/;
-
- /**
- * Returns true if stringMap contains any key whose value is Object equal to
- * <code>value</code>.
- */
- private static native boolean containsStringValue(JavaScriptObject stringMap,
- Object value) /*-{
- for (var key in stringMap) {
- // only keys that start with a colon ':' count
- if (key.charCodeAt(0) == 58) {
- var entryValue = stringMap[key];
- if (@java.util.Utility::equalsWithNullCheck(Ljava/lang/Object;Ljava/lang/Object;)(value, entryValue)) {
- return true;
- }
- }
- }
- return false;
- }-*/;
-
- /**
- * A map of integral hashCodes onto entries.
- */
- private transient JavaScriptObject hashCodeMap;
-
- /**
- * This is the slot that holds the value associated with the "null" key.
- */
- private transient V nullSlot;
-
- private transient boolean nullSlotLive;
-
- private int size;
-
- /**
- * A map of Strings onto values.
- */
- private transient JavaScriptObject stringMap;
-
- {
- clearImpl();
- }
-
- public HashMap() {
- }
-
- public HashMap(int ignored) {
- // This implementation of HashMap has no need of initial capacities.
- this(ignored, 0);
- }
-
- public HashMap(int ignored, float alsoIgnored) {
- // This implementation of HashMap has no need of load factors or capacities.
- if (ignored < 0 || alsoIgnored < 0) {
- throw new IllegalArgumentException(
- "initial capacity was negative or load factor was non-positive");
- }
- }
-
- public HashMap(Map<? extends K, ? extends V> toBeCopied) {
- this.putAll(toBeCopied);
- }
-
- @Override
- public void clear() {
- clearImpl();
- }
-
- public Object clone() {
- return new HashMap<K, V>(this);
- }
-
- @Override
- public boolean containsKey(Object key) {
- return (key == null) ? nullSlotLive : (!(key instanceof String) ? hasHashValue(
- key, key.hashCode()) : hasStringValue((String) key));
- }
-
- @Override
- public boolean containsValue(Object value) {
- if (nullSlotLive && Utility.equalsWithNullCheck(nullSlot, value)) {
- return true;
- } else if (containsStringValue(stringMap, value)) {
- return true;
- } else if (containsHashValue(hashCodeMap, value)) {
- return true;
- }
- return false;
- }
-
- @Override
- public Set<Map.Entry<K, V>> entrySet() {
- return new EntrySet();
- }
-
- @Override
- public V get(Object key) {
- return (key == null) ? nullSlot : (!(key instanceof String) ? getHashValue(
- key, key.hashCode()) : getStringValue((String) key));
- }
-
- @Override
- public V put(K key, V value) {
- return (key == null) ? putNullSlot(value) : (!(key instanceof String)
- ? putHashValue(key, value, key.hashCode()) : putStringValue(
- (String) key, value));
- }
-
- @Override
- public V remove(Object key) {
- return (key == null) ? removeNullSlot() : (!(key instanceof String) ? removeHashValue(
- key, key.hashCode()) : removeStringValue((String) key));
- }
-
- @Override
- public int size() {
- return size;
- }
-
- private void clearImpl() {
- hashCodeMap = JavaScriptObject.createArray();
- stringMap = JavaScriptObject.createObject();
- nullSlotLive = false;
- nullSlot = null;
- size = 0;
- }
-
- /**
- * Returns the Map.Entry whose key is Object equal to <code>key</code>,
- * provided that <code>key</code>'s hash code is <code>hashCode</code>;
- * or <code>null</code> if no such Map.Entry exists at the specified
- * hashCode.
- */
- private native V getHashValue(Object key, int hashCode) /*-{
- var array = this.@java.util.HashMap::hashCodeMap[hashCode];
- if (array) {
- for (var i = 0, c = array.length; i < c; ++i) {
- var entry = array[i];
- var entryKey = entry.@java.util.Map$Entry::getKey()();
- if (@java.util.Utility::equalsWithNullCheck(Ljava/lang/Object;Ljava/lang/Object;)(key, entryKey)) {
- return entry.@java.util.Map$Entry::getValue()();
- }
- }
- }
- return null;
- }-*/;
-
- /**
- * Returns the value for the given key in the stringMap. Returns
- * <code>null</code> if the specified key does not exist.
- */
- private native V getStringValue(String key) /*-{
- return (_ = this.@java.util.HashMap::stringMap[':' + key]) == null ? null : _ ;
- }-*/;
-
- /**
- * Returns true if the a key exists in the hashCodeMap that is Object equal to
- * <code>key</code>, provided that <code>key</code>'s hash code is
- * <code>hashCode</code>.
- */
- private native boolean hasHashValue(Object key, int hashCode) /*-{
- var array = this.@java.util.HashMap::hashCodeMap[hashCode];
- if (array) {
- for (var i = 0, c = array.length; i < c; ++i) {
- var entry = array[i];
- var entryKey = entry.@java.util.Map$Entry::getKey()();
- if (@java.util.Utility::equalsWithNullCheck(Ljava/lang/Object;Ljava/lang/Object;)(key, entryKey)) {
- return true;
- }
- }
- }
- return false;
- }-*/;
-
- /**
- * Returns true if the given key exists in the stringMap.
- */
- private native boolean hasStringValue(String key) /*-{
- return (':' + key) in this.@java.util.HashMap::stringMap;
- }-*/;
-
- /**
- * Sets the specified key to the specified value in the hashCodeMap. Returns
- * the value previously at that key. Returns <code>null</code> if the
- * specified key did not exist.
- */
- private native V putHashValue(K key, V value, int hashCode) /*-{
- var array = this.@java.util.HashMap::hashCodeMap[hashCode];
- if (array) {
- for (var i = 0, c = array.length; i < c; ++i) {
- var entry = array[i];
- var entryKey = entry.@java.util.Map$Entry::getKey()();
- if (@java.util.Utility::equalsWithNullCheck(Ljava/lang/Object;Ljava/lang/Object;)(key, entryKey)) {
- // Found an exact match, just update the existing entry
- var previous = entry.@java.util.Map$Entry::getValue()();
- entry.@java.util.Map$Entry::setValue(Ljava/lang/Object;)(value);
- return previous;
- }
- }
- } else {
- array = this.@java.util.HashMap::hashCodeMap[hashCode] = [];
- }
- var entry = @java.util.MapEntryImpl::create(Ljava/lang/Object;Ljava/lang/Object;)(key, value);
- array.push(entry);
- ++this.@java.util.HashMap::size;
- return null;
- }-*/;
-
- private V putNullSlot(V value) {
- V result = nullSlot;
- nullSlot = value;
- if (!nullSlotLive) {
- nullSlotLive = true;
- ++size;
- }
- return result;
- }
-
- /**
- * Sets the specified key to the specified value in the stringMap. Returns the
- * value previously at that key. Returns <code>null</code> if the specified
- * key did not exist.
- */
- private native V putStringValue(String key, V value) /*-{
- key = ':' + key;
- var result = this.@java.util.HashMap::stringMap[key];
- this.@java.util.HashMap::stringMap[key] = value;
- return (result === undefined) ?
- (++this.@java.util.HashMap::size, null) : result;
- }-*/;
-
- /**
- * Removes the pair whose key is Object equal to <code>key</code> from
- * <code>hashCodeMap</code>, provided that <code>key</code>'s hash code
- * is <code>hashCode</code>. Returns the value that was associated with the
- * removed key, or null if no such key existed.
- */
- private native V removeHashValue(Object key, int hashCode) /*-{
- var array = this.@java.util.HashMap::hashCodeMap[hashCode];
- if (array) {
- for (var i = 0, c = array.length; i < c; ++i) {
- var entry = array[i];
- var entryKey = entry.@java.util.Map$Entry::getKey()();
- if (@java.util.Utility::equalsWithNullCheck(Ljava/lang/Object;Ljava/lang/Object;)(key, entryKey)) {
- if (array.length == 1) {
- // remove the whole array
- delete this.@java.util.HashMap::hashCodeMap[hashCode];
- } else {
- // splice out the entry we're removing
- array.splice(i, 1);
- }
- --this.@java.util.HashMap::size;
- return entry.@java.util.Map$Entry::getValue()();
- }
- }
- }
- return null;
- }-*/;
-
- private V removeNullSlot() {
- V result = nullSlot;
- nullSlot = null;
- if (nullSlotLive) {
- nullSlotLive = false;
- --size;
- }
- return result;
- }
-
- /**
- * Removes the specified key from the stringMap and returns the value that was
- * previously there. Returns <code>null</code> if the specified key
- * does not exist.
- */
- private native V removeStringValue(String key) /*-{
- key = ':' + key;
- var result = this.@java.util.HashMap::stringMap[key];
- return (result === undefined) ? null :
- (--this.@java.util.HashMap::size,
- delete this.@java.util.HashMap::stringMap[key],
- result);
- }-*/;
-}
+/*
+ * 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 java.util;
+
+import java.io.Serializable;
+
+/**
+ * Implementation of Map interface based on a hash table. <a
+ * href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/HashMap.html">[Sun
+ * docs]</a>
+ *
+ * @param <K> key type
+ * @param <V> value type
+ */
+public class HashMap<K, V> extends AbstractHashMap<K, V> implements Cloneable,
+ Serializable {
+
+ public HashMap() {
+ }
+
+ public HashMap(int ignored) {
+ // This implementation of HashMap has no need of initial capacities.
+ this(ignored, 0);
+ }
+
+ public HashMap(int ignored, float alsoIgnored) {
+ // This implementation of HashMap has no need of load factors or capacities.
+ if (ignored < 0 || alsoIgnored < 0) {
+ throw new IllegalArgumentException(
+ "initial capacity was negative or load factor was non-positive");
+ }
+ }
+
+ public HashMap(Map<? extends K, ? extends V> toBeCopied) {
+ super(toBeCopied);
+ }
+
+ @Override
+ public Object clone() {
+ return new HashMap<K, V>(this);
+ }
+
+ @Override
+ protected boolean equals(Object value1, Object value2) {
+ return Utility.equalsWithNullCheck(value1, value2);
+ }
+
+ @Override
+ protected int getHashCode(Object key) {
+ return key.hashCode();
+ }
+}
diff --git a/user/super/com/google/gwt/emul/java/util/IdentityHashMap.java b/user/super/com/google/gwt/emul/java/util/IdentityHashMap.java
index cc30614..d95b8f7 100644
--- a/user/super/com/google/gwt/emul/java/util/IdentityHashMap.java
+++ b/user/super/com/google/gwt/emul/java/util/IdentityHashMap.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,7 +15,10 @@
*/
package java.util;
+import com.google.gwt.core.client.impl.Impl;
+
import java.io.Serializable;
+import java.util.Map.Entry;
/**
* Map using reference equality on keys. <a
@@ -25,45 +28,71 @@
* @param <K> key type
* @param <V> value type
*/
-public class IdentityHashMap<K, V> extends AbstractMap<K, V> implements
+public class IdentityHashMap<K, V> extends AbstractHashMap<K, V> implements
Map<K, V>, Cloneable, Serializable {
public IdentityHashMap() {
- this(10);
}
- public IdentityHashMap(int expectedMaxSize) {
- // TODO(jat): implement
- throw new UnsupportedOperationException("IdentityHashMap not implemented");
+ public IdentityHashMap(int ignored) {
+ // This implementation of HashMap has no need of load factors or capacities.
+ if (ignored < 0) {
+ throw new IllegalArgumentException("initial capacity was negative");
+ }
}
- public IdentityHashMap(Map<? extends K, ? extends V> map) {
- this(map.size());
- putAll(map);
+ public IdentityHashMap(Map<? extends K, ? extends V> toBeCopied) {
+ super(toBeCopied);
}
@Override
- public Set<java.util.Map.Entry<K, V>> entrySet() {
- // TODO(jat): implement
- return null;
+ public Object clone() {
+ return new IdentityHashMap<K, V>(this);
}
@Override
- public V get(Object key) {
- // TODO(jat): implement
- return null;
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof Map)) {
+ return false;
+ }
+ Map<?, ?> otherMap = (Map<?, ?>) obj;
+ if (size() != otherMap.size()) {
+ return false;
+ }
+
+ for (Entry<?, ?> entry : otherMap.entrySet()) {
+ Object otherKey = entry.getKey();
+ Object otherValue = entry.getValue();
+ if (!containsKey(otherKey)) {
+ return false;
+ }
+ if (otherValue != get(otherKey)) {
+ return false;
+ }
+ }
+ return true;
}
@Override
- public V put(K key, V value) {
- // TODO(jat): implement
- return null;
+ public int hashCode() {
+ int hashCode = 0;
+ for (Entry<K, V> entry : entrySet()) {
+ hashCode += System.identityHashCode(entry.getKey());
+ hashCode += System.identityHashCode(entry.getValue());
+ }
+ return hashCode;
}
@Override
- public V remove(Object key) {
- // TODO(jat): implement
- return null;
+ protected boolean equals(Object value1, Object value2) {
+ return value1 == value2;
}
+ @Override
+ protected int getHashCode(Object key) {
+ return Impl.getHashCode(key);
+ }
}
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java
index 731a474..002d560 100644
--- a/user/test/com/google/gwt/emultest/EmulSuite.java
+++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -38,6 +38,7 @@
import com.google.gwt.emultest.java.util.EnumSetTest;
import com.google.gwt.emultest.java.util.HashMapTest;
import com.google.gwt.emultest.java.util.HashSetTest;
+import com.google.gwt.emultest.java.util.IdentityHashMapTest;
import com.google.gwt.emultest.java.util.StackTest;
import com.google.gwt.junit.tools.GWTTestSuite;
@@ -77,6 +78,7 @@
suite.addTestSuite(EnumSetTest.class);
suite.addTestSuite(HashMapTest.class);
suite.addTestSuite(HashSetTest.class);
+ suite.addTestSuite(IdentityHashMapTest.class);
suite.addTestSuite(StackTest.class);
// $JUnit-END$
diff --git a/user/test/com/google/gwt/emultest/java/util/IdentityHashMapTest.java b/user/test/com/google/gwt/emultest/java/util/IdentityHashMapTest.java
new file mode 100644
index 0000000..6bddf46
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/IdentityHashMapTest.java
@@ -0,0 +1,709 @@
+/*
+ * Copyright 2006 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 org.apache.commons.collections.TestMap;
+
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+/**
+ * Tests <code>IdentityHashMap</code>.
+ */
+public class IdentityHashMapTest extends TestMap {
+
+ /**
+ * A class that is equal to all other instances of itself; used to ensure that
+ * identity rather than equality is being checked.
+ */
+ private static class Foo {
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Foo;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+ }
+
+ private static final int CAPACITY_16 = 16;
+ private static final int CAPACITY_NEG_ONE_HALF = -1;
+ private static final int CAPACITY_ZERO = 0;
+ private static final Integer INTEGER_1 = new Integer(1);
+ private static final Integer INTEGER_11 = new Integer(11);
+ private static final Integer INTEGER_2 = new Integer(2);
+ private static final Integer INTEGER_22 = new Integer(22);
+ private static final Integer INTEGER_3 = new Integer(3);
+ private static final Integer INTEGER_33 = new Integer(33);
+ private static final Integer INTEGER_ZERO_KEY = new Integer(0);
+ private static final String INTEGER_ZERO_VALUE = "integer zero";
+ private static final String KEY_1 = "key1";
+ private static final String KEY_2 = "key2";
+ private static final String KEY_3 = "key3";
+ private static final String KEY_4 = "key4";
+ private static final String KEY_KEY = "key";
+ private static final String KEY_TEST_CONTAINS_KEY = "testContainsKey";
+ private static final String KEY_TEST_CONTAINS_VALUE = "testContainsValue";
+ private static final String KEY_TEST_ENTRY_SET = "testEntrySet";
+ private static final String KEY_TEST_GET = "testGet";
+ private static final String KEY_TEST_KEY_SET = "testKeySet";
+ private static final String KEY_TEST_PUT = "testPut";
+ private static final String KEY_TEST_REMOVE = "testRemove";
+ private static final float LOAD_FACTOR_NEG_ONE = -1.0F;
+ private static final float LOAD_FACTOR_ONE_HALF = 0.5F;
+ private static final float LOAD_FACTOR_ONE_TENTH = 0.1F;
+ private static final float LOAD_FACTOR_ZERO = 0.0F;
+ private static final Object ODD_ZERO_KEY = new Object() {
+ public int hashCode() {
+ return 0;
+ }
+ };
+ private static final String ODD_ZERO_VALUE = "odd zero";
+ private static final int SIZE_ONE = 1;
+ private static final int SIZE_THREE = 3;
+ private static final int SIZE_TWO = 2;
+ private static final int SIZE_ZERO = 0;
+ private static final String STRING_ZERO_KEY = "0";
+ private static final String STRING_ZERO_VALUE = "string zero";
+ private static final String VALUE_1 = "val1";
+ private static final String VALUE_2 = "val2";
+ private static final String VALUE_3 = "val3";
+ private static final String VALUE_4 = "val4";
+ private static final String VALUE_TEST_CONTAINS_DOES_NOT_EXIST = "does not exist";
+ private static final Integer VALUE_TEST_CONTAINS_KEY = new Integer(5);
+ private static final String VALUE_TEST_ENTRY_SET_1 = KEY_TEST_ENTRY_SET
+ + " - value1";
+ private static final String VALUE_TEST_ENTRY_SET_2 = KEY_TEST_ENTRY_SET
+ + " - value2";
+ private static final String VALUE_TEST_GET = KEY_TEST_GET + " - Value";
+ private static final String VALUE_TEST_KEY_SET = KEY_TEST_KEY_SET
+ + " - value";
+ private static final String VALUE_TEST_PUT_1 = KEY_TEST_PUT + " - value 1";
+ private static final String VALUE_TEST_PUT_2 = KEY_TEST_PUT + " - value 2";
+ private static final String VALUE_TEST_REMOVE = KEY_TEST_REMOVE + " - value";
+ private static final String VALUE_VAL = "value";
+
+ /**
+ * Check the state of a newly constructed, empty IdentityHashMap.
+ *
+ * @param hashMap
+ */
+ private static void checkEmptyHashMapAssumptions(IdentityHashMap hashMap) {
+ assertNotNull(hashMap);
+ assertTrue(hashMap.isEmpty());
+
+ assertNotNull(hashMap.values());
+ assertTrue(hashMap.values().isEmpty());
+ assertTrue(hashMap.values().size() == 0);
+
+ assertNotNull(hashMap.keySet());
+ assertTrue(hashMap.keySet().isEmpty());
+ assertTrue(hashMap.keySet().size() == 0);
+
+ assertNotNull(hashMap.entrySet());
+ assertTrue(hashMap.entrySet().isEmpty());
+ assertTrue(hashMap.entrySet().size() == 0);
+
+ assertNotNull(hashMap.entrySet().iterator());
+ assertFalse(hashMap.entrySet().iterator().hasNext());
+ }
+
+ public String getModuleName() {
+ return "com.google.gwt.emultest.EmulSuite";
+ }
+
+ public void testAddWatch() {
+ IdentityHashMap m = new IdentityHashMap();
+ m.put("watch", "watch");
+ assertEquals(m.get("watch"), "watch");
+ }
+
+ public void testAddEqualKeys() {
+ final IdentityHashMap expected = new IdentityHashMap();
+ assertEquals(expected.size(), 0);
+ iterateThrough(expected);
+ expected.put(new Long(45), new Object());
+ assertEquals(expected.size(), 1);
+ iterateThrough(expected);
+ expected.put(new Integer(45), new Object());
+ assertNotSame(new Integer(45), new Long(45));
+ assertEquals(expected.size(), 2);
+ iterateThrough(expected);
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.clear()'
+ */
+ public void testClear() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ hashMap.put("Hello", "Bye");
+ assertFalse(hashMap.isEmpty());
+ assertTrue(hashMap.size() == SIZE_ONE);
+
+ hashMap.clear();
+ assertTrue(hashMap.isEmpty());
+ assertTrue(hashMap.size() == 0);
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.clone()'
+ */
+ public void testClone() {
+ IdentityHashMap srcMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(srcMap);
+
+ // Check empty clone behavior
+ IdentityHashMap dstMap = (IdentityHashMap) srcMap.clone();
+ assertNotNull(dstMap);
+ assertEquals(dstMap.size(), srcMap.size());
+ // assertTrue(dstMap.values().toArray().equals(srcMap.values().toArray()));
+ assertTrue(dstMap.keySet().equals(srcMap.keySet()));
+ assertTrue(dstMap.entrySet().equals(srcMap.entrySet()));
+
+ // Check non-empty clone behavior
+ srcMap.put(KEY_1, VALUE_1);
+ srcMap.put(KEY_2, VALUE_2);
+ srcMap.put(KEY_3, VALUE_3);
+ dstMap = (IdentityHashMap) srcMap.clone();
+ assertNotNull(dstMap);
+ assertEquals(dstMap.size(), srcMap.size());
+
+ assertTrue(dstMap.keySet().equals(srcMap.keySet()));
+
+ assertTrue(dstMap.entrySet().equals(srcMap.entrySet()));
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.containsKey(Object)'
+ */
+ public void testContainsKey() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ assertFalse(hashMap.containsKey(KEY_TEST_CONTAINS_KEY));
+ hashMap.put(KEY_TEST_CONTAINS_KEY, VALUE_TEST_CONTAINS_KEY);
+ assertTrue(hashMap.containsKey(KEY_TEST_CONTAINS_KEY));
+ assertFalse(hashMap.containsKey(VALUE_TEST_CONTAINS_DOES_NOT_EXIST));
+
+ assertFalse(hashMap.containsKey(null));
+ hashMap.put(null, VALUE_TEST_CONTAINS_KEY);
+ assertTrue(hashMap.containsKey(null));
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.containsValue(Object)'
+ */
+ public void testContainsValue() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ assertFalse("check contains of empty map",
+ hashMap.containsValue(VALUE_TEST_CONTAINS_KEY));
+ hashMap.put(KEY_TEST_CONTAINS_VALUE, VALUE_TEST_CONTAINS_KEY);
+ assertTrue("check contains of map with element",
+ hashMap.containsValue(VALUE_TEST_CONTAINS_KEY));
+ assertFalse("check contains of map other element",
+ hashMap.containsValue(VALUE_TEST_CONTAINS_DOES_NOT_EXIST));
+
+ if (useNullValue()) {
+ assertFalse(hashMap.containsValue(null));
+ }
+ hashMap.put(KEY_TEST_CONTAINS_VALUE, null);
+ assertTrue(hashMap.containsValue(null));
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.entrySet()'
+ */
+ public void testEntrySet() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ Set entrySet = hashMap.entrySet();
+ assertNotNull(entrySet);
+
+ // Check that the entry set looks right
+ hashMap.put(KEY_TEST_ENTRY_SET, VALUE_TEST_ENTRY_SET_1);
+ entrySet = hashMap.entrySet();
+ assertEquals(entrySet.size(), SIZE_ONE);
+ Iterator itSet = entrySet.iterator();
+ Map.Entry entry = (Map.Entry) itSet.next();
+ assertEquals(entry.getKey(), KEY_TEST_ENTRY_SET);
+ assertEquals(entry.getValue(), VALUE_TEST_ENTRY_SET_1);
+
+ // Check that entries in the entrySet are update correctly on overwrites
+ hashMap.put(KEY_TEST_ENTRY_SET, VALUE_TEST_ENTRY_SET_2);
+ entrySet = hashMap.entrySet();
+ assertEquals(entrySet.size(), SIZE_ONE);
+ itSet = entrySet.iterator();
+ entry = (Map.Entry) itSet.next();
+ assertEquals(entry.getKey(), KEY_TEST_ENTRY_SET);
+ assertEquals(entry.getValue(), VALUE_TEST_ENTRY_SET_2);
+
+ // Check that entries are updated on removes
+ hashMap.remove(KEY_TEST_ENTRY_SET);
+ checkEmptyHashMapAssumptions(hashMap);
+ }
+
+ /*
+ * Used to test the entrySet remove method.
+ */
+ public void testEntrySetRemove() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ hashMap.put("A", "B");
+ IdentityHashMap dummy = new IdentityHashMap();
+ dummy.put("A", "b");
+ Entry bogus = (Entry) dummy.entrySet().iterator().next();
+ Set entrySet = hashMap.entrySet();
+ boolean removed = entrySet.remove(bogus);
+ assertEquals(removed, false);
+ assertEquals(hashMap.get("A"), "B");
+ }
+
+ /*
+ * Test method for 'java.util.AbstractMap.equals(Object)'
+ */
+ public void testEquals() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ hashMap.put(KEY_KEY, VALUE_VAL);
+
+ IdentityHashMap copyMap = (IdentityHashMap) hashMap.clone();
+
+ assertTrue(hashMap.equals(copyMap));
+ hashMap.put(VALUE_VAL, KEY_KEY);
+ assertFalse(hashMap.equals(copyMap));
+ }
+
+ /*
+ * Test method for 'java.lang.Object.finalize()'.
+ */
+ public void testFinalize() {
+ // no tests for finalize
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.get(Object)'.
+ */
+ public void testGet() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ assertNull(hashMap.get(KEY_TEST_GET));
+ hashMap.put(KEY_TEST_GET, VALUE_TEST_GET);
+ assertNotNull(hashMap.get(KEY_TEST_GET));
+
+ assertNull(hashMap.get(null));
+ hashMap.put(null, VALUE_TEST_GET);
+ assertNotNull(hashMap.get(null));
+
+ hashMap.put(null, null);
+ assertNull(hashMap.get(null));
+ }
+
+ /*
+ * Test method for 'java.util.AbstractMap.hashCode()'.
+ */
+ public void testHashCode() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ // Check that hashCode changes
+ int hashCode1 = hashMap.hashCode();
+ hashMap.put(KEY_KEY, VALUE_VAL);
+ int hashCode2 = hashMap.hashCode();
+
+ assertTrue(hashCode1 != hashCode2);
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.IdentityHashMap()'.
+ */
+ public void testHashMap() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.IdentityHashMap(int)'
+ */
+ public void testHashMapInt() {
+ IdentityHashMap hashMap = new IdentityHashMap(CAPACITY_16);
+ checkEmptyHashMapAssumptions(hashMap);
+
+ // TODO(mmendez): how do we verify capacity?
+ boolean failed = true;
+ try {
+ new IdentityHashMap(-SIZE_ONE);
+ } catch (Throwable ex) {
+ if (ex instanceof IllegalArgumentException) {
+ failed = false;
+ }
+ }
+
+ if (failed) {
+ fail("Failure testing new IdentityHashMap(-1)");
+ }
+
+ IdentityHashMap zeroSizedHashMap = new IdentityHashMap(0);
+ assertNotNull(zeroSizedHashMap);
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.IdentityHashMap(Map)'
+ */
+ public void testHashMapMap() {
+ IdentityHashMap srcMap = new IdentityHashMap();
+ assertNotNull(srcMap);
+ checkEmptyHashMapAssumptions(srcMap);
+
+ srcMap.put(INTEGER_1, INTEGER_11);
+ srcMap.put(INTEGER_2, INTEGER_22);
+ srcMap.put(INTEGER_3, INTEGER_33);
+
+ IdentityHashMap hashMap = new IdentityHashMap(srcMap);
+ assertFalse(hashMap.isEmpty());
+ assertTrue(hashMap.size() == SIZE_THREE);
+
+ Collection valColl = hashMap.values();
+ assertTrue(valColl.contains(INTEGER_11));
+ assertTrue(valColl.contains(INTEGER_22));
+ assertTrue(valColl.contains(INTEGER_33));
+
+ Collection keyColl = hashMap.keySet();
+ assertTrue(keyColl.contains(INTEGER_1));
+ assertTrue(keyColl.contains(INTEGER_2));
+ assertTrue(keyColl.contains(INTEGER_3));
+ }
+
+ /**
+ * Test that the implementation differs from a standard map in demanding
+ * identity.
+ */
+ public void testIdentity() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ Foo foo1 = new Foo();
+ assertNull(hashMap.get(foo1));
+ hashMap.put(foo1, VALUE_1);
+ assertNotNull(hashMap.get(foo1));
+ assertSame(VALUE_1, hashMap.get(foo1));
+
+ Foo foo2 = new Foo();
+ assertNull(hashMap.get(foo2));
+ }
+
+ /**
+ * Test that the implementation differs from a standard map in demanding
+ * identity.
+ */
+ public void testIdentityBasedEquality() {
+ IdentityHashMap hashMap1 = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap1);
+
+ IdentityHashMap hashMap2 = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap2);
+
+ hashMap1.put(new Foo(), VALUE_1);
+ hashMap2.put(new Foo(), VALUE_1);
+ assertFalse(hashMap1.equals(hashMap2));
+ }
+
+ /**
+ * Test that the implementation differs from a standard map in demanding
+ * identity.
+ */
+ public void testIdentityBasedHashCode() {
+ IdentityHashMap hashMap1 = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap1);
+
+ IdentityHashMap hashMap2 = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap2);
+
+ hashMap1.put(new Foo(), VALUE_1);
+ hashMap2.put(new Foo(), VALUE_1);
+ if (GWT.isScript()) {
+ // Only reliable in web mode since hosted mode can have identity hash
+ // collisions.
+ assertFalse(hashMap1.hashCode() == hashMap2.hashCode());
+ }
+ }
+
+ /*
+ * Test method for 'java.util.AbstractMap.isEmpty()'
+ */
+ public void testIsEmpty() {
+ IdentityHashMap srcMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(srcMap);
+
+ IdentityHashMap dstMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(dstMap);
+
+ dstMap.putAll(srcMap);
+ assertTrue(dstMap.isEmpty());
+
+ dstMap.put(KEY_KEY, VALUE_VAL);
+ assertFalse(dstMap.isEmpty());
+
+ dstMap.remove(KEY_KEY);
+ assertTrue(dstMap.isEmpty());
+ assertEquals(dstMap.size(), 0);
+ }
+
+ public void testKeysConflict() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+
+ hashMap.put(STRING_ZERO_KEY, STRING_ZERO_VALUE);
+ hashMap.put(INTEGER_ZERO_KEY, INTEGER_ZERO_VALUE);
+ hashMap.put(ODD_ZERO_KEY, ODD_ZERO_VALUE);
+ assertEquals(hashMap.get(INTEGER_ZERO_KEY), INTEGER_ZERO_VALUE);
+ assertEquals(hashMap.get(ODD_ZERO_KEY), ODD_ZERO_VALUE);
+ assertEquals(hashMap.get(STRING_ZERO_KEY), STRING_ZERO_VALUE);
+ hashMap.remove(INTEGER_ZERO_KEY);
+ assertEquals(hashMap.get(ODD_ZERO_KEY), ODD_ZERO_VALUE);
+ assertEquals(hashMap.get(STRING_ZERO_KEY), STRING_ZERO_VALUE);
+ assertEquals(hashMap.get(INTEGER_ZERO_KEY), null);
+ hashMap.remove(ODD_ZERO_KEY);
+ assertEquals(hashMap.get(INTEGER_ZERO_KEY), null);
+ assertEquals(hashMap.get(ODD_ZERO_KEY), null);
+ assertEquals(hashMap.get(STRING_ZERO_KEY), STRING_ZERO_VALUE);
+ hashMap.remove(STRING_ZERO_KEY);
+ assertEquals(hashMap.get(INTEGER_ZERO_KEY), null);
+ assertEquals(hashMap.get(ODD_ZERO_KEY), null);
+ assertEquals(hashMap.get(STRING_ZERO_KEY), null);
+ assertEquals(hashMap.size(), 0);
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.keySet()'
+ */
+ public void testKeySet() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ Set keySet = hashMap.keySet();
+ assertNotNull(keySet);
+ assertTrue(keySet.isEmpty());
+ assertTrue(keySet.size() == 0);
+
+ hashMap.put(KEY_TEST_KEY_SET, VALUE_TEST_KEY_SET);
+
+ assertTrue(keySet.size() == SIZE_ONE);
+ assertTrue(keySet.contains(KEY_TEST_KEY_SET));
+ assertFalse(keySet.contains(VALUE_TEST_KEY_SET));
+ assertFalse(keySet.contains(KEY_TEST_KEY_SET.toUpperCase()));
+ }
+
+ /*
+ * Test method for 'java.util.IdentityHashMap.put(Object, Object)'
+ */
+ public void testPut() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ assertNull(hashMap.put(KEY_TEST_PUT, VALUE_TEST_PUT_1));
+ assertEquals(hashMap.put(KEY_TEST_PUT, VALUE_TEST_PUT_2), VALUE_TEST_PUT_1);
+ assertNull(hashMap.put(null, VALUE_TEST_PUT_1));
+ assertEquals(hashMap.put(null, VALUE_TEST_PUT_2), VALUE_TEST_PUT_1);
+ }
+
+ /**
+ * Test method for 'java.util.IdentityHashMap.putAll(Map)'.
+ */
+ public void testPutAll() {
+ IdentityHashMap srcMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(srcMap);
+
+ srcMap.put(KEY_1, VALUE_1);
+ srcMap.put(KEY_2, VALUE_2);
+ srcMap.put(KEY_3, VALUE_3);
+
+ // Make sure that the data is copied correctly
+ IdentityHashMap dstMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(dstMap);
+
+ dstMap.putAll(srcMap);
+ assertEquals(srcMap.size(), dstMap.size());
+ assertTrue(dstMap.containsKey(KEY_1));
+ assertTrue(dstMap.containsValue(VALUE_1));
+ assertFalse(dstMap.containsKey(KEY_1.toUpperCase()));
+ assertFalse(dstMap.containsValue(VALUE_1.toUpperCase()));
+
+ assertTrue(dstMap.containsKey(KEY_2));
+ assertTrue(dstMap.containsValue(VALUE_2));
+ assertFalse(dstMap.containsKey(KEY_2.toUpperCase()));
+ assertFalse(dstMap.containsValue(VALUE_2.toUpperCase()));
+
+ assertTrue(dstMap.containsKey(KEY_3));
+ assertTrue(dstMap.containsValue(VALUE_3));
+ assertFalse(dstMap.containsKey(KEY_3.toUpperCase()));
+ assertFalse(dstMap.containsValue(VALUE_3.toUpperCase()));
+
+ // Check that an empty map does not blow away the contents of the
+ // destination map
+ IdentityHashMap emptyMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(emptyMap);
+ dstMap.putAll(emptyMap);
+ assertTrue(dstMap.size() == srcMap.size());
+
+ // Check that put all overwrite any existing mapping in the destination map
+ srcMap.put(KEY_1, VALUE_2);
+ srcMap.put(KEY_2, VALUE_3);
+ srcMap.put(KEY_3, VALUE_1);
+
+ dstMap.putAll(srcMap);
+ assertEquals(dstMap.size(), srcMap.size());
+ assertEquals(dstMap.get(KEY_1), VALUE_2);
+ assertEquals(dstMap.get(KEY_2), VALUE_3);
+ assertEquals(dstMap.get(KEY_3), VALUE_1);
+
+ // Check that a putAll does adds data but does not remove it
+
+ srcMap.put(KEY_4, VALUE_4);
+ dstMap.putAll(srcMap);
+ assertEquals(dstMap.size(), srcMap.size());
+ assertTrue(dstMap.containsKey(KEY_4));
+ assertTrue(dstMap.containsValue(VALUE_4));
+ assertEquals(dstMap.get(KEY_1), VALUE_2);
+ assertEquals(dstMap.get(KEY_2), VALUE_3);
+ assertEquals(dstMap.get(KEY_3), VALUE_1);
+ assertEquals(dstMap.get(KEY_4), VALUE_4);
+
+ dstMap.putAll(dstMap);
+ }
+
+ /**
+ * Test method for 'java.util.IdentityHashMap.remove(Object)'.
+ */
+ public void testRemove() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ assertNull(hashMap.remove(null));
+ hashMap.put(null, VALUE_TEST_REMOVE);
+ assertNotNull(hashMap.remove(null));
+
+ hashMap.put(KEY_TEST_REMOVE, VALUE_TEST_REMOVE);
+ assertEquals(hashMap.remove(KEY_TEST_REMOVE), VALUE_TEST_REMOVE);
+ assertNull(hashMap.remove(KEY_TEST_REMOVE));
+ }
+
+ /**
+ * Test method for 'java.util.IdentityHashMap.size()'.
+ */
+ public void testSize() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ // Test size behavior on put
+ assertEquals(hashMap.size(), SIZE_ZERO);
+ hashMap.put(KEY_1, VALUE_1);
+ assertEquals(hashMap.size(), SIZE_ONE);
+ hashMap.put(KEY_2, VALUE_2);
+ assertEquals(hashMap.size(), SIZE_TWO);
+ hashMap.put(KEY_3, VALUE_3);
+ assertEquals(hashMap.size(), SIZE_THREE);
+
+ // Test size behavior on remove
+ hashMap.remove(KEY_1);
+ assertEquals(hashMap.size(), SIZE_TWO);
+ hashMap.remove(KEY_2);
+ assertEquals(hashMap.size(), SIZE_ONE);
+ hashMap.remove(KEY_3);
+ assertEquals(hashMap.size(), SIZE_ZERO);
+
+ // Test size behavior on putAll
+ hashMap.put(KEY_1, VALUE_1);
+ hashMap.put(KEY_2, VALUE_2);
+ hashMap.put(KEY_3, VALUE_3);
+ IdentityHashMap srcMap = new IdentityHashMap(hashMap);
+ hashMap.putAll(srcMap);
+ assertEquals(hashMap.size(), SIZE_THREE);
+
+ // Test size behavior on clear
+ hashMap.clear();
+ assertEquals(hashMap.size(), SIZE_ZERO);
+ }
+
+ /**
+ * Test method for 'java.util.AbstractMap.toString()'.
+ */
+ public void testToString() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+ hashMap.put(KEY_KEY, VALUE_VAL);
+ String entryString = makeEntryString(KEY_KEY, VALUE_VAL);
+ assertTrue(entryString.equals(hashMap.toString()));
+ }
+
+ /**
+ * Test method for 'java.util.AbstractMap.values()'.
+ */
+ public void testValues() {
+ IdentityHashMap hashMap = new IdentityHashMap();
+ checkEmptyHashMapAssumptions(hashMap);
+
+ assertNotNull(hashMap.values());
+
+ hashMap.put(KEY_KEY, VALUE_VAL);
+
+ Collection valColl = hashMap.values();
+ assertNotNull(valColl);
+ assertEquals(valColl.size(), SIZE_ONE);
+
+ Iterator itVal = valColl.iterator();
+ String val = (String) itVal.next();
+ assertEquals(val, VALUE_VAL);
+ }
+
+ protected Map makeEmptyMap() {
+ return new IdentityHashMap();
+ }
+
+ protected Map makeConfirmedMap() {
+ return new IdentityHashMap();
+ }
+
+ @Override
+ protected boolean useNullValue() {
+ // The JRE IdentityHashMap always thinks it has a null value.
+ return false;
+ }
+
+ private Iterator iterateThrough(final IdentityHashMap expected) {
+ Iterator iter = expected.entrySet().iterator();
+ for (int i = 0; i < expected.size(); i++) {
+ iter.next();
+ }
+ return iter;
+ }
+
+ private String makeEntryString(final String key, final String value) {
+ return "{" + key + "=" + value + "}";
+ }
+}
diff --git a/user/test/org/apache/commons/collections/TestMap.java b/user/test/org/apache/commons/collections/TestMap.java
index 10c8749..7c7f97e 100644
--- a/user/test/org/apache/commons/collections/TestMap.java
+++ b/user/test/org/apache/commons/collections/TestMap.java
@@ -282,6 +282,10 @@
*/
protected abstract Map makeEmptyMap();
+ protected Map makeConfirmedMap() {
+ return new HashMap();
+ }
+
/**
* Return a new, populated map. The mappings in the map should match the
* keys and values returned from {@link #getSampleKeys()} and {@link
@@ -882,7 +886,7 @@
protected void resetEmpty() {
this.map = makeEmptyMap();
views();
- this.confirmed = new HashMap();
+ this.confirmed = makeConfirmedMap();
}
@@ -893,7 +897,7 @@
protected void resetFull() {
this.map = makeFullMap();
views();
- this.confirmed = new HashMap();
+ this.confirmed = makeConfirmedMap();
Object[] k = getSampleKeys();
Object[] v = getSampleValues();
for (int i = 0; i < k.length; i++) {