bug 208434 - WritableSet and WritableList do not support concurrently modifiable iterators
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/WritableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/WritableList.java
index bc9a2f5..d5d41d8 100644
--- a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/WritableList.java
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/WritableList.java
@@ -11,13 +11,15 @@
  *     Gautam Saggar - bug 169529
  *     Brad Reynolds - bug 147515
  *     Sebastian Fuchs <spacehorst@gmail.com> - bug 243848
- *     Matthew Hall - bugs 208858, 213145, 243848
+ *     Matthew Hall - bugs 208858, 213145, 243848, 208434
  *     Ovidio Mallo - bug 332367
+ *     Nigel Westbury - bug 335792
  *******************************************************************************/
 package org.eclipse.core.databinding.observable.list;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.ConcurrentModificationException;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
@@ -26,7 +28,7 @@
 import org.eclipse.core.databinding.observable.Realm;
 
 /**
- * Mutable observable list backed by an ArrayList.
+ * Mutable observable list backed by a java.util.List.
  * 
  * <p>
  * This class is thread safe. All state accessing methods must be invoked from
@@ -63,19 +65,37 @@
 	 * Constructs a new instance with the default realm. Note that for backwards
 	 * compatibility reasons, the contents of the created WritableList will
 	 * change with the contents of the given list. If this is not desired,
-	 * {@link #WritableList(Collection, Object)} should be used by casting the
+	 * {@link #WritableList(Collection, Class)} should be used by casting the
 	 * first argument to {@link Collection}.
 	 * 
 	 * @param toWrap
 	 *            The java.util.List to wrap
 	 * @param elementType
 	 *            can be <code>null</code>
+	 * @deprecated use the form that takes Class<E> as the elementType parameter
 	 */
 	public WritableList(List<E> toWrap, Object elementType) {
 		this(Realm.getDefault(), toWrap, elementType);
 	}
 
 	/**
+	 * Constructs a new instance with the default realm. Note that for backwards
+	 * compatibility reasons, the contents of the created WritableList will
+	 * change with the contents of the given list. If this is not desired,
+	 * {@link #WritableList(Collection, Class)} should be used by casting the
+	 * first argument to {@link Collection}.
+	 * 
+	 * @param toWrap
+	 *            The java.util.List to wrap
+	 * @param elementType
+	 *            can be <code>null</code>
+	 * @since 1.5
+	 */
+	public WritableList(List<E> toWrap, Class<E> elementType) {
+		this(Realm.getDefault(), toWrap, elementType);
+	}
+
+	/**
 	 * Constructs a new instance in the default realm containing the elements of
 	 * the given collection. Changes to the given collection after calling this
 	 * method do not affect the contents of the created WritableList.
@@ -84,6 +104,7 @@
 	 *            the collection to copy
 	 * @param elementType
 	 *            can be <code>null</code>
+	 * @deprecated use the form that takes Class<E> as the elementType parameter
 	 * @since 1.2
 	 */
 	public WritableList(Collection<E> collection, Object elementType) {
@@ -91,6 +112,21 @@
 	}
 
 	/**
+	 * Constructs a new instance in the default realm containing the elements of
+	 * the given collection. Changes to the given collection after calling this
+	 * method do not affect the contents of the created WritableList.
+	 * 
+	 * @param collection
+	 *            the collection to copy
+	 * @param elementType
+	 *            can be <code>null</code>
+	 * @since 1.5
+	 */
+	public WritableList(Collection<E> collection, Class<E> elementType) {
+		this(Realm.getDefault(), new ArrayList<E>(collection), elementType);
+	}
+
+	/**
 	 * Creates a writable list containing elements of the given type, wrapping
 	 * an existing client-supplied list. Note that for backwards compatibility
 	 * reasons, the contents of the created WritableList will change with the
@@ -104,12 +140,33 @@
 	 *            The java.util.List to wrap
 	 * @param elementType
 	 *            can be <code>null</code>
+	 * @deprecated use the form that takes Class<E> as the elementType parameter
 	 */
 	public WritableList(Realm realm, List<E> toWrap, Object elementType) {
 		super(realm, toWrap, elementType);
 	}
 
 	/**
+	 * Creates a writable list containing elements of the given type, wrapping
+	 * an existing client-supplied list. Note that for backwards compatibility
+	 * reasons, the contents of the created WritableList will change with the
+	 * contents of the given list. If this is not desired,
+	 * {@link #WritableList(Realm, Collection, Class)} should be used by casting
+	 * the second argument to {@link Collection}.
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param toWrap
+	 *            The java.util.List to wrap
+	 * @param elementType
+	 *            can be <code>null</code>
+	 * @since 1.5
+	 */
+	public WritableList(Realm realm, List<E> toWrap, Class<E> elementType) {
+		super(realm, toWrap, elementType);
+	}
+
+	/**
 	 * Constructs a new instance in the default realm containing the elements of
 	 * the given collection. Changes to the given collection after calling this
 	 * method do not affect the contents of the created WritableList.
@@ -120,6 +177,7 @@
 	 *            the collection to copy
 	 * @param elementType
 	 *            can be <code>null</code>
+	 * @deprecated use the form that takes Class<E> as the elementType parameter
 	 * @since 1.2
 	 */
 	public WritableList(Realm realm, Collection<E> collection,
@@ -127,6 +185,24 @@
 		super(realm, new ArrayList<E>(collection), elementType);
 	}
 
+	/**
+	 * Constructs a new instance in the default realm containing the elements of
+	 * the given collection. Changes to the given collection after calling this
+	 * method do not affect the contents of the created WritableList.
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param collection
+	 *            the collection to copy
+	 * @param elementType
+	 *            can be <code>null</code>
+	 * @since 1.5
+	 */
+	public WritableList(Realm realm, Collection<E> collection,
+			Class<E> elementType) {
+		super(realm, new ArrayList<E>(collection), elementType);
+	}
+
 	public E set(int index, E element) {
 		checkRealm();
 		E oldElement = wrappedList.set(index, element);
@@ -267,6 +343,9 @@
 
 	public void clear() {
 		checkRealm();
+		if (wrappedList.isEmpty()) {
+			return;
+		}
 		// We remove the elements from back to front which is typically much
 		// faster on common list implementations like ArrayList.
 		List<ListDiffEntry<E>> entries = new ArrayList<ListDiffEntry<E>>(
@@ -281,13 +360,151 @@
 		fireListChange(Diffs.createListDiff(entries));
 	}
 
+	public Iterator<E> iterator() {
+		getterCalled();
+		final List<E> list = wrappedList;
+		final ListIterator<E> wrappedIterator = list.listIterator();
+		return new Iterator<E>() {
+			E last = null;
+
+			public boolean hasNext() {
+				getterCalled();
+				checkForComodification();
+				return wrappedIterator.hasNext();
+			}
+
+			public E next() {
+				getterCalled();
+				checkForComodification();
+				return last = wrappedIterator.next();
+			}
+
+			public void remove() {
+				checkRealm();
+				checkForComodification();
+				int index = wrappedIterator.previousIndex();
+				wrappedIterator.remove();
+				ListDiff<E> diff = Diffs.createListDiff(Diffs
+						.createListDiffEntry(index, false, last));
+				fireListChange(diff);
+			}
+
+			private void checkForComodification() {
+				if (list != wrappedList)
+					throw new ConcurrentModificationException();
+			}
+		};
+	}
+
+	public ListIterator<E> listIterator() {
+		return listIterator(0);
+	}
+
+	public ListIterator<E> listIterator(int index) {
+		getterCalled();
+		final List<E> list = wrappedList;
+		final ListIterator<E> wrappedIterator = list.listIterator(index);
+		return new ListIterator<E>() {
+			int lastIndex = -1;
+			E last = null;
+
+			public void add(E o) {
+				checkRealm();
+				checkForComodification();
+				wrappedIterator.add(o);
+				lastIndex = previousIndex();
+				ListDiff<E> diff = Diffs.createListDiff(Diffs
+						.createListDiffEntry(lastIndex, true, o));
+				fireListChange(diff);
+			}
+
+			public boolean hasNext() {
+				getterCalled();
+				checkForComodification();
+				return wrappedIterator.hasNext();
+			}
+
+			public boolean hasPrevious() {
+				getterCalled();
+				checkForComodification();
+				return wrappedIterator.hasPrevious();
+			}
+
+			public E next() {
+				getterCalled();
+				checkForComodification();
+				last = wrappedIterator.next();
+				lastIndex = previousIndex();
+				return last;
+			}
+
+			public int nextIndex() {
+				getterCalled();
+				checkForComodification();
+				return wrappedIterator.nextIndex();
+			}
+
+			public E previous() {
+				getterCalled();
+				checkForComodification();
+				last = wrappedIterator.previous();
+				lastIndex = nextIndex();
+				return last;
+			}
+
+			public int previousIndex() {
+				getterCalled();
+				checkForComodification();
+				return wrappedIterator.previousIndex();
+			}
+
+			public void remove() {
+				checkRealm();
+				checkForComodification();
+				wrappedIterator.remove();
+				ListDiff<E> diff = Diffs.createListDiff(Diffs
+						.createListDiffEntry(lastIndex, false, last));
+				lastIndex = -1;
+				fireListChange(diff);
+			}
+
+			public void set(E o) {
+				checkRealm();
+				checkForComodification();
+				wrappedIterator.set(o);
+				ListDiff<E> diff = Diffs.createListDiff(
+						Diffs.createListDiffEntry(lastIndex, false, last),
+						Diffs.createListDiffEntry(lastIndex, true, o));
+				last = o;
+				fireListChange(diff);
+			}
+
+			private void checkForComodification() {
+				if (list != wrappedList)
+					throw new ConcurrentModificationException();
+			}
+		};
+	}
+
 	/**
 	 * @param elementType
 	 *            can be <code>null</code>
 	 * @return new list with the default realm.
+	 * @deprecated use the form that takes Class<E> as the elementType parameter
 	 */
 	public static WritableList<Object> withElementType(Object elementType) {
 		return new WritableList<Object>(Realm.getDefault(),
 				new ArrayList<Object>(), elementType);
 	}
+
+	/**
+	 * @param elementType
+	 *            can be <code>null</code>
+	 * @return new list with the default realm.
+	 * @since 1.5
+	 */
+	public static <E> WritableList<E> withElementType(Class<E> elementType) {
+		return new WritableList<E>(Realm.getDefault(), new ArrayList<E>(),
+				elementType);
+	}
 }
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/WritableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/WritableSet.java
index fe9f371..53a65d7 100644
--- a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/WritableSet.java
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/WritableSet.java
@@ -8,13 +8,15 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Brad Reynolds - bug 147515
- *     Matthew Hall - bug 221351
+ *     Matthew Hall - bug 221351, 208434
+ *     Nigel Westbury - bug 335792
  *******************************************************************************/
 
 package org.eclipse.core.databinding.observable.set;
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.ConcurrentModificationException;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
@@ -171,6 +173,40 @@
 		fireSetChange(Diffs.createSetDiff(additions, removes));
 	}
 
+	public Iterator<E> iterator() {
+		getterCalled();
+		final Set<E> set = wrappedSet;
+		final Iterator<E> wrappedIterator = set.iterator();
+		return new Iterator<E>() {
+			E last;
+
+			public void remove() {
+				getterCalled();
+				checkForComodification();
+				wrappedIterator.remove();
+				fireSetChange(Diffs.createSetDiff(Collections.<E> emptySet(),
+						Collections.singleton(last)));
+			}
+
+			public boolean hasNext() {
+				getterCalled();
+				checkForComodification();
+				return wrappedIterator.hasNext();
+			}
+
+			public E next() {
+				getterCalled();
+				checkForComodification();
+				return last = wrappedIterator.next();
+			}
+
+			private void checkForComodification() {
+				if (set != wrappedSet)
+					throw new ConcurrentModificationException();
+			}
+		};
+	}
+
 	/**
 	 * @param elementType
 	 *            can be <code>null</code>
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/WritableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/WritableListTest.java
index 9b4004c..2fec7c5 100755
--- a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/WritableListTest.java
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/WritableListTest.java
@@ -9,6 +9,7 @@
  *     IBM Corporation - initial API and implementation
  *     Brad Reynolds - bug 164653, 147515
  *     Matthew Hall - bug 213145
+ *     Nigel Westbury - bug 208434
  *******************************************************************************/
 
 package org.eclipse.core.tests.databinding.observable.list;
@@ -17,6 +18,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 
 import junit.framework.Test;
@@ -203,6 +205,23 @@
 		assertEquals(2, wlist.size());
 	}
 
+	public void testIteratorRemoval() {
+		RealmTester.setDefault(new CurrentRealm(true));
+		List<String> list = new ArrayList<String>(Arrays.asList(new String[] {
+				"a", "b", "c" }));
+		WritableList<String> wlist = new WritableList<String>(list,
+				String.class);
+		Iterator<String> iter = wlist.iterator();
+		String s1 = iter.next();
+		String s2 = iter.next();
+		iter.remove();
+		String s3 = iter.next();
+		assertEquals(s1, "a");
+		assertEquals(s2, "b");
+		assertEquals(s3, "c");
+		assertEquals(2, wlist.size());
+	}
+
 	public static Test suite() {
 		TestSuite suite = new TestSuite(WritableListTest.class.getName());
 		suite.addTestSuite(WritableListTest.class);