I18N constant maps are now guaranteed to iterate in declaration order. This allows a better degree of user control, and fixes inconsistencies between hosted and web mode. Review by: jat (desk) git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4608 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/i18n/client/Constants.java b/user/src/com/google/gwt/i18n/client/Constants.java index c5be987..8417853 100644 --- a/user/src/com/google/gwt/i18n/client/Constants.java +++ b/user/src/com/google/gwt/i18n/client/Constants.java
@@ -64,7 +64,7 @@ * * {@example com.google.gwt.examples.i18n.NumberFormatConstantsAnnot} * </p> - * + * * <p> * It is also possible to change the property name bound to a constant accessor * using the {@code @Key} annotation. For example, @@ -152,7 +152,8 @@ * the constant accessor <code>someMap()</code> would return a * <code>Map</code> that maps <code>"a"</code> onto <code>"X"</code>, * <code>"b"</code> onto <code>"Y"</code>, and <code>"c"</code> onto - * <code>"Z"</code>. + * <code>"Z"</code>. Iterating through this <code>Map</code> will return + * the keys or entries in declaration order. * </p> * * <p>The benefit of using annotations, aside from not having to switch to
diff --git a/user/src/com/google/gwt/i18n/client/impl/ConstantMap.java b/user/src/com/google/gwt/i18n/client/impl/ConstantMap.java index 0ecec17..f683e97 100644 --- a/user/src/com/google/gwt/i18n/client/impl/ConstantMap.java +++ b/user/src/com/google/gwt/i18n/client/impl/ConstantMap.java
@@ -20,10 +20,9 @@ import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; /** @@ -33,10 +32,62 @@ */ public class ConstantMap extends AbstractMap<String, String> { - /** - * A cache of a synthesized entry set. - */ - private Set<Map.Entry<String, String>> entries; + private static final class EntryImpl implements Entry<String, String> { + private final String key; + private final String value; + + private EntryImpl(String key, String value) { + assert (key != null); + assert (value != null); + this.key = key; + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Map.Entry) { + Map.Entry<?, ?> other = (Map.Entry<?, ?>) o; + // Key and value known to be non-null. + if (key.equals(other.getKey()) && value.equals(other.getValue())) { + return true; + } + } + return false; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + /** + * Calculate the hash code using Sun's specified algorithm. + */ + @Override + public int hashCode() { + int keyHash = 0; + int valueHash = 0; + if (getKey() != null) { + keyHash = getKey().hashCode(); + } + if (getValue() != null) { + valueHash = getValue().hashCode(); + } + return keyHash ^ valueHash; + } + + public String setValue(String value) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return key + "=" + value; + } + } /** * The original set of keys. @@ -56,6 +107,8 @@ init(); for (int i = 0; i < keys.length; ++i) { + assert keys[i] != null; + assert values[i] != null; putImpl(keys[i], values[i]); } } @@ -67,14 +120,48 @@ @Override public Set<Map.Entry<String, String>> entrySet() { - if (entries == null) { - Map<String, String> copy = new HashMap<String, String>(); - for (String key : keys) { - copy.put(key, get(key)); + return new AbstractSet<Entry<String, String>>() { + @Override + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry<?, ?> other = (Entry<?, ?>) o; + String value = get(other.getKey()); + if (value != null && value.equals(other.getValue())) { + return true; + } + return false; } - entries = Collections.unmodifiableMap(copy).entrySet(); - } - return entries; + + @Override + public Iterator<Map.Entry<String, String>> iterator() { + return new Iterator<Entry<String, String>>() { + private int next = 0; + + public boolean hasNext() { + return next < ConstantMap.this.size(); + } + + public Entry<String, String> next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + String key = keys[next++]; + return new EntryImpl(key, get(key)); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public int size() { + return ConstantMap.this.size(); + } + }; } @Override
diff --git a/user/src/com/google/gwt/i18n/rebind/ConstantsMapMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/ConstantsMapMethodCreator.java index a192966..ee6fa28 100644 --- a/user/src/com/google/gwt/i18n/rebind/ConstantsMapMethodCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/ConstantsMapMethodCreator.java
@@ -22,8 +22,8 @@ import com.google.gwt.i18n.rebind.AbstractResource.ResourceList; import com.google.gwt.user.rebind.AbstractGeneratorClassCreator; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.LinkedHashMap; +import java.util.Map; /** * Creator for methods of the form Map getX() . @@ -81,7 +81,8 @@ String[] keys = ConstantsStringArrayMethodCreator.split(keyString); ResourceList resources = getResources(); - SortedMap<String, String> map = new TreeMap<String, String>(); + // Use a LinkedHashMap to preserve declaration order (but remove dups). + Map<String, String> map = new LinkedHashMap<String, String>(); for (String key : keys) { if (key.length() == 0) { continue;