Skip to main content
summaryrefslogtreecommitdiffstats
blob: 5493e0d5f1ee746d54baffb370865a6b4f9fc194 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/*******************************************************************************
 * Copyright (c) 2005, 2010 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.CollectionTools;
import org.eclipse.jpt.utility.internal.StringTools;

/**
 * A <code>CloneListIterator</code> iterates over a copy of a list,
 * allowing for concurrent access to the original list.
 * <p>
 * The original list passed to the <code>CloneListIterator</code>'s
 * constructor should be synchronized; otherwise you run the risk of
 * a corrupted list (e.g. {@link java.util.Vector}.
 * <p>
 * By default, a <code>CloneListIterator</code> does not support the
 * modification operations; this is because it does not have
 * access to the original list. But if the <code>CloneListIterator</code>
 * is supplied with a {@link Mutator} it will delegate the
 * modification operations to the {@link Mutator}.
 * Alternatively, a subclass can override the modification methods.
 * 
 * @param <E> the type of elements returned by the iterator
 * 
 * @see org.eclipse.jpt.utility.internal.iterables.LiveCloneListIterable
 * @see org.eclipse.jpt.utility.internal.iterables.SnapshotCloneListIterable
 */
public class CloneListIterator<E>
	implements ListIterator<E>
{
	private final ListIterator<Object> listIterator;
	private int cursor;
	private State state;
	private final Mutator<E> mutator;

	private enum State {
		UNKNOWN,
		PREVIOUS,
		NEXT
	}


	// ********** constructors **********

	/**
	 * Construct a list iterator on a copy of the specified list.
	 * The modification methods will not be supported,
	 * unless a subclass overrides them.
	 */
	public CloneListIterator(List<? extends E> list) {
		this(list, Mutator.ReadOnly.<E>instance());
	}

	/**
	 * Construct a list iterator on a copy of the specified array.
	 * The modification methods will not be supported,
	 * unless a subclass overrides them.
	 */
	public CloneListIterator(E[] array) {
		this(array, Mutator.ReadOnly.<E>instance());
	}

	/**
	 * Construct a list iterator on a copy of the specified list.
	 * Use the specified list mutator to modify the original list.
	 */
	public CloneListIterator(List<? extends E> list, Mutator<E> mutator) {
		this(mutator, list.toArray());
	}

	/**
	 * Construct a list iterator on a copy of the specified array.
	 * Use the specified list mutator to modify the original list.
	 */
	public CloneListIterator(E[] array, Mutator<E> mutator) {
		this(mutator, array.clone());
	}

	/**
	 * Internal constructor used by subclasses.
	 * Swap order of arguments to prevent collision with other constructor.
	 * The passed in array will *not* be cloned.
	 */
	protected CloneListIterator(Mutator<E> mutator, Object... array) {
		super();
		// build a copy of the list and keep it in synch with original (if the mutator allows changes)
		// that way the nested list iterator will maintain some of our state
		this.listIterator = CollectionTools.list(array).listIterator();
		this.mutator = mutator;
		this.cursor = 0;
		this.state = State.UNKNOWN;
	}


	// ********** ListIterator implementation **********

	public boolean hasNext() {
		return this.listIterator.hasNext();
	}

	public E next() {
		// allow the nested iterator to throw an exception before we modify the index
		E next = this.nestedNext();
		this.cursor++;
		this.state = State.NEXT;
		return next;
	}

	public void remove() {
		// allow the nested iterator to throw an exception before we modify the original list
		this.listIterator.remove();
		if (this.state == State.PREVIOUS) {
			this.remove(this.cursor);
		} else {
			this.cursor--;
			this.remove(this.cursor);
		}
	}

	public int nextIndex() {
		return this.listIterator.nextIndex();
	}

	public int previousIndex() {
		return this.listIterator.previousIndex();
	}

	public boolean hasPrevious() {
		return this.listIterator.hasPrevious();
	}

	public E previous() {
		// allow the nested iterator to throw an exception before we modify the index
		E previous = this.nestedPrevious();
		this.cursor--;
		this.state = State.PREVIOUS;
		return previous;
	}

	public void add(E o) {
		// allow the nested iterator to throw an exception before we modify the original list
		this.listIterator.add(o);
		this.add(this.cursor, o);
		this.cursor++;
	}

	public void set(E o) {
		// allow the nested iterator to throw an exception before we modify the original list
		this.listIterator.set(o);
		if (this.state == State.PREVIOUS) {
			this.set(this.cursor, o);
		} else {
			this.set(this.cursor - 1, o);
		}
	}


	// ********** internal methods **********

	/**
	 * The list passed in during construction held elements of type <code>E</code>,
	 * so this cast is not a problem. We need this cast because
	 * all the elements of the original collection were copied into
	 * an object array (<code>Object[]</code>).
	 */
	@SuppressWarnings("unchecked")
	protected E nestedNext() {
		return (E) this.listIterator.next();
	}

	/**
	 * The list passed in during construction held elements of type <code>E</code>,
	 * so this cast is not a problem. We need this cast because
	 * all the elements of the original collection were copied into
	 * an object array (<code>Object[]</code>).
	 */
	@SuppressWarnings("unchecked")
	protected E nestedPrevious() {
		return (E) this.listIterator.previous();
	}

	/**
	 * Add the specified element to the original list.
	 * <p>
	 * This method can be overridden by a subclass as an
	 * alternative to building a {@link Mutator}.
	 */
	protected void add(int index, E o) {
		this.mutator.add(index, o);
	}

	/**
	 * Remove the specified element from the original list.
	 * <p>
	 * This method can be overridden by a subclass as an
	 * alternative to building a {@link Mutator}.
	 */
	protected void remove(int index) {
		this.mutator.remove(index);
	}

	/**
	 * Set the specified element in the original list.
	 * <p>
	 * This method can be overridden by a subclass as an
	 * alternative to building a {@link Mutator}.
	 */
	protected void set(int index, E o) {
		this.mutator.set(index, o);
	}


	// ********** overrides **********

	@Override
	public String toString() {
		return StringTools.buildToStringFor(this);
	}


	//********** member interface **********

	/**
	 * Used by {@link CloneListIterator} to remove
	 * elements from the original list; since the list iterator
	 * does not have direct access to the original list.
	 */
	public interface Mutator<T> {

		/**
		 * Add the specified object to the original list.
		 */
		void add(int index, T o);

		/**
		 * Remove the specified object from the original list.
		 */
		void remove(int index);

		/**
		 * Set the specified object in the original list.
		 */
		void set(int index, T o);


		final class ReadOnly<S> implements Mutator<S> {
			@SuppressWarnings("rawtypes")
			public static final Mutator INSTANCE = new ReadOnly();
			@SuppressWarnings("unchecked")
			public static <R> Mutator<R> instance() {
				return INSTANCE;
			}
			// ensure single instance
			private ReadOnly() {
				super();
			}
			// add is not supported
			public void add(int index, Object o) {
				throw new UnsupportedOperationException();
			}
			// remove is not supported
			public void remove(int index) {
				throw new UnsupportedOperationException();
			}
			// set is not supported
			public void set(int index, Object o) {
				throw new UnsupportedOperationException();
			}
			@Override
			public String toString() {
				return "CloneListIterator.Mutator.ReadOnly"; //$NON-NLS-1$
			}
			private static final long serialVersionUID = 1L;
			private Object readResolve() {
				// replace this object with the singleton
				return INSTANCE;
			}
		}

	}

}

Back to the top