From 87e44c59bce702324121b6d9a250259a4861b488 Mon Sep 17 00:00:00 2001 From: bvosburgh Date: Thu, 25 Jun 2009 15:15:37 +0000 Subject: Iterator work --- .../utility/internal/iterators/ArrayIterator.java | 12 +- .../internal/iterators/ArrayListIterator.java | 12 +- .../utility/internal/iterators/CloneIterator.java | 44 +- .../utility/internal/iterators/GraphIterator.java | 8 +- .../internal/iterators/SynchronizedIterator.java | 52 +++ .../iterators/SynchronizedListIterator.java | 77 ++++ .../internal/iterators/ArrayIteratorTests.java | 24 +- .../internal/iterators/ArrayListIteratorTests.java | 21 +- .../internal/iterators/CloneIteratorTests.java | 6 +- .../internal/iterators/CloneListIteratorTests.java | 4 +- .../internal/iterators/GraphIteratorTests.java | 4 +- .../iterators/JptUtilityIteratorsTests.java | 2 + .../iterators/SynchronizedIteratorTests.java | 313 +++++++++++++ .../iterators/SynchronizedListIteratorTests.java | 500 +++++++++++++++++++++ 14 files changed, 1009 insertions(+), 70 deletions(-) create mode 100644 jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/SynchronizedIterator.java create mode 100644 jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/SynchronizedListIterator.java create mode 100644 jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/SynchronizedIteratorTests.java create mode 100644 jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/SynchronizedListIteratorTests.java diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/ArrayIterator.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/ArrayIterator.java index 9e6aa342c4..7fffe95d0f 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/ArrayIterator.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/ArrayIterator.java @@ -23,8 +23,8 @@ public class ArrayIterator implements Iterator { final E[] array; // private-protected - int nextIndex; // private-protected - private final int maxIndex; + int cursor; // private-protected + private final int max; /** @@ -48,17 +48,17 @@ public class ArrayIterator throw new IllegalArgumentException("length: " + length); //$NON-NLS-1$ } this.array = array; - this.nextIndex = start; - this.maxIndex = start + length; + this.cursor = start; + this.max = start + length; } public boolean hasNext() { - return this.nextIndex < this.maxIndex; + return this.cursor != this.max; } public E next() { if (this.hasNext()) { - return this.array[this.nextIndex++]; + return this.array[this.cursor++]; } throw new NoSuchElementException(); } diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/ArrayListIterator.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/ArrayListIterator.java index 5ed13629d3..8a38965fcb 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/ArrayListIterator.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/ArrayListIterator.java @@ -24,7 +24,7 @@ public class ArrayListIterator extends ArrayIterator implements ListIterator { - private final int minIndex; + private final int min; /** @@ -41,24 +41,24 @@ public class ArrayListIterator */ public ArrayListIterator(E[] array, int start, int length) { super(array, start, length); - this.minIndex = start; + this.min = start; } public int nextIndex() { - return this.nextIndex; + return this.cursor; } public int previousIndex() { - return this.nextIndex - 1; + return this.cursor - 1; } public boolean hasPrevious() { - return this.nextIndex > this.minIndex; + return this.cursor != this.min; } public E previous() { if (this.hasPrevious()) { - return this.array[--this.nextIndex]; + return this.array[--this.cursor]; } throw new NoSuchElementException(); } diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/CloneIterator.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/CloneIterator.java index 3dfbd73226..81424d7ab1 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/CloneIterator.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/CloneIterator.java @@ -24,8 +24,8 @@ import org.eclipse.jpt.utility.internal.StringTools; * By default, a CloneIterator does not support the * #remove() operation; this is because it does not have * access to the original collection. But if the CloneIterator - * is supplied with an Mutator it will delegate the - * #remove() operation to the Mutator. + * is supplied with an Remover it will delegate the + * #remove() operation to the Remover. * Alternatively, a subclass can override the #remove(Object) * method. */ @@ -34,7 +34,7 @@ public class CloneIterator { private final Iterator iterator; private E current; - private final Mutator mutator; + private final Remover remover; private boolean removeAllowed; @@ -46,7 +46,7 @@ public class CloneIterator * unless a subclass overrides the #remove(Object). */ public CloneIterator(Collection collection) { - this(collection, Mutator.ReadOnly.instance()); + this(collection, Remover.ReadOnly.instance()); } /** @@ -55,25 +55,25 @@ public class CloneIterator * unless a subclass overrides the #remove(Object). */ public CloneIterator(E[] array) { - this(array, Mutator.ReadOnly.instance()); + this(array, Remover.ReadOnly.instance()); } /** * Construct an iterator on a copy of the specified collection. - * Use the specified mutator to remove objects from the + * Use the specified remover to remove objects from the * original collection. */ - public CloneIterator(Collection collection, Mutator mutator) { - this(mutator, collection.toArray()); + public CloneIterator(Collection collection, Remover remover) { + this(remover, collection.toArray()); } /** * Construct an iterator on a copy of the specified array. - * Use the specified mutator to remove objects from the + * Use the specified remover to remove objects from the * original array. */ - public CloneIterator(E[] array, Mutator mutator) { - this(mutator, array.clone()); + public CloneIterator(E[] array, Remover remover) { + this(remover, array.clone()); } /** @@ -81,11 +81,11 @@ public class CloneIterator * Swap order of arguments to prevent collision with other constructor. * The passed in array will *not* be cloned. */ - protected CloneIterator(Mutator mutator, Object... array) { + protected CloneIterator(Remover remover, Object... array) { super(); this.iterator = new ArrayIterator(array); this.current = null; - this.mutator = mutator; + this.remover = remover; this.removeAllowed = false; } @@ -128,10 +128,10 @@ public class CloneIterator * Remove the specified element from the original collection. *

* This method can be overridden by a subclass as an - * alternative to building an Mutator. + * alternative to building a Remover. */ protected void remove(E e) { - this.mutator.remove(e); + this.remover.remove(e); } @Override @@ -147,19 +147,19 @@ public class CloneIterator * elements from the original collection; since the iterator * does not have direct access to the original collection. */ - public interface Mutator { + public interface Remover { /** * Remove the specified object from the original collection. */ - void remove(T current); + void remove(T element); - final class ReadOnly implements Mutator { + final class ReadOnly implements Remover { @SuppressWarnings("unchecked") - public static final Mutator INSTANCE = new ReadOnly(); + public static final Remover INSTANCE = new ReadOnly(); @SuppressWarnings("unchecked") - public static Mutator instance() { + public static Remover instance() { return INSTANCE; } // ensure single instance @@ -167,12 +167,12 @@ public class CloneIterator super(); } // remove is not supported - public void remove(Object current) { + public void remove(Object element) { throw new UnsupportedOperationException(); } @Override public String toString() { - return "CloneIterator.Mutator.ReadOnly"; //$NON-NLS-1$ + return "CloneIterator.Remover.ReadOnly"; //$NON-NLS-1$ } } diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/GraphIterator.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/GraphIterator.java index 9b28f19117..c852b1f847 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/GraphIterator.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/GraphIterator.java @@ -52,8 +52,9 @@ import org.eclipse.jpt.utility.internal.StringTools; public class GraphIterator implements Iterator { - private final LinkedList> iterators; - private final HashSet visitedNeighbors; + // use a LinkedList since we will be pulling off the front and adding to the end + private final LinkedList> iterators = new LinkedList>(); + private final HashSet visitedNeighbors = new HashSet(); private final MisterRogers misterRogers; private Iterator currentIterator; @@ -141,10 +142,7 @@ public class GraphIterator public GraphIterator(Iterator roots, MisterRogers misterRogers) { super(); this.currentIterator = roots; - // use a LinkedList since we will be pulling off the front and adding to the end - this.iterators = new LinkedList>(); this.misterRogers = misterRogers; - this.visitedNeighbors = new HashSet(); this.loadNextNeighbor(); } diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/SynchronizedIterator.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/SynchronizedIterator.java new file mode 100644 index 0000000000..8b8721bed4 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/SynchronizedIterator.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0, which accompanies this distribution + * and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal.iterators; + +import java.util.Iterator; + +import org.eclipse.jpt.utility.internal.StringTools; + +/** + * Wrap an iterator and synchronize all its methods so it can be safely shared + * among multiple threads. + */ +public class SynchronizedIterator + implements Iterator +{ + private final Iterator iterator; + + + public SynchronizedIterator(Iterable iterable) { + this(iterable.iterator()); + } + + public SynchronizedIterator(Iterator iterator) { + super(); + this.iterator = iterator; + } + + public synchronized boolean hasNext() { + return this.iterator.hasNext(); + } + + public synchronized E next() { + return this.iterator.next(); + } + + public synchronized void remove() { + this.iterator.remove(); + } + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.iterator); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/SynchronizedListIterator.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/SynchronizedListIterator.java new file mode 100644 index 0000000000..1a52f50617 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/SynchronizedListIterator.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0, which accompanies this distribution + * and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal.iterators; + +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.jpt.utility.internal.StringTools; + +/** + * Wrap a list iterator and synchronize all its methods so it can be safely shared + * among multiple threads. + */ +public class SynchronizedListIterator + implements ListIterator +{ + private final ListIterator listIterator; + + + public SynchronizedListIterator(List list) { + this(list.listIterator()); + } + + public SynchronizedListIterator(ListIterator listIterator) { + super(); + this.listIterator = listIterator; + } + + public synchronized boolean hasNext() { + return this.listIterator.hasNext(); + } + + public synchronized E next() { + return this.listIterator.next(); + } + + public synchronized int nextIndex() { + return this.listIterator.nextIndex(); + } + + public synchronized boolean hasPrevious() { + return this.listIterator.hasPrevious(); + } + + public synchronized E previous() { + return this.listIterator.previous(); + } + + public synchronized int previousIndex() { + return this.listIterator.previousIndex(); + } + + public synchronized void remove() { + this.listIterator.remove(); + } + + public synchronized void add(E e) { + this.listIterator.add(e); + } + + public synchronized void set(E e) { + this.listIterator.set(e); + } + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.listIterator); + } + +} diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/ArrayIteratorTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/ArrayIteratorTests.java index 4e2e53d84b..a99d88d3fe 100644 --- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/ArrayIteratorTests.java +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/ArrayIteratorTests.java @@ -23,7 +23,7 @@ public class ArrayIteratorTests extends TestCase { public void testHasNext() { int i = 0; - for (Iterator stream = this.buildIterator(); stream.hasNext();) { + for (Iterator stream = this.buildIterator(); stream.hasNext(); ) { stream.next(); i++; } @@ -31,9 +31,9 @@ public class ArrayIteratorTests extends TestCase { } public void testNext() { - int i = 0; - for (Iterator stream = this.buildIterator(); stream.hasNext();) { - assertEquals("bogus element", ++i, Integer.parseInt(stream.next())); + int i = 1; + for (Iterator stream = this.buildIterator(); stream.hasNext(); ) { + assertEquals("bogus element", i++, Integer.parseInt(stream.next())); } } @@ -54,7 +54,7 @@ public class ArrayIteratorTests extends TestCase { public void testUnsupportedOperationException() { boolean exCaught = false; - for (Iterator stream = this.buildIterator(); stream.hasNext();) { + for (Iterator stream = this.buildIterator(); stream.hasNext(); ) { if (stream.next().equals("3")) { try { stream.remove(); @@ -79,21 +79,29 @@ public class ArrayIteratorTests extends TestCase { integers[1] = new Integer(1); integers[2] = new Integer(2); int i = 0; - for (Iterator stream = new ArrayIterator(integers); stream.hasNext();) { + for (Iterator stream = this.buildGenericIterator(integers); stream.hasNext();) { assertEquals(i++, stream.next().intValue()); } assertEquals(integers.length, i); } + Iterator buildGenericIterator(Integer[] integers) { + return new ArrayIterator(integers); + } + public void testVarargs() { int i = 0; - for (Iterator stream = new ArrayIterator(new Integer(0), new Integer(1), new Integer(2)); stream.hasNext();) { + for (Iterator stream = this.buildVarArgIterator(); stream.hasNext();) { assertEquals(i++, stream.next().intValue()); } assertEquals(3, i); } - public void triggerIllegalArgumentException(int start, int length) { + Iterator buildVarArgIterator() { + return new ArrayIterator(new Integer(0), new Integer(1), new Integer(2)); + } + + void triggerIllegalArgumentException(int start, int length) { boolean exCaught = false; Iterator stream = null; try { diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/ArrayListIteratorTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/ArrayListIteratorTests.java index d0a5ad1992..9685b6ff60 100644 --- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/ArrayListIteratorTests.java +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/ArrayListIteratorTests.java @@ -12,6 +12,7 @@ package org.eclipse.jpt.utility.tests.internal.iterators; import java.util.Iterator; import java.util.ListIterator; import java.util.NoSuchElementException; + import org.eclipse.jpt.utility.internal.iterators.ArrayListIterator; @SuppressWarnings("nls") @@ -109,25 +110,13 @@ public class ArrayListIteratorTests extends ArrayIteratorTests { } @Override - public void testGenerics() { - Integer[] integers = new Integer[3]; - integers[0] = new Integer(0); - integers[1] = new Integer(1); - integers[2] = new Integer(2); - int i = 0; - for (Iterator stream = new ArrayListIterator(integers); stream.hasNext();) { - assertEquals(i++, stream.next().intValue()); - } - assertEquals(integers.length, i); + Iterator buildGenericIterator(Integer[] integers) { + return new ArrayListIterator(integers); } @Override - public void testVarargs() { - int i = 0; - for (Iterator stream = new ArrayListIterator(new Integer(0), new Integer(1), new Integer(2)); stream.hasNext();) { - assertEquals(i++, stream.next().intValue()); - } - assertEquals(3, i); + Iterator buildVarArgIterator() { + return new ArrayListIterator(new Integer(0), new Integer(1), new Integer(2)); } private ListIterator buildListIterator() { diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/CloneIteratorTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/CloneIteratorTests.java index b35cf0a3a0..952df8b02f 100644 --- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/CloneIteratorTests.java +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/CloneIteratorTests.java @@ -91,9 +91,9 @@ public class CloneIteratorTests extends TestCase { } public void testRemoveEliminator() { - CloneIterator.Mutator eliminator = new CloneIterator.Mutator() { - public void remove(String current) { - CloneIteratorTests.this.originalCollection.remove(current); + CloneIterator.Remover eliminator = new CloneIterator.Remover() { + public void remove(String element) { + CloneIteratorTests.this.originalCollection.remove(element); } }; this.verifyRemove(new CloneIterator(this.originalCollection, eliminator)); diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/CloneListIteratorTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/CloneListIteratorTests.java index 0d43074560..44652f3819 100644 --- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/CloneListIteratorTests.java +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/CloneListIteratorTests.java @@ -250,7 +250,7 @@ public class CloneListIteratorTests extends TestCase { } catch (IllegalStateException ex) { exCaught = true; } - assertTrue("IllegalStateException not thrown", exCaught); + assertTrue(exCaught); while (iterator.hasNext()) { String next = iterator.next(); if (next.equals(addedAfter)) { @@ -265,7 +265,7 @@ public class CloneListIteratorTests extends TestCase { } catch (IllegalStateException ex) { exCaught = true; } - assertTrue("IllegalStateException not thrown", exCaught); + assertTrue(exCaught); } if (next.equals(replaced)) { iterator.set(replacement); diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/GraphIteratorTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/GraphIteratorTests.java index 4e4c80d0ef..b0e7ebd428 100644 --- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/GraphIteratorTests.java +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/GraphIteratorTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2007 Oracle. All rights reserved. + * Copyright (c) 2005, 2009 Oracle. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0, which accompanies this distribution * and is available at http://www.eclipse.org/legal/epl-v10.html. @@ -162,7 +162,7 @@ public class GraphIteratorTests extends TestCase { return ncNode; } - private class GraphNode { + public class GraphNode { private String name; private Collection neighbors = new ArrayList(); diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/JptUtilityIteratorsTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/JptUtilityIteratorsTests.java index 132b2c7d63..8a660b0ff4 100644 --- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/JptUtilityIteratorsTests.java +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/JptUtilityIteratorsTests.java @@ -38,6 +38,8 @@ public class JptUtilityIteratorsTests { suite.addTestSuite(ReadOnlyListIteratorTests.class); suite.addTestSuite(SingleElementIteratorTests.class); suite.addTestSuite(SingleElementListIteratorTests.class); + suite.addTestSuite(SynchronizedIteratorTests.class); + suite.addTestSuite(SynchronizedListIteratorTests.class); suite.addTestSuite(TransformationIteratorTests.class); suite.addTestSuite(TransformationListIteratorTests.class); suite.addTestSuite(TreeIteratorTests.class); diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/SynchronizedIteratorTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/SynchronizedIteratorTests.java new file mode 100644 index 0000000000..cd527b228e --- /dev/null +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/SynchronizedIteratorTests.java @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0, which accompanies this distribution + * and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.tests.internal.iterators; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import junit.framework.TestCase; + +import org.eclipse.jpt.utility.internal.CollectionTools; +import org.eclipse.jpt.utility.internal.iterators.SynchronizedIterator; + +@SuppressWarnings("nls") +public class SynchronizedIteratorTests extends TestCase { + + public SynchronizedIteratorTests(String name) { + super(name); + } + + /** + * test that an unsynchronized iterator will produce corrupt output; + * thread 1 will read the first element from the iterator + * and then sleep for a bit, allowing thread 2 to sneak in and + * read the same element from the iterator + */ + public void testUnsynchronizedNext() throws Exception { + TestIterator iterator = this.buildNestedIterator(); + + NextTestThread thread1 = new NextTestThread(iterator); + NextTestThread thread2 = new NextTestThread(iterator); + iterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // both threads should have read the same element from the iterator :-( + assertEquals("foo", thread1.next); + assertEquals("foo", thread2.next); + } + + /** + * test that a synchronized iterator will produce valid output; + * thread 1 will read the first element from the iterator + * and then sleep for a bit, but thread 2 will be locked out and + * wait to read the second element from the iterator + */ + public void testSynchronizedNext() throws Exception { + TestIterator nestedIterator = this.buildNestedIterator(); + Iterator iterator = this.buildSynchronizedIterator(nestedIterator); + + NextTestThread thread1 = new NextTestThread(iterator); + NextTestThread thread2 = new NextTestThread(iterator); + nestedIterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // the threads should have read the correct elements from the iterator :-) + assertEquals("foo", thread1.next); + assertEquals("bar", thread2.next); + } + + public void testUnsynchronizedHasNext() throws Exception { + TestIterator iterator = this.buildNestedIterator(); + iterator.next(); + iterator.next(); + + NextTestThread thread1 = new NextTestThread(iterator); + HasNextTestThread thread2 = new HasNextTestThread(iterator); + iterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // thread 1 will have the last element, + // but thread 2 will think there are more elements on the iterator :-( + assertEquals("baz", thread1.next); + assertEquals(true, thread2.hasNext); + } + + public void testSynchronizedHasNext() throws Exception { + TestIterator nestedIterator = this.buildNestedIterator(); + Iterator iterator = this.buildSynchronizedIterator(nestedIterator); + iterator.next(); + iterator.next(); + + NextTestThread thread1 = new NextTestThread(iterator); + HasNextTestThread thread2 = new HasNextTestThread(iterator); + nestedIterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // thread 1 will have the last element, + // and thread 2 will think there are no more elements on the iterator :-) + assertEquals("baz", thread1.next); + assertEquals(false, thread2.hasNext); + } + + public void testUnsynchronizedRemove() throws Exception { + TestIterator iterator = this.buildNestedIterator(); + iterator.next(); + + NextTestThread thread1 = new NextTestThread(iterator); + RemoveTestThread thread2 = new RemoveTestThread(iterator); + iterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // the wrong element was removed :-( + assertEquals("bar", thread1.next); + assertFalse(iterator.list.contains("foo")); + assertTrue(iterator.list.contains("bar")); + assertTrue(iterator.list.contains("baz")); + } + + public void testSynchronizedRemove() throws Exception { + TestIterator nestedIterator = this.buildNestedIterator(); + Iterator iterator = this.buildSynchronizedIterator(nestedIterator); + iterator.next(); + + NextTestThread thread1 = new NextTestThread(iterator); + RemoveTestThread thread2 = new RemoveTestThread(iterator); + nestedIterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // the wrong element was removed :-( + assertEquals("bar", thread1.next); + assertTrue(nestedIterator.list.contains("foo")); + assertFalse(nestedIterator.list.contains("bar")); + assertTrue(nestedIterator.list.contains("baz")); + } + + TestIterator buildNestedIterator() { + return new TestIterator(this.buildArray()); + } + + String[] buildArray() { + return new String[] {"foo", "bar", "baz"}; + } + + Iterator buildSynchronizedIterator(Iterator nestedIterator) { + return new SynchronizedIterator(nestedIterator); + } + + + /** + * next thread + */ + class NextTestThread extends Thread { + final Iterator iterator; + E next; + + NextTestThread(Iterator iterator) { + super(); + this.iterator = iterator; + } + + @Override + public void run() { + this.next = this.iterator.next(); + } + + } + + /** + * has next thread + */ + class HasNextTestThread extends Thread { + final Iterator iterator; + boolean hasNext; + + HasNextTestThread(Iterator iterator) { + super(); + this.iterator = iterator; + } + + @Override + public void run() { + this.hasNext = this.iterator.hasNext(); + } + + } + + /** + * remove thread + */ + class RemoveTestThread extends Thread { + final Iterator iterator; + + RemoveTestThread(Iterator iterator) { + super(); + this.iterator = iterator; + } + + @Override + public void run() { + this.iterator.remove(); + } + + } + + /** + * test iterator + */ + class TestIterator implements Iterator { + final ArrayList list; + int nextIndex; + int lastIndex = -1; + Thread slowThread; + + TestIterator(E... array) { + super(); + this.list = new ArrayList(); + CollectionTools.addAll(this.list, array); + this.nextIndex = 0; + } + + public boolean hasNext() { + return this.nextIndex != this.list.size(); + } + + public E next() { + if (this.hasNext()) { + E next = this.list.get(this.nextIndex); + this.sleep(); + this.lastIndex = this.nextIndex++; + return next; + } + throw new NoSuchElementException(); + } + + public void remove() { + if (this.lastIndex == -1) { + throw new IllegalStateException(); + } + this.list.remove(this.lastIndex); + if (this.lastIndex < this.nextIndex) { // check necessary for ListIterator + this.nextIndex--; + } + this.lastIndex = -1; + } + + void sleep() { + if (Thread.currentThread() == this.slowThread) { + SynchronizedIteratorTests.sleep(200); + } + } + + } + + static void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/SynchronizedListIteratorTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/SynchronizedListIteratorTests.java new file mode 100644 index 0000000000..de5f53dc8d --- /dev/null +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/iterators/SynchronizedListIteratorTests.java @@ -0,0 +1,500 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0, which accompanies this distribution + * and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.tests.internal.iterators; + +import java.util.Iterator; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import org.eclipse.jpt.utility.internal.iterators.SynchronizedListIterator; + +@SuppressWarnings("nls") +public class SynchronizedListIteratorTests extends SynchronizedIteratorTests { + + public SynchronizedListIteratorTests(String name) { + super(name); + } + + public void testUnsynchronizedPrevious() throws Exception { + TestListIterator iterator = this.buildNestedIterator(); + iterator.next(); + iterator.next(); + + PreviousTestThread thread1 = new PreviousTestThread(iterator); + PreviousTestThread thread2 = new PreviousTestThread(iterator); + iterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // both threads should have read the same element from the iterator :-( + assertEquals("bar", thread1.previous); + assertEquals("bar", thread2.previous); + } + + public void testSynchronizedPrevious() throws Exception { + TestListIterator nestedIterator = this.buildNestedIterator(); + ListIterator iterator = this.buildSynchronizedIterator(nestedIterator); + iterator.next(); + iterator.next(); + + PreviousTestThread thread1 = new PreviousTestThread(iterator); + PreviousTestThread thread2 = new PreviousTestThread(iterator); + nestedIterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // the threads should have read the correct elements from the iterator :-) + assertEquals("bar", thread1.previous); + assertEquals("foo", thread2.previous); + } + + public void testUnsynchronizedHasPrevious() throws Exception { + TestListIterator iterator = this.buildNestedIterator(); + iterator.next(); + + PreviousTestThread thread1 = new PreviousTestThread(iterator); + HasPreviousTestThread thread2 = new HasPreviousTestThread(iterator); + iterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // thread 1 will have the first element, + // but thread 2 will think there are more "previous" elements on the iterator :-( + assertEquals("foo", thread1.previous); + assertEquals(true, thread2.hasPrevious); + } + + public void testSynchronizedHasPrevious() throws Exception { + TestListIterator nestedIterator = this.buildNestedIterator(); + ListIterator iterator = this.buildSynchronizedIterator(nestedIterator); + iterator.next(); + + PreviousTestThread thread1 = new PreviousTestThread(iterator); + HasPreviousTestThread thread2 = new HasPreviousTestThread(iterator); + nestedIterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // thread 1 will have the first element, + // and thread 2 will think there are no more "previous" elements on the iterator :-) + assertEquals("foo", thread1.previous); + assertEquals(false, thread2.hasPrevious); + } + + public void testUnsynchronizedNextIndex() throws Exception { + TestListIterator iterator = this.buildNestedIterator(); + + NextTestThread thread1 = new NextTestThread(iterator); + NextIndexTestThread thread2 = new NextIndexTestThread(iterator); + iterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // thread 1 will have the first element, + // but thread 2 will think the next index is still 0 :-( + assertEquals("foo", thread1.next); + assertEquals(0, thread2.nextIndex); + } + + public void testSynchronizedNextIndex() throws Exception { + TestListIterator nestedIterator = this.buildNestedIterator(); + ListIterator iterator = this.buildSynchronizedIterator(nestedIterator); + + NextTestThread thread1 = new NextTestThread(iterator); + NextIndexTestThread thread2 = new NextIndexTestThread(iterator); + nestedIterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // thread 1 will have the first element, + // and thread 2 will think the next index is 1 :-) + assertEquals("foo", thread1.next); + assertEquals(1, thread2.nextIndex); + } + + public void testUnsynchronizedPreviousIndex() throws Exception { + TestListIterator iterator = this.buildNestedIterator(); + iterator.next(); + + PreviousTestThread thread1 = new PreviousTestThread(iterator); + PreviousIndexTestThread thread2 = new PreviousIndexTestThread(iterator); + iterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // thread 1 will have the first element, + // but thread 2 will think the next index is still 0 :-( + assertEquals("foo", thread1.previous); + assertEquals(0, thread2.previousIndex); + } + + public void testSynchronizedPreviousIndex() throws Exception { + TestListIterator nestedIterator = this.buildNestedIterator(); + ListIterator iterator = this.buildSynchronizedIterator(nestedIterator); + iterator.next(); + + PreviousTestThread thread1 = new PreviousTestThread(iterator); + PreviousIndexTestThread thread2 = new PreviousIndexTestThread(iterator); + nestedIterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // thread 1 will have the first element, + // and thread 2 will think the next index is -1 :-) + assertEquals("foo", thread1.previous); + assertEquals(-1, thread2.previousIndex); + } + + public void testUnsynchronizedSet() throws Exception { + TestListIterator iterator = this.buildNestedIterator(); + iterator.next(); + + NextTestThread thread1 = new NextTestThread(iterator); + SetTestThread thread2 = new SetTestThread(iterator, "xxx"); + iterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // the wrong element was set :-( + assertEquals("bar", thread1.next); + assertFalse(iterator.list.contains("foo")); + assertTrue(iterator.list.contains("xxx")); + assertTrue(iterator.list.contains("bar")); + assertTrue(iterator.list.contains("baz")); + } + + public void testSynchronizedSet() throws Exception { + TestListIterator nestedIterator = this.buildNestedIterator(); + ListIterator iterator = this.buildSynchronizedIterator(nestedIterator); + iterator.next(); + + NextTestThread thread1 = new NextTestThread(iterator); + SetTestThread thread2 = new SetTestThread(iterator, "xxx"); + nestedIterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // the right element was set :-) + assertEquals("bar", thread1.next); + assertTrue(nestedIterator.list.contains("foo")); + assertFalse(nestedIterator.list.contains("bar")); + assertTrue(nestedIterator.list.contains("xxx")); + assertTrue(nestedIterator.list.contains("baz")); + } + + public void testUnsynchronizedAdd() throws Exception { + TestListIterator iterator = this.buildNestedIterator(); + iterator.next(); + + NextTestThread thread1 = new NextTestThread(iterator); + AddTestThread thread2 = new AddTestThread(iterator, "xxx"); + iterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // the element was added at the wrong index :-( + assertEquals("bar", thread1.next); + assertTrue(iterator.list.contains("foo")); + assertEquals(0, iterator.list.indexOf("xxx")); + assertTrue(iterator.list.contains("xxx")); + assertTrue(iterator.list.contains("bar")); + assertTrue(iterator.list.contains("baz")); + } + + public void testSynchronizedAdd() throws Exception { + TestListIterator nestedIterator = this.buildNestedIterator(); + ListIterator iterator = this.buildSynchronizedIterator(nestedIterator); + iterator.next(); + + NextTestThread thread1 = new NextTestThread(iterator); + AddTestThread thread2 = new AddTestThread(iterator, "xxx"); + nestedIterator.slowThread = thread1; + + thread1.start(); + + // allow thread 1 to read the first element and get bogged down + sleep(100); + thread2.start(); + + // wait for the threads to finish + thread1.join(); + thread2.join(); + + // the element was added at the correct index :-) + assertEquals("bar", thread1.next); + assertTrue(nestedIterator.list.contains("foo")); + assertEquals(1, nestedIterator.list.indexOf("xxx")); + assertTrue(nestedIterator.list.contains("xxx")); + assertTrue(nestedIterator.list.contains("bar")); + assertTrue(nestedIterator.list.contains("baz")); + } + + @Override + ListIterator buildSynchronizedIterator(Iterator nestedIterator) { + return new SynchronizedListIterator((ListIterator) nestedIterator); + } + + @Override + TestListIterator buildNestedIterator() { + return new TestListIterator(this.buildArray()); + } + + /** + * previous thread + */ + class PreviousTestThread extends Thread { + final ListIterator iterator; + E previous; + + PreviousTestThread(ListIterator iterator) { + super(); + this.iterator = iterator; + } + + @Override + public void run() { + this.previous = this.iterator.previous(); + } + + } + + /** + * has previous thread + */ + class HasPreviousTestThread extends Thread { + final ListIterator iterator; + boolean hasPrevious; + + HasPreviousTestThread(ListIterator iterator) { + super(); + this.iterator = iterator; + } + + @Override + public void run() { + this.hasPrevious = this.iterator.hasPrevious(); + } + + } + + /** + * next index thread + */ + class NextIndexTestThread extends Thread { + final ListIterator iterator; + int nextIndex; + + NextIndexTestThread(ListIterator iterator) { + super(); + this.iterator = iterator; + } + + @Override + public void run() { + this.nextIndex = this.iterator.nextIndex(); + } + + } + + /** + * previous index thread + */ + class PreviousIndexTestThread extends Thread { + final ListIterator iterator; + int previousIndex; + + PreviousIndexTestThread(ListIterator iterator) { + super(); + this.iterator = iterator; + } + + @Override + public void run() { + this.previousIndex = this.iterator.previousIndex(); + } + + } + + /** + * set thread + */ + class SetTestThread extends Thread { + final ListIterator iterator; + final E element; + + SetTestThread(ListIterator iterator, E element) { + super(); + this.iterator = iterator; + this.element = element; + } + + @Override + public void run() { + this.iterator.set(this.element); + } + + } + + /** + * add thread + */ + class AddTestThread extends Thread { + final ListIterator iterator; + final E element; + + AddTestThread(ListIterator iterator, E element) { + super(); + this.iterator = iterator; + this.element = element; + } + + @Override + public void run() { + this.iterator.add(this.element); + } + + } + + /** + * test list iterator + */ + class TestListIterator extends TestIterator implements ListIterator { + + TestListIterator(E... array) { + super(array); + } + + public int nextIndex() { + return this.nextIndex; + } + + public boolean hasPrevious() { + return this.nextIndex != 0; + } + + public E previous() { + if (this.hasPrevious()) { + E previous = this.list.get(this.previousIndex()); + sleep(); + this.nextIndex--; + this.lastIndex = this.nextIndex; + return previous; + } + throw new NoSuchElementException(); + } + + public int previousIndex() { + return this.nextIndex - 1; + } + + public void set(E e) { + if (this.lastIndex == -1) { + throw new IllegalStateException(); + } + this.list.set(this.lastIndex, e); + } + + public void add(E e) { + this.list.add(this.lastIndex, e); + this.lastIndex++; + this.lastIndex = -1; + } + + } + +} -- cgit v1.2.3