Adding LinkedHashMap and supporting tests and RPC serializers
Review by:jat (desk review) 

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2609 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/User.gwt.xml b/user/src/com/google/gwt/user/User.gwt.xml
index af4e794..eda5a3c 100644
--- a/user/src/com/google/gwt/user/User.gwt.xml
+++ b/user/src/com/google/gwt/user/User.gwt.xml
@@ -40,4 +40,8 @@
    <inherits name="com.google.gwt.user.CaptionPanel" />
    <inherits name="com.google.gwt.user.Window" />
    <inherits name="com.google.gwt.user.Accessibility"/>
+    
+    <super-source path="translatable"/>
+    <source path="client"/>
+    
 </module>
diff --git a/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java b/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java
new file mode 100644
index 0000000..820ffa7
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java
@@ -0,0 +1,80 @@
+/*

+ * 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.user.client.rpc.core.java.util;

+

+import com.google.gwt.user.client.rpc.SerializationException;

+import com.google.gwt.user.client.rpc.SerializationStreamReader;

+import com.google.gwt.user.client.rpc.SerializationStreamWriter;

+

+import java.lang.reflect.Field;

+import java.util.LinkedHashMap;

+import java.util.Set;

+import java.util.Map.Entry;

+

+/**

+ * Custom field serializer for {@link java.util.LinkedHashMap} for the server

+ * (uses reflection).

+ */

+public final class LinkedHashMap_CustomFieldSerializer {

+

+  public static void deserialize(SerializationStreamReader streamReader,

+      LinkedHashMap instance) throws SerializationException {

+    int size = streamReader.readInt();

+

+    for (int i = 0; i < size; ++i) {

+      Object key = streamReader.readObject();

+      Object value = streamReader.readObject();

+      instance.put(key, value);

+    }

+  }

+

+  public static LinkedHashMap instantiate(SerializationStreamReader streamReader)

+      throws SerializationException {

+    boolean accessOrder = streamReader.readBoolean();

+    return new LinkedHashMap(16, .75f, accessOrder);

+  }

+

+  public static void serialize(SerializationStreamWriter streamWriter,

+      LinkedHashMap instance) throws SerializationException {

+    streamWriter.writeBoolean(getAccessOrder(instance));

+    int size = instance.size();

+    streamWriter.writeInt(size);

+

+    for (Entry entry : (Set<Entry>) instance.entrySet()) {

+      streamWriter.writeObject(entry.getKey());

+      streamWriter.writeObject(entry.getValue());

+    }

+  }

+

+  private static boolean getAccessOrder(LinkedHashMap instance)

+      throws SerializationException {

+    Field accessOrderField;

+    try {

+      accessOrderField = LinkedHashMap.class.getDeclaredField("accessOrder");

+      accessOrderField.setAccessible(true);

+      return ((Boolean) accessOrderField.get(instance)).booleanValue();

+    } catch (SecurityException e) {

+      throw new SerializationException("Can't get accessOrder field", e);

+    } catch (NoSuchFieldException e) {

+      throw new SerializationException("Can't get accessOrder field", e);

+    } catch (IllegalArgumentException e) {

+      throw new SerializationException("Can't get accessOrder field", e);

+    } catch (IllegalAccessException e) {

+      throw new SerializationException("Can't get accessOrder field", e);

+    }

+  }

+

+}

diff --git a/user/super/com/google/gwt/emul/java/util/LinkedHashMap.java b/user/super/com/google/gwt/emul/java/util/LinkedHashMap.java
index ec4fef1..c1ff2a4 100644
--- a/user/super/com/google/gwt/emul/java/util/LinkedHashMap.java
+++ b/user/super/com/google/gwt/emul/java/util/LinkedHashMap.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,6 +15,8 @@
  */
 package java.util;
 
+import java.io.Serializable;
+
 /**
  * Hash table implementation of the Map interface with predictable iteration
  * order. <a
@@ -24,64 +26,277 @@
  * @param <K> key type.
  * @param <V> value type.
  */
-public class LinkedHashMap<K, V> extends HashMap<K, V> implements Map<K, V>,
-    Cloneable {
+public class LinkedHashMap<K, V> extends HashMap<K, V> implements Map<K, V> {
+
+  /**
+   * The entry we use includes next/prev pointers for a doubly-linked circular
+   * list with a head node. This reduces the special cases we have to deal with
+   * in the list operations.
+   * 
+   * Note that we duplicate the key from the underlying hashmap so we can find
+   * the eldest entry. The alternative would have been to modify HashMap so more
+   * of the code was directly usable here, but this would have added some
+   * overhead to HashMap, or to reimplement most of the HashMap code here with
+   * small modifications. Paying a small storage cost only if you use
+   * LinkedHashMap and minimizing code size seemed like a better tradeoff
+   */
+  private class ChainEntry extends MapEntryImpl<K, V> implements Serializable {
+    private transient ChainEntry next;
+    private transient ChainEntry prev;
+
+    public ChainEntry() {
+      this(null, null);
+    }
+
+    public ChainEntry(K key, V value) {
+      super(key, value);
+      next = prev = null;
+    }
+
+    /**
+     * Add this node after the specified node in the chain.
+     * 
+     * @param node the node to insert after
+     */
+    public void addAfter(ChainEntry node) {
+      update(node, node.next);
+    }
+
+    /**
+     * Add this node before the specified node in the chain.
+     * 
+     * @param node the node to insert before
+     */
+    public void addBefore(ChainEntry node) {
+      update(node.prev, node);
+    }
+
+    /**
+     * Remove this node from any list it may be a part of.
+     */
+    public void remove() {
+      next.prev = prev;
+      prev.next = next;
+      next = null;
+      prev = null;
+    }
+
+    private void update(ChainEntry prev, ChainEntry next) {
+      // This entry is not in the list.
+      assert (this.next == null) && (this.prev == null);
+
+      // Will not break the chain.
+      assert (prev != null && next != null);
+
+      // Update me.
+      this.prev = prev;
+      this.next = next;
+      next.prev = this;
+      prev.next = this;
+    }
+  }
+
+  private final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
+
+    private final class EntryIterator implements Iterator<Entry<K, V>> {
+      // Current entry.
+      private ChainEntry current;
+
+      // Next entry. Used to allow removal of the current node.
+      private ChainEntry next;
+
+      public EntryIterator() {
+        current = head;
+        next = head.next;
+      }
+
+      public boolean hasNext() {
+        return next != head;
+      }
+
+      public java.util.Map.Entry<K, V> next() {
+        if (next == null) {
+          throw new NoSuchElementException();
+        }
+        current = next;
+        next = next.next;
+        return current;
+      }
+
+      public void remove() {
+        if (current == null || current == head) {
+          throw new IllegalStateException("No current entry");
+        }
+        current.remove();
+        map.remove(current.getKey());
+        current = null;
+      }
+    }
+
+    @Override
+    public void clear() {
+      LinkedHashMap.this.clear();
+    }
+
+    @Override
+    public boolean contains(Object o) {
+      if (!(o instanceof Map.Entry)) {
+        return false;
+      }
+      Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
+      Object key = entry.getKey();
+      if (LinkedHashMap.this.containsKey(key)) {
+        Object value = LinkedHashMap.this.get(key);
+        return Utility.equalsWithNullCheck(entry.getValue(), value);
+      }
+      return false;
+    }
+
+    @Override
+    public Iterator<Map.Entry<K, V>> iterator() {
+      return new EntryIterator();
+    }
+
+    @Override
+    public int size() {
+      return map.size();
+    }
+  }
+
+  // True if we should use the access order (ie, for LRU caches) instead of
+  // insertion order.
+  private transient boolean accessOrder;
+
+  /*
+   * The head of the LRU/insert order chain, which is a doubly-linked circular
+   * list.
+   * 
+   * The most recently inserted/accessed node is at the end of the chain, ie.
+   * chain.prev.
+   */
+  private transient ChainEntry head;
+
+  /*
+   * The hashmap that keeps track of our entries and the chain. Note that we
+   * duplicate the key here to eliminate changes to HashMap and minimize the
+   * code here, at the expense of additional space.
+   */
+  private transient HashMap<K, ChainEntry> map;
+
+  {
+    // Head's value should never be accessed.
+    head = new ChainEntry();
+    head.prev = head;
+    head.next = head;
+  }
 
   public LinkedHashMap() {
-    this(11, 0.75f);
+    this(11, 0.75f, false);
   }
 
-  /**
-   * @param ignored
-   */
   public LinkedHashMap(int ignored) {
-    this(ignored, 0.75f);
+    this(ignored, 0.75f, false);
   }
 
-  /**
-   * @param ignored
-   * @param alsoIgnored
-   */
   public LinkedHashMap(int ignored, float alsoIgnored) {
-    super(ignored, alsoIgnored);
-    // TODO(jat): implement
-    throw new UnsupportedOperationException("LinkedHashMap not supported");
+    this(ignored, alsoIgnored, false);
   }
 
-  /**
-   * @param toBeCopied
-   */
+  public LinkedHashMap(int ignored, float alsoIgnored, boolean accessOrder) {
+    super();
+    this.accessOrder = accessOrder;
+    this.map = new HashMap<K, ChainEntry>(ignored, alsoIgnored);
+  }
+
   public LinkedHashMap(Map<? extends K, ? extends V> toBeCopied) {
-    this();
+    this(toBeCopied.size());
     putAll(toBeCopied);
   }
 
   @Override
   public void clear() {
-    // TODO(jat): implement
+    map.clear();
+    head.prev = head;
+    head.next = head;
   }
 
   @Override
-  public Object clone() {
-    // TODO(jat): implement
+  public boolean containsKey(Object key) {
+    return map.containsKey(key);
+  }
+
+  @Override
+  public boolean containsValue(Object value) {
+    ChainEntry node = head.next;
+    while (node != head) {
+      if (Utility.equalsWithNullCheck(node.getValue(), value)) {
+        return true;
+      }
+      node = node.next;
+    }
+    return false;
+  }
+
+  @Override
+  public Set<Map.Entry<K, V>> entrySet() {
+    return new EntrySet();
+  }
+
+  @Override
+  public V get(Object key) {
+    ChainEntry entry = map.get(key);
+    if (entry != null) {
+      if (accessOrder) {
+        // Move to the tail of the chain on access if requested.
+        entry.remove();
+        entry.addBefore(head);
+      }
+      return entry.getValue();
+    }
     return null;
   }
 
   @Override
   public V put(K key, V value) {
-    // TODO(jat): implement
-    return null;
+    ChainEntry old = map.get(key);
+    if (old == null) {
+      ChainEntry newEntry = new ChainEntry(key, value);
+      map.put(key, newEntry);
+      newEntry.addBefore(head);
+      ChainEntry eldest = head.next;
+      if (removeEldestEntry(eldest)) {
+        eldest.remove();
+        map.remove(eldest.getKey());
+      }
+      return null;
+    } else {
+      V oldValue = old.getValue();
+      old.setValue(value);
+      // If orders by access, move to end of execution block.
+      if (accessOrder) {
+        old.remove();
+        old.addBefore(head);
+      }
+      return oldValue;
+    }
   }
 
   @Override
   public V remove(Object key) {
-    // TODO(jat): implement
+    ChainEntry entry = map.remove(key);
+    if (entry != null) {
+      entry.remove();
+      return entry.getValue();
+    }
     return null;
   }
 
-  protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
-    // TODO(jat): implement
-    return false;
+  @Override
+  public int size() {
+    return map.size();
   }
 
+  protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+    return false;
+  }
 }
diff --git a/user/super/com/google/gwt/user/translatable/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java b/user/super/com/google/gwt/user/translatable/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java
new file mode 100644
index 0000000..a930bda
--- /dev/null
+++ b/user/super/com/google/gwt/user/translatable/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java
@@ -0,0 +1,64 @@
+/*

+ * 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.user.client.rpc.core.java.util;

+

+import com.google.gwt.user.client.rpc.SerializationException;

+import com.google.gwt.user.client.rpc.SerializationStreamReader;

+import com.google.gwt.user.client.rpc.SerializationStreamWriter;

+

+import java.util.LinkedHashMap;

+import java.util.Set;

+import java.util.Map.Entry;

+

+/**

+ * Custom field serializer for {@link java.util.LinkedHashMap}, which is the

+ * same as {@link java.util.HashMap}.

+ */

+public final class LinkedHashMap_CustomFieldSerializer {

+

+  public static void deserialize(SerializationStreamReader streamReader,

+      LinkedHashMap instance) throws SerializationException {

+    int size = streamReader.readInt();

+

+    for (int i = 0; i < size; ++i) {

+      Object key = streamReader.readObject();

+      Object value = streamReader.readObject();

+      instance.put(key, value);

+    }

+  }

+

+  public static LinkedHashMap instantiate(SerializationStreamReader streamReader)

+      throws SerializationException {

+    boolean accessOrder = streamReader.readBoolean();

+    return new LinkedHashMap(16, .75f, accessOrder);

+  }

+

+  public static void serialize(SerializationStreamWriter streamWriter,

+      LinkedHashMap instance) throws SerializationException {

+    streamWriter.writeBoolean(getAccessOrder(instance));

+    int size = instance.size();

+    streamWriter.writeInt(size);

+    

+    for (Entry entry : (Set<Entry>) instance.entrySet()) {

+      streamWriter.writeObject(entry.getKey());

+      streamWriter.writeObject(entry.getValue());

+    }

+  }

+

+  private static native boolean getAccessOrder(LinkedHashMap instance) /*-{

+    return instance.@java.util.LinkedHashMap::accessOrder;

+   }-*/;

+}

diff --git a/user/test/com/google/gwt/emultest/java/util/CollectionsTest.java b/user/test/com/google/gwt/emultest/java/util/CollectionsTest.java
index 51898e7..8ae4f67 100644
--- a/user/test/com/google/gwt/emultest/java/util/CollectionsTest.java
+++ b/user/test/com/google/gwt/emultest/java/util/CollectionsTest.java
@@ -19,6 +19,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.LinkedHashMap;
 import java.util.List;
 
 /**
@@ -26,6 +27,10 @@
  */
 public class CollectionsTest extends EmulTestBase {
 
+  LinkedHashMap dummy() {
+    return new LinkedHashMap();
+  }
+
   public static List createSortedList() {
     ArrayList l = new ArrayList();
     l.add("a");
@@ -46,13 +51,10 @@
 
   /**
    * Test Collections.binarySearch(List, Object).
-   *
-   * Verify the following cases:
-   *   empty List
-   *   odd numbers of elements
-   *   even numbers of elements
-   *   not found value larger than all elements
-   *   not found value smaller than all elements
+   * 
+   * Verify the following cases: empty List odd numbers of elements even numbers
+   * of elements not found value larger than all elements not found value
+   * smaller than all elements
    */
   public void testBinarySearchObject() {
     List a1 = new ArrayList();
@@ -63,7 +65,7 @@
     assertEquals(-2, ret);
     ret = Collections.binarySearch(a2, "y");
     assertEquals(2, ret);
-    List a3 = new ArrayList(Arrays.asList(new String[]{"b", "c", "x", "y"}));
+    List a3 = new ArrayList(Arrays.asList(new String[] {"b", "c", "x", "y"}));
     ret = Collections.binarySearch(a3, "z");
     assertEquals(-5, ret);
     ret = Collections.binarySearch(a3, "a");
@@ -74,14 +76,10 @@
 
   /**
    * Test Collections.binarySearch(List, Object, Comparator).
-   *
-   * Verify the following cases:
-   *   empty List
-   *   odd numbers of elements
-   *   even numbers of elements
-   *   not found value larger than all elements
-   *   not found value smaller than all elements
-   *   null Comparator uses natural ordering
+   * 
+   * Verify the following cases: empty List odd numbers of elements even numbers
+   * of elements not found value larger than all elements not found value
+   * smaller than all elements null Comparator uses natural ordering
    */
   public void testBinarySearchObjectComparator() {
     Comparator inverseSort = new Comparator() {
@@ -92,12 +90,12 @@
     List a1 = new ArrayList();
     int ret = Collections.binarySearch(a1, "", inverseSort);
     assertEquals(-1, ret);
-    List a2 = new ArrayList(Arrays.asList(new String[]{"y", "g", "a"}));
+    List a2 = new ArrayList(Arrays.asList(new String[] {"y", "g", "a"}));
     ret = Collections.binarySearch(a2, "c", inverseSort);
     assertEquals(-3, ret);
     ret = Collections.binarySearch(a2, "a", inverseSort);
     assertEquals(2, ret);
-    List a3 = new ArrayList(Arrays.asList(new String[]{"y", "x", "c", "b"}));
+    List a3 = new ArrayList(Arrays.asList(new String[] {"y", "x", "c", "b"}));
     ret = Collections.binarySearch(a3, "a", inverseSort);
     assertEquals(-5, ret);
     ret = Collections.binarySearch(a3, "z", inverseSort);
@@ -105,8 +103,8 @@
     ret = Collections.binarySearch(a3, "y", inverseSort);
     assertEquals(0, ret);
 
-    List a4 = new ArrayList(Arrays.asList(
-        new String[]{"a", "b", "c", "d", "e"}));
+    List a4 = new ArrayList(
+        Arrays.asList(new String[] {"a", "b", "c", "d", "e"}));
     ret = Collections.binarySearch(a4, "d", null); // should not NPE
     assertEquals(3, ret);
   }
@@ -154,7 +152,7 @@
     Object[] expected = {"c", "b", "a"};
     assertEquals(expected, a);
   }
-  
+
   public void testToArray() {
     List<Integer> testList = createRandomList();
     Integer[] testArray = new Integer[testList.size()];
diff --git a/user/test/com/google/gwt/emultest/java/util/LinkedHashMapTest.java b/user/test/com/google/gwt/emultest/java/util/LinkedHashMapTest.java
new file mode 100644
index 0000000..c493ca1
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/util/LinkedHashMapTest.java
@@ -0,0 +1,780 @@
+/*

+ * 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.Iterator;

+import java.util.Map;

+import java.util.Set;

+import java.util.Map.Entry;

+import java.util.LinkedHashMap;

+

+/**

+ * Tests <code>LinkedHashMap</code>.

+ */

+public class LinkedHashMapTest extends TestMap {

+  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() {

+    @Override

+    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 LinkedHashMap.

+   * 

+   * @param hashMap

+   */

+  private static void checkEmptyLinkedHashMapAssumptions(LinkedHashMap 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());

+  }

+

+  @Override

+  public String getModuleName() {

+    return "com.google.gwt.emultest.EmulSuite";

+  }

+

+  // should be a method-level class, however to avoid serialization warning made

+  // static instead.

+  static class TestRemoveEldestMap extends LinkedHashMap {

+

+    public boolean removeEldest;

+    public String expectedKey;

+

+    public TestRemoveEldestMap() {

+      this(false);

+    }

+

+    public TestRemoveEldestMap(boolean accessOrder) {

+      super(1, .5f, accessOrder);

+    }

+

+    @Override

+    public boolean removeEldestEntry(Map.Entry entry) {

+      if (removeEldest) {

+        assertEquals(expectedKey, entry.getKey());

+        return true;

+      } else {

+        return false;

+      }

+    }

+  }

+

+  public void testRemoveEldest() {

+    TestRemoveEldestMap m = new TestRemoveEldestMap(false);

+    m.put("A", "A");

+    m.put("B", "B");

+    m.put("C", "C");

+    m.put("D", "D");

+    m.get("B");

+    m.get("D");

+    m.removeEldest = true;

+    m.expectedKey = "A";

+    m.put("E", "E");

+    m.put("B", "New-B");

+    Iterator<Map.Entry> entries = m.entrySet().iterator();

+    Map.Entry first = entries.next();

+    assertEquals("B", first.getKey());

+    assertEquals("New-B", first.getValue());

+    assertEquals(4, m.size());

+  }

+

+  public void testRemoveEldestMapLRU() {

+    TestRemoveEldestMap m;

+    Iterator<Map.Entry> entries;

+    Map.Entry first;

+    m = new TestRemoveEldestMap(true);

+    m.put("A", "A");

+    m.put("B", "B");

+    m.put("C", "C");

+    m.put("D", "D");

+    m.get("A");

+    m.get("D");

+    m.removeEldest = true;

+    m.expectedKey = "B";

+    m.put("E", "E");

+

+    m.put("C", "New-C");

+    entries = m.entrySet().iterator();

+    first = entries.next();

+    assertEquals("A", first.getKey());

+    assertEquals("D", entries.next().getKey());

+    assertEquals("E", entries.next().getKey());

+    assertEquals("New-C", entries.next().getValue());

+  }

+

+  public void testAddWatch() {

+    LinkedHashMap<String, String> m = new LinkedHashMap<String, String>();

+    m.put("watch", "watch");

+    assertEquals(m.get("watch"), "watch");

+  }

+

+  public void testLRU() {

+    LinkedHashMap<String, String> m = new LinkedHashMap<String, String>(10,

+        .5f, true);

+    m.put("A", "A");

+    m.put("B", "B");

+    m.put("C", "C");

+    m.put("D", "D");

+    Iterator<Entry<String, String>> entry = m.entrySet().iterator();

+    assertEquals("A", entry.next().getValue());

+    assertEquals("B", entry.next().getValue());

+    assertEquals("C", entry.next().getValue());

+    assertEquals("D", entry.next().getValue());

+    m.get("B");

+    m.get("D");

+    entry = m.entrySet().iterator();

+    assertEquals("A", entry.next().getValue());

+    assertEquals("C", entry.next().getValue());

+    assertEquals("B", entry.next().getValue());

+    assertEquals("D", entry.next().getValue());

+  }

+

+  public void testAddEqualKeys() {

+    final LinkedHashMap<Number, Object> expected = new LinkedHashMap<Number, Object>();

+    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.LinkedHashMap.clear()'

+   */

+  public void testClear() {

+    LinkedHashMap<String, String> hashMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(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.LinkedHashMap.clone()'

+   */

+  // public void donttestClone() {

+  // LinkedHashMap srcMap = new LinkedHashMap();

+  // checkEmptyLinkedHashMapAssumptions(srcMap);

+  //

+  // // Check empty clone behavior

+  // LinkedHashMap dstMap = (LinkedHashMap) 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 = (LinkedHashMap) 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.LinkedHashMap.containsKey(Object)'

+   */

+  public void testContainsKey() {

+    LinkedHashMap hashMap = new LinkedHashMap();

+    checkEmptyLinkedHashMapAssumptions(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.LinkedHashMap.containsValue(Object)'

+   */

+  public void testContainsValue() {

+    LinkedHashMap hashMap = new LinkedHashMap();

+    checkEmptyLinkedHashMapAssumptions(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));

+

+    assertFalse(hashMap.containsValue(null));

+    hashMap.put(KEY_TEST_CONTAINS_VALUE, null);

+    assertTrue(hashMap.containsValue(null));

+  }

+

+  /*

+   * Test method for 'java.util.LinkedHashMap.entrySet()'

+   */

+  public void testEntrySet() {

+    LinkedHashMap hashMap = new LinkedHashMap();

+    checkEmptyLinkedHashMapAssumptions(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);

+    checkEmptyLinkedHashMapAssumptions(hashMap);

+  }

+

+  /*

+   * Used to test the entrySet remove method.

+   */

+  public void testEntrySetRemove() {

+    LinkedHashMap<String, String> hashMap = new LinkedHashMap<String, String>();

+    hashMap.put("A", "B");

+    LinkedHashMap<String, String> dummy = new LinkedHashMap<String, String>();

+    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() {

+  // LinkedHashMap hashMap = new LinkedHashMap();

+  // checkEmptyLinkedHashMapAssumptions(hashMap);

+  //

+  // hashMap.put(KEY_KEY, VALUE_VAL);

+  //

+  // LinkedHashMap copyMap = (LinkedHashMap) 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.LinkedHashMap.get(Object)'.

+   */

+  public void testGet() {

+    LinkedHashMap hashMap = new LinkedHashMap();

+    checkEmptyLinkedHashMapAssumptions(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() {

+    LinkedHashMap hashMap = new LinkedHashMap();

+    checkEmptyLinkedHashMapAssumptions(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.LinkedHashMap.LinkedHashMap()'.

+   */

+  public void testLinkedHashMap() {

+    LinkedHashMap hashMap = new LinkedHashMap();

+    checkEmptyLinkedHashMapAssumptions(hashMap);

+  }

+

+  /*

+   * Test method for 'java.util.LinkedHashMap.LinkedHashMap(int)'

+   */

+  public void testLinkedHashMapInt() {

+    LinkedHashMap hashMap = new LinkedHashMap(CAPACITY_16);

+    checkEmptyLinkedHashMapAssumptions(hashMap);

+

+    // TODO(mmendez): how do we verify capacity?

+    boolean failed = true;

+    try {

+      new LinkedHashMap(-SIZE_ONE);

+    } catch (Throwable ex) {

+      if (ex instanceof IllegalArgumentException) {

+        failed = false;

+      }

+    }

+

+    if (failed) {

+      fail("Failure testing new LinkedHashMap(-1)");

+    }

+

+    LinkedHashMap zeroSizedLinkedHashMap = new LinkedHashMap(0);

+    assertNotNull(zeroSizedLinkedHashMap);

+  }

+

+  /*

+   * Test method for 'java.util.LinkedHashMap.LinkedHashMap(int, float)'

+   */

+  public void testLinkedHashMapIntFloat() {

+

+    LinkedHashMap hashMap = new LinkedHashMap(CAPACITY_16, LOAD_FACTOR_ONE_HALF);

+    checkEmptyLinkedHashMapAssumptions(hashMap);

+

+    // TODO(mmendez): how do we verify capacity and load factor?

+

+    // Test new LinkedHashMap(-1, 0.0F)

+    boolean failed = true;

+    try {

+      new LinkedHashMap(CAPACITY_NEG_ONE_HALF, LOAD_FACTOR_ZERO);

+    } catch (Throwable ex) {

+      if (ex instanceof IllegalArgumentException) {

+        failed = false;

+      }

+    }

+

+    if (failed) {

+      fail("Failure testing new LinkedHashMap(-1, 0.0F)");

+    }

+

+    // Test new LinkedHashMap(0, -1.0F)

+    failed = true;

+    try {

+      new LinkedHashMap(CAPACITY_ZERO, LOAD_FACTOR_NEG_ONE);

+    } catch (Throwable ex) {

+      if (ex instanceof IllegalArgumentException) {

+        failed = false;

+      }

+    }

+

+    if (failed) {

+      fail("Failure testing new LinkedHashMap(0, -1.0F)");

+    }

+

+    // Test new LinkedHashMap(0,0F);

+    hashMap = new LinkedHashMap(CAPACITY_ZERO, LOAD_FACTOR_ONE_TENTH);

+    assertNotNull(hashMap);

+  }

+

+  /*

+   * Test method for 'java.util.LinkedHashMap.LinkedHashMap(Map)'

+   */

+  public void testLinkedHashMapMap() {

+    LinkedHashMap srcMap = new LinkedHashMap();

+    assertNotNull(srcMap);

+    checkEmptyLinkedHashMapAssumptions(srcMap);

+

+    srcMap.put(INTEGER_1, INTEGER_11);

+    srcMap.put(INTEGER_2, INTEGER_22);

+    srcMap.put(INTEGER_3, INTEGER_33);

+

+    LinkedHashMap hashMap = cloneLinkedHashMap(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 method for 'java.util.AbstractMap.isEmpty()'

+   */

+  public void testIsEmpty() {

+    LinkedHashMap<String, String> srcMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(srcMap);

+

+    LinkedHashMap<String, String> dstMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(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() {

+    LinkedHashMap<Object, String> hashMap = new LinkedHashMap<Object, String>();

+

+    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.LinkedHashMap.keySet()'

+   */

+  public void testKeySet() {

+    LinkedHashMap<String, String> hashMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(hashMap);

+

+    Set keySet = hashMap.keySet();

+    System.err.println("keySet:" + keySet);

+    assertNotNull(keySet);

+    assertTrue(keySet.isEmpty());

+    assertTrue(keySet.size() == 0);

+

+    hashMap.put(KEY_TEST_KEY_SET, VALUE_TEST_KEY_SET);

+    assertEquals(SIZE_ONE, keySet.size());

+    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.LinkedHashMap.put(Object, Object)'

+   */

+  public void testPut() {

+    LinkedHashMap<String, String> hashMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(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.LinkedHashMap.putAll(Map)'.

+   */

+  public void testPutAll() {

+    LinkedHashMap<String, String> srcMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(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

+    LinkedHashMap<String, String> dstMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(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

+    LinkedHashMap<String, String> emptyMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(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.LinkedHashMap.remove(Object)'.

+   */

+  public void testRemove() {

+    LinkedHashMap<String, String> hashMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(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.LinkedHashMap.size()'.

+   */

+  public void testSize() {

+    LinkedHashMap<String, String> hashMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(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);

+    LinkedHashMap srcMap = cloneLinkedHashMap(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() {

+    LinkedHashMap<String, String> hashMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(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() {

+    LinkedHashMap<String, String> hashMap = new LinkedHashMap<String, String>();

+    checkEmptyLinkedHashMapAssumptions(hashMap);

+

+    assertNotNull(hashMap.values());

+

+    hashMap.put(KEY_KEY, VALUE_VAL);

+

+    Collection<String> valColl = hashMap.values();

+    assertNotNull(valColl);

+    assertEquals(valColl.size(), SIZE_ONE);

+

+    Iterator<String> itVal = valColl.iterator();

+    String val = itVal.next();

+    assertEquals(val, VALUE_VAL);

+  }

+

+  @Override

+  protected Map makeEmptyMap() {

+    return new LinkedHashMap();

+  }

+

+  /**

+   * This method exists because java 1.5 no longer has

+   * LinkedHashMap(LinkedHashMap), replacing it with LinkedHashMap(Map<?

+   * extends K, ? extends V> m). Nevertheless, we want to use it in web mode to

+   * test that web mode function.

+   * 

+   * @param hashMap the LinkedHashMap to be copied

+   * @return the copy

+   */

+  private LinkedHashMap cloneLinkedHashMap(LinkedHashMap hashMap) {

+    if (GWT.isScript()) {

+      return new LinkedHashMap(hashMap);

+    } else {

+      LinkedHashMap m = new LinkedHashMap();

+      m.putAll(hashMap);

+      return m;

+    }

+  }

+

+  private Iterator<Map.Entry<Number, Object>> iterateThrough(

+      final LinkedHashMap<Number, Object> expected) {

+    Iterator<Map.Entry<Number, Object>> 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/com/google/gwt/user/client/rpc/CollectionsTest.java b/user/test/com/google/gwt/user/client/rpc/CollectionsTest.java
index 214dea4..b35e439 100644
--- a/user/test/com/google/gwt/user/client/rpc/CollectionsTest.java
+++ b/user/test/com/google/gwt/user/client/rpc/CollectionsTest.java
@@ -24,6 +24,7 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Vector;
 
@@ -264,6 +265,51 @@
     });
   }
 
+  public void testLinkedHashMap() {
+    delayTestFinish(TEST_DELAY);
+    CollectionsTestServiceAsync service = getServiceAsync();
+
+    final LinkedHashMap<String, IsSerializable> expected = TestSetFactory.createLinkedHashMap();
+
+    service.echo(expected,
+        new AsyncCallback<LinkedHashMap<String, IsSerializable>>() {
+          public void onFailure(Throwable caught) {
+            TestSetValidator.rethrowException(caught);
+          }
+
+          public void onSuccess(LinkedHashMap<String, IsSerializable> result) {
+            assertNotNull(result);
+            expected.get("SerializableSet");
+            result.get("SerializableSet");
+            assertTrue(TestSetValidator.isValid(expected,
+                (LinkedHashMap) result));
+            finishTest();
+          }
+        });
+  }
+
+  public void testLinkedHashMapLRU() {
+    delayTestFinish(TEST_DELAY);
+    CollectionsTestServiceAsync service = getServiceAsync();
+
+    final LinkedHashMap<String, IsSerializable> expected = TestSetFactory.createLRULinkedHashMap();
+
+    service.echo(expected,
+        new AsyncCallback<LinkedHashMap<String, IsSerializable>>() {
+          public void onFailure(Throwable caught) {
+            TestSetValidator.rethrowException(caught);
+          }
+
+          public void onSuccess(LinkedHashMap actual) {
+            assertNotNull(actual);
+            expected.get("SerializableSet");
+            actual.get("SerializableSet");
+            assertTrue(TestSetValidator.isValid(expected, actual));
+            finishTest();
+          }
+        });
+  }
+
   public void testLongArray() {
     delayTestFinish(TEST_DELAY);
 
diff --git a/user/test/com/google/gwt/user/client/rpc/CollectionsTestService.java b/user/test/com/google/gwt/user/client/rpc/CollectionsTestService.java
index 114f2c8..84c6330 100644
--- a/user/test/com/google/gwt/user/client/rpc/CollectionsTestService.java
+++ b/user/test/com/google/gwt/user/client/rpc/CollectionsTestService.java
@@ -21,6 +21,7 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Vector;
 
@@ -72,6 +73,10 @@
   HashMap<String, IsSerializable> echo(HashMap<String, IsSerializable> value)
       throws CollectionsTestServiceException;
 
+  LinkedHashMap<String, IsSerializable> echo(
+      LinkedHashMap<String, IsSerializable> value)
+      throws CollectionsTestServiceException;
+
   HashSet<IsSerializable> echo(HashSet<IsSerializable> value)
       throws CollectionsTestServiceException;
 
diff --git a/user/test/com/google/gwt/user/client/rpc/CollectionsTestServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/CollectionsTestServiceAsync.java
index 1a4d331..4c9187e 100644
--- a/user/test/com/google/gwt/user/client/rpc/CollectionsTestServiceAsync.java
+++ b/user/test/com/google/gwt/user/client/rpc/CollectionsTestServiceAsync.java
@@ -21,6 +21,7 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Vector;
 
@@ -58,6 +59,9 @@
   void echo(HashMap<String, IsSerializable> value,
       AsyncCallback<HashMap<String, IsSerializable>> callback);
 
+  void echo(LinkedHashMap<String, IsSerializable> value,
+      AsyncCallback<LinkedHashMap<String, IsSerializable>> callback);
+
   void echo(HashSet<IsSerializable> value,
       AsyncCallback<HashSet<IsSerializable>> callback);
 
diff --git a/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java b/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
index cbe483d..d5da927 100644
--- a/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
+++ b/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
@@ -22,6 +22,7 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Vector;
 
@@ -262,6 +263,27 @@
     return map;
   }
 
+  public static LinkedHashMap createLRULinkedHashMap() {
+    LinkedHashMap map = new LinkedHashMap(100, 1.0f, true);
+    map.put("SerializableNode", new SerializableNode());
+    map.put("SerializableList", new SerializableList());
+    map.put("SerializableMap", new SerializableMap());
+    map.put("SerializableSet", new SerializableSet());
+    map.put("SerializableVector", new SerializableVector());
+    map.get("SerializableMap");
+    return map;
+  }
+
+  public static LinkedHashMap createLinkedHashMap() {
+    LinkedHashMap map = new LinkedHashMap();
+    map.put("SerializableNode", new SerializableNode());
+    map.put("SerializableList", new SerializableList());
+    map.put("SerializableMap", new SerializableMap());
+    map.put("SerializableSet", new SerializableSet());
+    map.put("SerializableVector", new SerializableVector());
+    return map;
+  }
+
   public static HashSet createHashSet() {
     HashSet set = new HashSet();
     set.add(new SerializableNode());
diff --git a/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java b/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java
index dd663fb..d210669 100644
--- a/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java
+++ b/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java
@@ -24,6 +24,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -35,6 +36,15 @@
  */
 public class TestSetValidator {
 
+  public static boolean equals(Iterator expected, Iterator actual) {
+    while (expected.hasNext() && actual.hasNext()) {
+      if (!expected.next().equals(actual.next())) {
+        return false;
+      }
+    }
+    return expected.hasNext() == actual.hasNext();
+  }
+
   public static boolean equals(boolean[] expected, boolean[] actual) {
     if (actual == null) {
       return false;
@@ -276,6 +286,15 @@
     return true;
   }
 
+  public static boolean isValid(LinkedHashMap expected, LinkedHashMap map) {
+    if (isValid((Map) expected, (HashMap) map)) {
+      Iterator expectedEntries = expected.entrySet().iterator();
+      Iterator actualEntries = map.entrySet().iterator();
+      return equals(expectedEntries, actualEntries);
+    }
+    return false;
+  }
+
   public static boolean isValid(SerializableClass actual) {
     if (actual == null) {
       return false;
diff --git a/user/test/com/google/gwt/user/server/rpc/CollectionsTestServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/CollectionsTestServiceImpl.java
index c002e2e..cd61558 100644
--- a/user/test/com/google/gwt/user/server/rpc/CollectionsTestServiceImpl.java
+++ b/user/test/com/google/gwt/user/server/rpc/CollectionsTestServiceImpl.java
@@ -26,6 +26,7 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Vector;
 
@@ -184,6 +185,17 @@
   }
 
   @SuppressWarnings("unchecked")
+  public LinkedHashMap echo(LinkedHashMap actual)
+      throws CollectionsTestServiceException {
+    HashMap expected = TestSetFactory.createLinkedHashMap();
+    if (!TestSetValidator.isValid(expected, actual)) {
+      throw new CollectionsTestServiceException("expected:"
+          + expected.toString() + " actual:" + actual.toString());
+    }
+    return actual;
+  }
+
+  @SuppressWarnings("unchecked")
   public HashSet echo(HashSet actual) throws CollectionsTestServiceException {
     HashSet expected = TestSetFactory.createHashSet();
     if (!TestSetValidator.isValid(expected, actual)) {