package xapi.gwt.collect; import xapi.annotation.inject.InstanceOverride; import xapi.collect.api.CollectionOptions; import xapi.collect.api.IntTo; import xapi.collect.api.ObjectTo; import xapi.except.NotYetImplemented; import xapi.fu.In2Out1; import xapi.platform.GwtPlatform; import xapi.util.X_Util; import xapi.util.impl.AbstractPair; import com.google.gwt.core.client.GwtScriptOnly; import com.google.gwt.core.client.JavaScriptObject; import javax.inject.Provider; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @GwtPlatform @InstanceOverride(implFor=IntTo.class) @GwtScriptOnly public class IntToListGwt <E> extends JavaScriptObject implements IntTo<E>{ public static <V> IntTo<V> create(final Provider<V[]> arrayProvider) { return JavaScriptObject.createArray().<IntToListGwt<V>>cast().setArrayProvider(arrayProvider); } /** * This method is not safe for general use. It is only to be used when you need to apply generics to a given type; * unfortunately, the bounds let you send any subtype (not just one with enhanced generics). */ static <T, R extends T> IntTo<R> createForClass(final Class<T> cls) { return create(new Provider<R[]>() { @SuppressWarnings("unchecked") @Override public R[] get() { return (R[])Array.newInstance(cls, 0); } }); } public static native <V> IntTo<V> newInstance() /*-{ return []; }-*/; protected IntToListGwt() {} @Override public final boolean add(final E item) { set(size(), item); return true; } @Override @SuppressWarnings("unchecked") public final boolean addAll(final E... items) { concat(items); return true; } @Override public final boolean addAll(final Iterable<E> items) { for (final E item : items) { add(item); } return true; } @Override public final boolean addAll(final IntTo<E> items) { if (X_Util.isArray(items)) { concat(items); } else { items.forEachValue(this::add); } return true; } public final native void concat(Object array) /*-{ this.concat(array); }-*/; @Override public final Deque<E> asDeque() { final LinkedList<E> set = new LinkedList<E>(); for (final E e : forEach()) { set.add(e); } return set; } @Override public final List<E> asList() { final ArrayList<E> list = new ArrayList<E>(); for (final E e : forEach()) { list.add(e); } return list; } @Override public final Set<E> asSet() { final Set<E> set = new LinkedHashSet<E>(); for (final E e : forEach()) { set.add(e); } return set; } @Override public final E at(final int index) { return getValue(index); } @Override public final native void clear() /*-{ this.splice(0, this.length); }-*/; @Override public final ObjectTo<Integer, E> clone(final CollectionOptions options) { throw new NotYetImplemented("IntToList.clone not yet supported"); } @Override public final boolean contains(final E value) { for (final E e : forEach()) { if (X_Util.equal(value, e)) { return true; } } return false; } @Override @SuppressWarnings("unchecked") public final Entry<Integer, E> entryFor(final Object key) { return new AbstractPair<Integer, E>(size(), (E)key); } @Override public final boolean findRemove(final E value, final boolean all) { boolean removed = false; for (int i = 0, m = size(); i < m; i++) { final E next = getValue(i); if (X_Util.equal(next, value)) { remove(i--); if (!all) { return true; } removed = true; } } return removed; } @Override public final Iterable<E> forEach() { return new IntTo.IntToIterable<E>(this); } @Override public final E get(final Object key) { return getValue((Integer)key); } public final native E getValue(int key) /*-{ return this[key]||null; }-*/; @Override public final int indexOf(final E value) { for (int i = 0, m = size(); i < m; i++) { if (X_Util.equal(getValue(i), value)) { return i; } } return -1; } @Override public final native boolean insert(int pos, E item) /*-{ while(this.length < pos){ this.push(undefined); } this.splice(pos, 0, item); return true; }-*/; @Override public final boolean isEmpty() { return size()==0; } @Override public final native E pop() /*-{ return this.splice(this.length-1, 1); }-*/; @Override public final void push(final E value) { set(size(), value); } @Override public final E put(final Entry<Integer, E> item) { set(item.getKey(), item.getValue()); return item.getValue(); } @Override public final boolean remove(final int index) { if (index < size()) { return splice(index) != null; } return false; } @Override public final E remove(final Object key) { return splice((Integer)key); } @Override public final native void set(int index, E value) /*-{ while (this.length < index) this[this.length] = null; this[index] = value; }-*/; public final native IntToListGwt<E> setArrayProvider(Provider<E[]> provider) /*-{ this.toArray = function(){return provider.@javax.inject.Provider::get()();} return this; }-*/; @Override @SuppressWarnings("unchecked") public final void setValue(final Object key, final Object value) { set((Integer)key, (E)value); } @Override public final native int size() /*-{ return this.length; }-*/; public final native E splice(int key) /*-{ if (key < this.length) { var ret = this[key]; this.splice(key, 1); return ret; } return null; }-*/; @Override public final native E[] toArray() /*-{ if (this.toArray) { var arr = this.toArray(); for (var i in this) { if (i !== '_v$') { arr[i] = this[i]; } } return arr; } throw "toArray not supported"; }-*/; public final Class<Integer> keyType() { return Integer.class; } public final native Class<E> valueType() /*-{ if (this._v$) { return this._v$; } return @IntToListGwt::guessValueClass(*)(this); }-*/; private static final <E> Class<E> guessValueClass(IntToListGwt<E> from) { // Brutal, but effective return Class.class.cast(from.toArray().getClass().getComponentType()); } @Override public final Collection<E> toCollection(Collection<E> into) { if (into == null) { into = new ArrayList<E>(); } for (final E e : forEach()) { into.add(e); } return into; } @Override public final Map<Integer, E> toMap(Map<Integer, E> into) { if (into == null) { into = new LinkedHashMap<>(); } for (int i = 0, m = size(); i < m; i++) { into.put(i, getValue(i)); } return into; } @Override public final boolean readWhileTrue(In2Out1<Integer, E, Boolean> callback) { for (int i = 0, m = size(); i < m; i++ ) { if (!callback.io(i, get(i))) { return false; } } return true; } @Override public final String toString(Integer key, E value) { return String.valueOf(value); } }