/* * Copyright 2014 Grow Bit * * 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 org.turbogwt.core.collections; import com.google.gwt.core.client.JavaScriptObject; import java.util.AbstractList; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; /** * An implementation of {@link java.util.List} wrapping a {@link JsArray}. * * @param <T> Type of list values * * @author Danilo Reinert */ public class JsArrayList<T> extends AbstractList<T> { private final JsArray<T> jsArray; @SuppressWarnings("unchecked") public JsArrayList() { this.jsArray = (JsArray<T>) JavaScriptObject.createArray(); } @SuppressWarnings("unchecked") public JsArrayList(JsArray<T> jsArray) { this.jsArray = (JsArray<T>) (jsArray != null ? jsArray : JavaScriptObject.createArray()); } @SuppressWarnings("unchecked") public JsArrayList(T... array) { this.jsArray = JsArray.fromArray(array); } public <E extends JavaScriptObject> JsArrayList(com.google.gwt.core.client.JsArray<E> jsArray) { this.jsArray = JsArray.cast(jsArray); } public JsArray<T> asJsArray() { return jsArray; } @Override public int size() { return jsArray.length(); } @Override public boolean contains(Object o) { return jsArray.indexOf(o) > -1; } @Override public Iterator<T> iterator() { return new Itr(); } @Override public Object[] toArray() { return jsArray.toArray(); } @Override @SuppressWarnings("unchecked") public <E> E[] toArray(E[] a) { if (a != null && a.length >= jsArray.length()) { for (int i = 0; i < jsArray.length(); i++) { E e = (E) jsArray.get(i); a[i] = e; } return a; } return (E[]) jsArray.toArray(); } @Override public boolean add(T t) { jsArray.push(t); return true; } @Override public native boolean remove(Object o) /*-{ var a = this.@org.turbogwt.core.collections.JsArrayList::jsArray; return a.splice(a.indexOf(o), 1).length > 0; }-*/; @Override public boolean containsAll(Collection<?> c) { boolean containsAll = true; for (Object o : c) { int indexOfIt = jsArray.indexOf(o); if (indexOfIt == -1) { containsAll = false; break; } } return containsAll; } @Override @SuppressWarnings("unchecked") public boolean addAll(Collection<? extends T> c) { jsArray.pushApply(JsArray.fromArray((T[]) c.toArray())); return true; } @Override @SuppressWarnings("unchecked") public boolean addAll(int i, Collection<? extends T> c) { JsArray<T> right = jsArray.slice(i); jsArray.splice(i, jsArray.length() - i); jsArray.pushApply(JsArray.fromArray((T[]) c.toArray())); jsArray.pushApply(right); return true; } @Override public boolean removeAll(Collection<?> c) { boolean changed = false; for (Object o : c) { Iterator<T> it = iterator(); while (it.hasNext()) { T t = it.next(); if (t.equals(o)) { it.remove(); changed = true; } } } return changed; } @Override public boolean retainAll(Collection<?> c) { boolean changed = false; Iterator<T> it = iterator(); while (it.hasNext()) { T t = it.next(); if (!c.contains(t)) { it.remove(); changed = true; } } return changed; } @Override public void clear() { jsArray.setLength(0); } @Override public T get(int i) { return jsArray.get(i); } @Override public T set(int i, T t) { if (i < -1 || i > jsArray.length()) throw new IndexOutOfBoundsException("Index: " + i); jsArray.set(i, t); return t; } @Override public void add(int i, T t) { if (i < -1 || i > jsArray.length()) throw new IndexOutOfBoundsException("Index: " + i); jsArray.splice(i, t); } @Override public T remove(int i) { if (i < 0 || i >= jsArray.length()) throw new IndexOutOfBoundsException(String.valueOf(i)); T toReturn = jsArray.get(i); jsArray.splice(i, 1); return toReturn; } @Override public int indexOf(Object o) { return jsArray.indexOf(o); } @Override public int lastIndexOf(Object o) { return jsArray.lastIndexOf(o); } @Override public ListIterator<T> listIterator() { return new ListItr(0); } @Override public ListIterator<T> listIterator(int i) { if (i < 0 || i > jsArray.length()) { throw new IndexOutOfBoundsException("Index: " + i); } return new ListItr(i); } @Override public List<T> subList(int i, int i2) { return new JsArrayList<>(subArray(i, i2)); } private native JsArray<T> subArray(int i, int i2) /*-{ return this.@org.turbogwt.core.collections.JsArrayList::jsArray.slice(i, i2); }-*/; private class Itr implements Iterator<T> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such public boolean hasNext() { return cursor != JsArrayList.this.size(); } public T next() { int i = cursor; if (i >= JsArrayList.this.size()) { throw new NoSuchElementException(); } cursor = i + 1; return JsArrayList.this.jsArray.get(lastRet = i); } public void remove() { if (lastRet < 0) { throw new IllegalStateException(); } try { JsArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } } private class ListItr extends Itr implements ListIterator<T> { ListItr(int index) { super(); cursor = index; } public boolean hasPrevious() { return cursor != 0; } public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; } public T previous() { int i = cursor - 1; if (i < 0) { throw new NoSuchElementException(); } cursor = i; return JsArrayList.this.jsArray.get(lastRet = i); } public void set(T e) { if (lastRet < 0) { throw new IllegalStateException(); } JsArrayList.this.set(lastRet, e); } public void add(T e) { int i = cursor; JsArrayList.this.add(i, e); cursor = i + 1; lastRet = -1; } } }