diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractQueue.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractQueue.java new file mode 100644 index 000000000..f0fe3ecdf --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractQueue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * 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.teavm.classlib.java.util; + +import org.teavm.classlib.java.lang.TIllegalStateException; + +/** + * + * @author Alexey Andreev + */ +public abstract class TAbstractQueue extends TAbstractCollection implements TQueue { + @Override + public boolean add(E e) { + if (offer(e)) { + return true; + } + throw new TIllegalStateException(); + } + + @Override + public E remove() { + if (isEmpty()) { + throw new TNoSuchElementException(); + } + return poll(); + } + + @Override + public E element() { + if (isEmpty()) { + throw new TNoSuchElementException(); + } + return peek(); + } + + @Override + public void clear() { + while (!isEmpty()) { + poll(); + } + } + + @Override + public boolean addAll(TCollection c) { + boolean oneAdded = false; + for (TIterator iter = c.iterator(); iter.hasNext();) { + oneAdded |= add(iter.next()); + } + return oneAdded; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrayDeque.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrayDeque.java new file mode 100644 index 000000000..163e2e81a --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TArrayDeque.java @@ -0,0 +1,376 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * 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.teavm.classlib.java.util; + +import java.util.Arrays; +import org.teavm.classlib.java.lang.*; + +/** + * + * @author Alexey Andreev + */ +public class TArrayDeque extends TAbstractCollection implements TDeque { + private int version; + private Object[] array; + private int head; + private int tail; + + public TArrayDeque() { + this(8); + } + + public TArrayDeque(int numElements) { + array = new Object[numElements + 1]; + } + + public TArrayDeque(TCollection c) { + if (c.isEmpty()) { + array = new Object[8]; + } else { + array = new Object[c.size() + 1]; + int index = 0; + for (TIterator iter = c.iterator(); iter.hasNext();) { + array[index++] = iter.next(); + } + tail = array.length - 1; + } + } + + @Override + public void addFirst(E e) { + if (e == null) { + throw new TNullPointerException(); + } + ensureCapacity(size() + 1); + --head; + if (head < 0) { + head += array.length; + } + array[head] = e; + ++version; + } + + @Override + public void addLast(E e) { + if (e == null) { + throw new TNullPointerException(); + } + ensureCapacity(size() + 1); + array[tail++] = e; + if (tail >= array.length) { + tail = 0; + } + ++version; + } + + @Override + public boolean offerFirst(E e) { + addFirst(e); + return true; + } + + @Override + public boolean offerLast(E e) { + addLast(e); + return true; + } + + @Override + public E removeFirst() { + E value = pollFirst(); + if (value == null) { + throw new TNoSuchElementException(); + } + return value; + } + + @Override + public E removeLast() { + E value = pollLast(); + if (value == null) { + throw new TNoSuchElementException(); + } + return value; + } + + @Override + public E pollFirst() { + if (head == tail) { + return null; + } + @SuppressWarnings("unchecked") + E result = (E)array[head]; + array[head] = null; + head++; + if (head >= array.length) { + head = 0; + } + ++version; + return result; + } + + @Override + public E pollLast() { + if (head == tail) { + return null; + } + --tail; + if (tail < 0) { + tail = array.length - 1; + } + @SuppressWarnings("unchecked") + E result = (E)array[tail]; + array[tail] = null; + ++version; + return result; + } + + @Override + public E getFirst() { + E result = peekFirst(); + if (result == null) { + throw new TNoSuchElementException(); + } + return result; + } + + @Override + public E getLast() { + E result = peekLast(); + if (result == null) { + throw new TNoSuchElementException(); + } + return result; + } + + @Override + @SuppressWarnings("unchecked") + public E peekFirst() { + return !isEmpty() ? (E)array[head] : null; + } + + @Override + @SuppressWarnings("unchecked") + public E peekLast() { + return !isEmpty() ? (E)array[tail > 0 ? tail - 1 : array.length - 1] : null; + } + + @Override + public boolean removeFirstOccurrence(Object o) { + if (o == null) { + return false; + } + for (TIterator iter = iterator(); iter.hasNext();) { + if (iter.next().equals(o)) { + iter.remove(); + return true; + } + } + return false; + } + + @Override + public boolean removeLastOccurrence(Object o) { + if (o == null) { + return false; + } + for (TIterator iter = descendingIterator(); iter.hasNext();) { + if (iter.next().equals(o)) { + iter.remove(); + return true; + } + } + return false; + } + + @Override + public boolean add(E e) { + addLast(e); + return true; + } + + @Override + public boolean offer(E e) { + return offerLast(e); + } + + @Override + public E remove() { + return removeFirst(); + } + + @Override + public E poll() { + return pollFirst(); + } + + @Override + public E element() { + return getFirst(); + } + + @Override + public E peek() { + return peekFirst(); + } + + @Override + public void push(E e) { + addFirst(e); + } + + @Override + public E pop() { + return removeFirst(); + } + + @Override + public int size() { + return tail >= head ? tail - head : array.length - head + tail; + } + + @Override + public boolean isEmpty() { + return head == tail; + } + + private void removeAt(int index) { + if (head < tail) { + if (tail - index < index - head) { + for (int i = index + 1; i < tail; ++i) { + array[i - 1] = array[i]; + } + array[tail - 1] = null; + } else { + for (int i = index - 1; i >= head; --i) { + array[i + 1] = array[i]; + } + array[head] = null; + } + } else { + if (index >= head) { + for (int i = index - 1; i >= head; --i) { + array[i + 1] = array[i]; + } + array[head] = null; + } else { + for (int i = index + 1; i < tail; ++i) { + array[i - 1] = array[i]; + } + array[tail - 1] = null; + } + } + } + + @Override + public TIterator iterator() { + return new TIterator() { + private int refVersion = version; + private int index = head; + private int lastIndex = -1; + private boolean wrapped = head <= tail; + @Override public boolean hasNext() { + return !wrapped || index < tail; + } + @Override public E next() { + if (version > refVersion) { + throw new TConcurrentModificationException(); + } + lastIndex = index; + @SuppressWarnings("unchecked") + E result = (E)array[index++]; + if (index >= array.length) { + index = 0; + wrapped = true; + } + return result; + } + @Override public void remove() { + removeAt(lastIndex); + lastIndex = -1; + } + }; + } + + @Override + public TIterator descendingIterator() { + return new TIterator() { + private int refVersion = version; + private int index = tail; + private int lastIndex = -1; + private boolean wrapped = head <= tail; + @Override public boolean hasNext() { + return !wrapped || index > head; + } + @Override public E next() { + if (version > refVersion) { + throw new TConcurrentModificationException(); + } + --index; + if (index < 0) { + index = array.length - 1; + wrapped = true; + } + lastIndex = index; + @SuppressWarnings("unchecked") + E result = (E)array[index]; + return result; + } + @Override public void remove() { + removeAt(lastIndex); + lastIndex = -1; + } + }; + } + + private void ensureCapacity(int capacity) { + if (capacity < array.length) { + return; + } + int newArraySize = TMath.max(array.length * 2, capacity * 3 / 2 + 1); + if (newArraySize < 1) { + newArraySize = TInteger.MAX_VALUE; + } + Object[] newArray = new Object[newArraySize]; + int j = 0; + if (head <= tail) { + for (int i = head; i < tail; ++i) { + newArray[j++] = array[i]; + } + } else { + for (int i = head; i < array.length; ++i) { + newArray[j++] = array[i]; + } + for (int i = 0; i < tail; ++i) { + newArray[j++] = array[i]; + } + } + head = 0; + tail = j; + array = newArray; + } + + @Override + public void clear() { + Arrays.fill(array, null); + head = tail; + } + + @Override + protected TArrayDeque clone() { + return new TArrayDeque<>(this); + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArrayDequeTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArrayDequeTest.java new file mode 100644 index 000000000..92ba9e4e1 --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/ArrayDequeTest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * 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.teavm.classlib.java.util; + +import static org.junit.Assert.*; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class ArrayDequeTest { + @Test + public void addsToFront() { + Deque deque = new ArrayDeque<>(); + deque.addFirst(1); + deque.addFirst(2); + Iterator iter = deque.iterator(); + assertEquals(2, deque.size()); + assertEquals((Integer)2, iter.next()); + assertEquals((Integer)1, iter.next()); + assertFalse(iter.hasNext()); + } + + @Test + public void addsToBack() { + Deque deque = new ArrayDeque<>(); + deque.addLast(1); + deque.addLast(2); + Iterator iter = deque.iterator(); + assertEquals(2, deque.size()); + assertEquals((Integer)1, iter.next()); + assertEquals((Integer)2, iter.next()); + assertFalse(iter.hasNext()); + } + + @Test + public void addsManyToFront() { + Deque deque = new ArrayDeque<>(); + for (int i = 0; i < 1000; ++i) { + deque.addFirst(i); + } + assertEquals(1000, deque.size()); + Iterator iter = deque.iterator(); + assertEquals((Integer)999, iter.next()); + for (int i = 2; i < 500; ++i) { + iter.next(); + } + assertEquals((Integer)500, iter.next()); + for (int i = 1; i < 500; ++i) { + iter.next(); + } + assertEquals((Integer)0, iter.next()); + } + + @Test + public void addsManyToBack() { + Deque deque = new ArrayDeque<>(); + for (int i = 0; i < 1000; ++i) { + deque.addLast(i); + } + assertEquals(1000, deque.size()); + Iterator iter = deque.iterator(); + assertEquals((Integer)0, iter.next()); + for (int i = 1; i < 500; ++i) { + iter.next(); + } + assertEquals((Integer)500, iter.next()); + for (int i = 2; i < 500; ++i) { + iter.next(); + } + assertEquals((Integer)999, iter.next()); + } + + @Test + public void removesFromFront() { + Deque deque = new ArrayDeque<>(); + deque.addFirst(1); + deque.addFirst(2); + assertEquals((Integer)2, deque.removeFirst()); + assertEquals((Integer)1, deque.removeFirst()); + assertEquals(0, deque.size()); + } + + @Test + public void removesFromBack() { + Deque deque = new ArrayDeque<>(); + deque.addFirst(1); + deque.addFirst(2); + assertEquals((Integer)1, deque.removeLast()); + assertEquals((Integer)2, deque.removeLast()); + assertEquals(0, deque.size()); + } + + @Test + public void addAndRemoves() { + Deque deque = new ArrayDeque<>(); + for (int i = 0; i < 100; ++i) { + deque.addLast(i); + } + assertEquals((Integer)0, deque.removeFirst()); + for (int i = 1; i < 20; ++i) { + deque.removeFirst(); + } + assertEquals((Integer)20, deque.removeFirst()); + for (int i = 101; i < 111; ++i) { + deque.addLast(i); + } + assertEquals((Integer)110, deque.removeLast()); + for (int i = 2; i < 40; ++i) { + deque.removeLast(); + } + assertEquals((Integer)70, deque.removeLast()); + } +}