package xapi.gwt.collect; import xapi.collect.impl.ArrayIterable; import xapi.collect.impl.EntryValueAdapter; import xapi.collect.impl.IteratorWrapper; import com.google.gwt.core.client.JavaScriptObject; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; public class JsDictionary <V> extends JavaScriptObject { protected JsDictionary() { } static class KeyItr extends ArrayIterable<String>{ private final JsDictionary from; public KeyItr(JsDictionary from) { super(from.keyArray()); this.from = from; } @Override protected void remove(String key) { from.remove(key); } } static class EntryItr <V> implements Iterator<Entry<String, V>> { private final JsDictionary<V> from; int pos = 0; int max; Entry<String, V> entry; public EntryItr(JsDictionary <V> from) { max = from.keyArray().length; this.from = from; } @Override public boolean hasNext() { return pos < max; } @Override public Entry<String,V> next() { final String key = from.keyArray()[pos++]; final V next = from.get(key); entry = new Entry<String,V>() { @Override public String getKey() { return key; } @Override public V getValue() { return next; } @Override public V setValue(V value) { if (value == null) { return from.removeAndReturn(key); } else return from.put(key, value); } }; return entry; } @Override public void remove() { assert entry != null : "You must call next() before remove() in JsDictionary.entries()"; from.remove(entry.getKey()); } } public static native <V> JsDictionary<V> create(Class<V> valueType) /*-{ return {_v$: valueType, _k: @xapi.gwt.collect.JsDictionary::newStringArray()() }; }-*/; private static String[] newStringArray() { return new String[0]; } protected final native String[] keyArray() /*-{ return this._k; }-*/; public final native boolean containsKey(String key) /*-{ return this.hasOwnProperty(key)&&this[key] != undefined; }-*/; public final boolean containsValue(V value) { String[] keys = keyArray(); int i = keys.length; if (value == null) { for (;i-->0;) { if (get(keys[i]) == null) return true; } } else { for (;i-->0;) { if (value.equals(get(keys[i]))) return true; } } return false; } public final native V get(String key) /*-{ return this.hasOwnProperty(key) && this[key].v; }-*/; public final native V put(String key, V value) /*-{ if (this.hasOwnProperty(key)) { var slot = this._k[this._k.length], v = slot.v; slot.v = value; return v; } var slot = {i : this._k.length, v: value}; this[key] = slot; this._k[slot.i] = key; return null; }-*/; public final native boolean isEmpty() /*-{ return this._k.length == 0; }-*/; public final native void clear() /*-{ for ( var i = this._k.length; i -->0; delete this[this._k[i]] ); this._k.length = 0; }-*/; public final native boolean remove(String key) /*-{ if (this.hasOwnProperty(key)) { var index = this._k.indexOf(key); delete this[key]; this._k.splice(index, 1); return true; } // we don't allow you to delete items which aren't keys... like __prototype return false; }-*/; public final native V removeAndReturn(String key) /*-{ if (this.hasOwnProperty(key)) { var value = this[key]; delete this[key]; return value; } return null; }-*/; public final void putAll(Iterable<Entry<String,V>> items) { for (Entry<String, V> item : items) put(item.getKey(), item.getValue()); } public final void removeAll(Iterable<String> items) { for (String item : items) { remove(item); } } public final Iterable<String> keys() { return new KeyItr(this); } public final Iterable<V> values() { return new EntryValueAdapter<String, V>(entries()); } public final Iterable<Entry<String,V>> entries() { return new IteratorWrapper<Entry<String, V>>(new EntryItr(this)); } public final int size() { return keyArray().length; } public final V[] toArray() { // String[] keys = keyArray(); // // V[] array = GwtReflect.newArray(valueType(), keys.length); // for (int i = keys.length; i --> 0; array[i] = get(keys[i])); // return array; throw new UnsupportedOperationException(); } public final Collection<V> toCollection(Collection<V> into) { if (into == null) { into = new ArrayList<V>(); } String[] keys = keyArray(); for (int i = keys.length; i-->0;into.add(get(keys[i]))); return into; } public final Map<String,V> toMap(Map<String,V> into) { if (into == null) { into = new LinkedHashMap<String, V>(); } for (Entry<String,V> entry : entries()) { into.put(entry.getKey(), entry.getValue()); } return into; } public final native Class<V> valueType() /*-{ return this._v$; }-*/; public final Class<String> keyType() { return String.class; } }