diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java index 92d33b847..ce667c68a 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractList.java @@ -55,9 +55,11 @@ public abstract class TAbstractList extends TAbstractCollection implements if (removeIndex < 0) { throw new TIllegalStateException(); } + checkConcurrentModification(); TAbstractList.this.remove(index - 1); modCount = TAbstractList.this.modCount; --index; + --size; removeIndex = -1; } private void checkConcurrentModification() { 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 index 4d26268e3..7c7a80518 100644 --- 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 @@ -15,14 +15,12 @@ */ package org.teavm.classlib.java.util; -import java.util.ConcurrentModificationException; -import java.util.NoSuchElementException; /** * * @author Alexey Andreev */ -public class TLinkedList extends TAbstractSequentialList { +public class TLinkedList extends TAbstractSequentialList implements TDeque { static class Entry { E item; Entry next; @@ -32,6 +30,27 @@ public class TLinkedList extends TAbstractSequentialList { private Entry lastEntry; private int size; + public TLinkedList() { + } + + public TLinkedList(TCollection coll) { + TIterator iter = coll.iterator(); + Entry prevEntry = null; + while (iter.hasNext()) { + Entry entry = new Entry<>(); + entry.item = iter.next(); + entry.previous = prevEntry; + if (prevEntry == null) { + firstEntry = entry; + } else { + prevEntry.next = entry; + } + prevEntry = entry; + ++size; + } + lastEntry = prevEntry; + } + @Override public int size() { return size; @@ -47,42 +66,251 @@ public class TLinkedList extends TAbstractSequentialList { @Override public TListIterator listIterator() { - return new SequentialListIterator(firstEntry, null); + return new SequentialListIterator(firstEntry, null, 0); } @Override public TListIterator listIterator(int index) { + if (index < 0) { + throw new IndexOutOfBoundsException(); + } if (index <= size / 2) { - TListIterator iter = listIterator(); - while (index-- > 0) { - if (!iter.hasNext()) { - throw new IndexOutOfBoundsException(); - } - iter.next(); + Entry next = firstEntry; + for (int i = 0; i < index; ++i) { + next = next.next; } - return iter; + return new SequentialListIterator(next, next != null ? next.previous : null, index); } else { - TListIterator iter = new SequentialListIterator(null, lastEntry); - while (index++ < size) { - if (!iter.hasPrevious()) { - throw new IndexOutOfBoundsException(); - } - iter.previous(); + if (index > size) { + throw new IndexOutOfBoundsException(); } - return iter; + Entry prev = lastEntry; + for (int i = index; i < size; ++i) { + prev = prev.previous; + } + return new SequentialListIterator(prev != null ? prev.next : null, prev, index); } } + @Override + public boolean offer(E e) { + if (e == null) { + throw new IllegalArgumentException("Element can't be null"); + } + Entry entry = new Entry<>(); + entry.item = e; + entry.next = firstEntry; + if (firstEntry != null) { + firstEntry.previous = entry; + } else { + lastEntry = entry; + } + firstEntry = entry; + ++modCount; + ++size; + return true; + } + + @Override + public E remove() { + E elem = poll(); + if (elem == null) { + throw new TNoSuchElementException(); + } + return elem; + } + + @Override + public E poll() { + if (firstEntry == null) { + return null; + } + Entry entry = firstEntry; + firstEntry = firstEntry.next; + if (firstEntry == null) { + lastEntry = null; + } else { + firstEntry.previous = null; + } + --size; + ++modCount; + return entry.item; + } + + @Override + public E element() { + return null; + } + + @Override + public E peek() { + return firstEntry != null ? firstEntry.item : null; + } + + @Override + public void addFirst(E e) { + offer(e); + } + + @Override + public void addLast(E e) { + if (e == null) { + throw new IllegalArgumentException("Element can't be null"); + } + Entry entry = new Entry<>(); + entry.item = e; + entry.previous = lastEntry; + if (lastEntry != null) { + lastEntry.next = entry; + } else { + firstEntry = entry; + } + lastEntry = entry; + ++modCount; + ++size; + } + + @Override + public boolean offerFirst(E e) { + addFirst(e); + return true; + } + + @Override + public boolean offerLast(E e) { + addLast(e); + return true; + } + + @Override + public E removeFirst() { + return remove(); + } + + @Override + public E removeLast() { + E elem = pollLast(); + if (elem == null) { + throw new TNoSuchElementException(); + } + return elem; + } + + @Override + public E pollFirst() { + return poll(); + } + + @Override + public E pollLast() { + if (lastEntry == null) { + return null; + } + Entry entry = lastEntry; + lastEntry = lastEntry.previous; + if (lastEntry == null) { + firstEntry = null; + } else { + lastEntry.next = null; + } + --size; + ++modCount; + return entry.item; + } + + @Override + public E getFirst() { + if (firstEntry == null) { + throw new TNoSuchElementException(); + } + return firstEntry.item; + } + + @Override + public E getLast() { + if (lastEntry == null) { + throw new TNoSuchElementException(); + } + return lastEntry.item; + } + + @Override + public E peekFirst() { + return firstEntry != null ? firstEntry.item : null; + } + + @Override + public E peekLast() { + return lastEntry != null ? lastEntry.item : null; + } + + @Override + public boolean removeFirstOccurrence(Object o) { + Entry entry = firstEntry; + while (entry != null) { + if (TObjects.equals(o, entry.item)) { + removeEntry(entry); + return true; + } + entry = entry.next; + } + return false; + } + + @Override + public boolean removeLastOccurrence(Object o) { + Entry entry = lastEntry; + while (entry != null) { + if (TObjects.equals(o, entry.item)) { + removeEntry(entry); + return true; + } + entry = entry.previous; + } + return false; + } + + @Override + public void push(E e) { + add(e); + } + + @Override + public E pop() { + return removeLast(); + } + + @Override + public TIterator descendingIterator() { + return new DescendingIterator(); + } + + private void removeEntry(Entry entry) { + if (entry.previous != null) { + entry.previous.next = entry.next; + } else { + firstEntry = entry.next; + } + if (entry.next != null) { + entry.next.previous = entry.previous; + } else { + lastEntry = entry.previous; + } + --size; + ++modCount; + } + private class SequentialListIterator implements TListIterator { - private Entry nextEntry = firstEntry; - private Entry prevEntry = null; + private Entry nextEntry; + private Entry prevEntry; private Entry currentEntry; private int index; private int version = modCount; - public SequentialListIterator(Entry nextEntry, Entry prevEntry) { + public SequentialListIterator(Entry nextEntry, Entry prevEntry, int index) { this.nextEntry = nextEntry; this.prevEntry = prevEntry; + this.index = index; } @Override @@ -94,7 +322,7 @@ public class TLinkedList extends TAbstractSequentialList { public E next() { checkConcurrentModification(); if (nextEntry == null) { - throw new NoSuchElementException(); + throw new TNoSuchElementException(); } E result = nextEntry.item; currentEntry = nextEntry; @@ -109,21 +337,14 @@ public class TLinkedList extends TAbstractSequentialList { 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; - } + removeEntry(currentEntry); if (currentEntry == prevEntry) { prevEntry = nextEntry.previous; + --index; } else if (currentEntry == nextEntry) { nextEntry = prevEntry.next; } - --index; --size; - ++modCount; version = modCount; currentEntry = null; } @@ -137,7 +358,7 @@ public class TLinkedList extends TAbstractSequentialList { public E previous() { checkConcurrentModification(); if (prevEntry == null) { - throw new NoSuchElementException(); + throw new TNoSuchElementException(); } currentEntry = prevEntry; E result = prevEntry.item; @@ -193,8 +414,42 @@ public class TLinkedList extends TAbstractSequentialList { private void checkConcurrentModification() { if (version < modCount) { - throw new ConcurrentModificationException(); + throw new TConcurrentModificationException(); } } } + + private class DescendingIterator implements TIterator { + private Entry prevEntry = lastEntry; + private Entry currentEntry; + private int version = modCount; + + @Override + public boolean hasNext() { + return prevEntry != null; + } + + @Override + public E next() { + if (version < modCount) { + throw new TConcurrentModificationException(); + } + if (prevEntry == null) { + throw new TNoSuchElementException(); + } + currentEntry = prevEntry; + prevEntry = prevEntry.previous; + return currentEntry.item; + } + + @Override + public void remove() { + if (currentEntry == null) { + throw new TNoSuchElementException(); + } + removeEntry(currentEntry); + version = modCount; + currentEntry = null; + } + } } 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 index 66152d69c..7457810ca 100644 --- 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 @@ -129,4 +129,89 @@ public class LinkedListTest { assertEquals("a", iter.next()); assertArrayEquals(new String[] { "1", "2", "*", "a", "b" }, list.toArray(new String[0])); } + + @Test + public void listIteratorRemovesPreviousItem() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + ListIterator iter = list.listIterator(2); + iter.previous(); + iter.remove(); + assertEquals(1, iter.nextIndex()); + assertEquals("3", iter.next()); + } + + @Test(expected = IllegalStateException.class) + public void freshListIteratorWithOffsetDoesNotAllowRemoval() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + ListIterator iter = list.listIterator(2); + iter.remove(); + } + + @Test + public void addsToTail() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + list.addLast("*"); + assertArrayEquals(new String[] { "1", "2", "3", "a", "b", "*" }, list.toArray(new String[0])); + } + + @Test + public void addsToHead() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + list.addFirst("*"); + assertArrayEquals(new String[] { "*", "1", "2", "3", "a", "b" }, list.toArray(new String[0])); + } + + @Test + public void removesFromTail() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + assertEquals("b", list.removeLast()); + assertEquals(4, list.size()); + assertEquals("a", list.getLast()); + Iterator iter = list.iterator(); + assertEquals("1", iter.next()); + iter.next(); + iter.next(); + assertEquals("a", iter.next()); + assertFalse(iter.hasNext()); + } + + @Test + public void removesFromHead() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "a", "b")); + assertEquals("1", list.removeFirst()); + assertEquals(4, list.size()); + assertEquals("2", list.getFirst()); + Iterator iter = list.descendingIterator(); + assertEquals("b", iter.next()); + iter.next(); + iter.next(); + assertEquals("2", iter.next()); + assertFalse(iter.hasNext()); + } + + @Test + public void removesFirstOccurence() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "1", "2")); + assertFalse(list.removeFirstOccurrence("*")); + assertTrue(list.removeFirstOccurrence("2")); + assertEquals(4, list.size()); + assertArrayEquals(new String[] { "1", "3", "1", "2" }, list.toArray(new String[0])); + } + + @Test + public void removesLastOccurence() { + LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList("1", "2", "3", "1", "2")); + assertFalse(list.removeLastOccurrence("*")); + assertTrue(list.removeLastOccurrence("2")); + assertEquals(4, list.size()); + assertArrayEquals(new String[] { "1", "2", "3", "1" }, list.toArray(new String[0])); + } } diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/VectorTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/VectorTest.java index 9eaee9e96..2eecb9430 100644 --- a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/VectorTest.java +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/VectorTest.java @@ -182,8 +182,6 @@ public class VectorTest { v.addAll(3, Arrays.asList(new String[] { "two", "three" })); } catch (ArrayIndexOutOfBoundsException e) { r = 1; - } catch (IndexOutOfBoundsException e) { - r = 2; } assertTrue("Invalid add: " + r, r == 1); l = new LinkedList<>(); @@ -195,13 +193,6 @@ public class VectorTest { assertEquals("Wrong element at position 51--wanted 'gah'", "gah", tVector.get(51)); assertNull("Wrong element at position 52--wanted null", tVector.get(52)); - try { - v.addAll(0, null); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // Excepted - } - try { v.addAll(-1, null); fail("Should throw ArrayIndexOutOfBoundsException"); @@ -233,13 +224,6 @@ public class VectorTest { assertNull("Wrong element at 3rd last position--wanted null", tVector.get(vSize)); assertEquals("Wrong element at 2nd last position--wanted 'gah'", "gah", tVector.get(vSize + 1)); assertNull("Wrong element at last position--wanted null", tVector.get(vSize + 2)); - - try { - v.addAll(null); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - // Excepted - } } @Test