diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractSequentialList.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractSequentialList.java new file mode 100644 index 000000000..cdb095cd4 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractSequentialList.java @@ -0,0 +1,87 @@ +/* + * 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.TIndexOutOfBoundsException; + +/** + * + * @author Alexey Andreev + */ +public abstract class TAbstractSequentialList extends TAbstractList { + @Override + public E get(int index) { + if (index < 0) { + throw new TIndexOutOfBoundsException(); + } + TIterator iter = listIterator(index); + return iter.next(); + } + + @Override + public E set(int index, E element) { + if (index < 0) { + throw new TIndexOutOfBoundsException(); + } + TListIterator iter = listIterator(index); + E old = iter.next(); + iter.set(element); + return old; + } + + @Override + public void add(int index, E element) { + if (index < 0) { + throw new TIndexOutOfBoundsException(); + } + TListIterator iter = listIterator(index); + iter.add(element); + } + + @Override + public E remove(int index) { + if (index < 0) { + throw new TIndexOutOfBoundsException(); + } + TListIterator iter = listIterator(index); + E elem = iter.next(); + iter.remove(); + return elem; + } + + @Override + public boolean addAll(int index, TCollection c) { + if (index < 0) { + throw new TIndexOutOfBoundsException(); + } + TListIterator iter = listIterator(index); + boolean added = false; + for (TIterator srcIter = c.iterator(); srcIter.hasNext();) { + iter.add(srcIter.next()); + iter.next(); + added = true; + } + return added; + } + + @Override + public TIterator iterator() { + return listIterator(); + } + + @Override + public abstract TListIterator listIterator(int index); +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLinkedList.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLinkedList.java new file mode 100644 index 000000000..4d26268e3 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLinkedList.java @@ -0,0 +1,200 @@ +/* + * 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.ConcurrentModificationException; +import java.util.NoSuchElementException; + +/** + * + * @author Alexey Andreev + */ +public class TLinkedList extends TAbstractSequentialList { + static class Entry { + E item; + Entry next; + Entry previous; + } + private Entry firstEntry; + private Entry lastEntry; + private int size; + + @Override + public int size() { + return size; + } + + @Override + public void clear() { + firstEntry = null; + lastEntry = null; + size = 0; + modCount++; + } + + @Override + public TListIterator listIterator() { + return new SequentialListIterator(firstEntry, null); + } + + @Override + public TListIterator listIterator(int index) { + if (index <= size / 2) { + TListIterator iter = listIterator(); + while (index-- > 0) { + if (!iter.hasNext()) { + throw new IndexOutOfBoundsException(); + } + iter.next(); + } + return iter; + } else { + TListIterator iter = new SequentialListIterator(null, lastEntry); + while (index++ < size) { + if (!iter.hasPrevious()) { + throw new IndexOutOfBoundsException(); + } + iter.previous(); + } + return iter; + } + } + + private class SequentialListIterator implements TListIterator { + private Entry nextEntry = firstEntry; + private Entry prevEntry = null; + private Entry currentEntry; + private int index; + private int version = modCount; + + public SequentialListIterator(Entry nextEntry, Entry prevEntry) { + this.nextEntry = nextEntry; + this.prevEntry = prevEntry; + } + + @Override + public boolean hasNext() { + return nextEntry != null; + } + + @Override + public E next() { + checkConcurrentModification(); + if (nextEntry == null) { + throw new NoSuchElementException(); + } + E result = nextEntry.item; + currentEntry = nextEntry; + prevEntry = nextEntry; + nextEntry = nextEntry.next; + ++index; + return result; + } + + @Override + public void remove() { + if (currentEntry == null) { + throw new IllegalStateException(); + } + currentEntry.next.previous = currentEntry.next; + currentEntry.previous.next = currentEntry.next; + if (currentEntry == firstEntry) { + firstEntry = firstEntry.next; + } else if (currentEntry == lastEntry) { + lastEntry = lastEntry.previous; + } + if (currentEntry == prevEntry) { + prevEntry = nextEntry.previous; + } else if (currentEntry == nextEntry) { + nextEntry = prevEntry.next; + } + --index; + --size; + ++modCount; + version = modCount; + currentEntry = null; + } + + @Override + public boolean hasPrevious() { + return prevEntry != null; + } + + @Override + public E previous() { + checkConcurrentModification(); + if (prevEntry == null) { + throw new NoSuchElementException(); + } + currentEntry = prevEntry; + E result = prevEntry.item; + nextEntry = prevEntry; + prevEntry = prevEntry.previous; + --index; + return result; + } + + @Override + public int nextIndex() { + return index; + } + + @Override + public int previousIndex() { + return index - 1; + } + + @Override + public void set(E e) { + if (currentEntry == null) { + throw new IllegalStateException(); + } + checkConcurrentModification(); + currentEntry.item = e; + currentEntry = null; + } + + @Override + public void add(E e) { + checkConcurrentModification(); + Entry newEntry = new Entry<>(); + newEntry.item = e; + newEntry.previous = prevEntry; + newEntry.next = nextEntry; + if (prevEntry != null) { + prevEntry.next = newEntry; + } else { + firstEntry = newEntry; + } + if (nextEntry != null) { + nextEntry.previous = newEntry; + } else { + lastEntry = newEntry; + } + prevEntry = newEntry; + ++size; + ++modCount; + version = modCount; + currentEntry = null; + } + + private void checkConcurrentModification() { + if (version < modCount) { + throw new ConcurrentModificationException(); + } + } + } +} diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedListTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedListTest.java new file mode 100644 index 000000000..66152d69c --- /dev/null +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/LinkedListTest.java @@ -0,0 +1,132 @@ +/* + * 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.*; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class LinkedListTest { + @Test + public void emptyListCreated() { + LinkedList list = new LinkedList<>(); + assertTrue(list.isEmpty()); + assertEquals(0, list.size()); + assertFalse(list.iterator().hasNext()); + } + + @Test + public void elementAdded() { + LinkedList list = new LinkedList<>(); + list.add("foo"); + Iterator iter = list.iterator(); + assertEquals("foo", iter.next()); + assertEquals(1, list.size()); + assertFalse(iter.hasNext()); + } + + @Test + public void elementRetrievedByIndex() { + LinkedList list = new LinkedList<>(); + list.add("foo"); + list.add("bar"); + list.add("baz"); + assertEquals("foo", list.get(0)); + assertEquals("bar", list.get(1)); + assertEquals("baz", list.get(2)); + } + + @Test + public void listIteratorPositioned() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + ListIterator iter = list.listIterator(1); + assertEquals(1, iter.nextIndex()); + assertEquals(0, iter.previousIndex()); + assertTrue(iter.hasNext()); + assertTrue(iter.hasPrevious()); + assertEquals("2", iter.next()); + } + + @Test + public void listIteratorMoved() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + ListIterator iter = list.listIterator(1); + assertEquals("2", iter.next()); + assertEquals("3", iter.next()); + assertEquals("a", iter.next()); + assertEquals("a", iter.previous()); + assertEquals("3", iter.previous()); + assertEquals(2, iter.nextIndex()); + assertEquals(1, iter.previousIndex()); + } + + @Test(expected = NoSuchElementException.class) + public void listInteratorCantMoveBeyondLowerBound() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + ListIterator iter = list.listIterator(1); + assertEquals("1", iter.previous()); + iter.previous(); + } + + @Test(expected = NoSuchElementException.class) + public void listInteratorCantMoveBeyondUpperBound() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + ListIterator iter = list.listIterator(4); + assertEquals("b", iter.next()); + iter.next(); + } + + @Test + public void listIteratorRemovesItem() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + ListIterator iter = list.listIterator(2); + assertEquals("3", iter.next()); + iter.remove(); + assertEquals(2, iter.nextIndex()); + assertEquals("a", iter.next()); + assertArrayEquals(new String[] { "1", "2", "a", "b" }, list.toArray(new String[0])); + } + + @Test + public void listIteratorAddsItem() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + ListIterator iter = list.listIterator(2); + iter.add("*"); + assertEquals("3", iter.next()); + assertArrayEquals(new String[] { "1", "2", "*", "3", "a", "b" }, list.toArray(new String[0])); + } + + @Test + public void listIteratorReplacesItem() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + ListIterator iter = list.listIterator(2); + iter.next(); + iter.set("*"); + assertEquals("a", iter.next()); + assertArrayEquals(new String[] { "1", "2", "*", "a", "b" }, list.toArray(new String[0])); + } +}