View Javadoc

1   /*
2    *   Magic-Project is a turn based strategy simulator
3    *   Copyright (C) 2003-2007 Fabrice Daugan
4    *
5    *   This program is free software; you can redistribute it and/or modify it 
6    * under the terms of the GNU General Public License as published by the Free 
7    * Software Foundation; either version 2 of the License, or (at your option) any
8    * later version.
9    *
10   *   This program is distributed in the hope that it will be useful, but WITHOUT 
11   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
13   * details.
14   *
15   *   You should have received a copy of the GNU General Public License along  
16   * with this program; if not, write to the Free Software Foundation, Inc., 
17   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  package net.sf.magicproject.tools;
20  
21  import java.util.AbstractList;
22  import java.util.Collection;
23  import java.util.ConcurrentModificationException;
24  import java.util.List;
25  import java.util.RandomAccess;
26  
27  /***
28   * Resizable-array implementation of the <tt>List</tt> interface. Implements
29   * all optional list operations, and permits all elements, including
30   * <tt>null</tt>. In addition to implementing the <tt>List</tt> interface,
31   * this class provides methods to manipulate the size of the array that is used
32   * internally to store the list. (This class is roughly equivalent to
33   * <tt>Vector</tt>, except that it is unsynchronized.)
34   * <p>
35   * The <tt>size</tt>, <tt>isEmpty</tt>, <tt>get</tt>, <tt>set</tt>,
36   * <tt>iterator</tt>, and <tt>listIterator</tt> operations run in constant
37   * time. The <tt>add</tt> operation runs in <i>amortized constant time</i>,
38   * that is, adding n elements requires O(n) time. All of the other operations
39   * run in linear time (roughly speaking). The constant factor is low compared to
40   * that for the <tt>LinkedList</tt> implementation.
41   * <p>
42   * Each <tt>ArrayList</tt> instance has a <i>capacity</i>. The capacity is
43   * the size of the array used to store the elements in the list. It is always at
44   * least as large as the list size. As elements are added to an ArrayList, its
45   * capacity grows automatically. The details of the growth policy are not
46   * specified beyond the fact that adding an element has constant amortized time
47   * cost.
48   * <p>
49   * An application can increase the capacity of an <tt>ArrayList</tt> instance
50   * before adding a large number of elements using the <tt>ensureCapacity</tt>
51   * operation. This may reduce the amount of incremental reallocation.
52   * <p>
53   * <strong>Note that this implementation is not synchronized.</strong> If
54   * multiple threads access an <tt>ArrayList</tt> instance concurrently, and at
55   * least one of the threads modifies the list structurally, it <i>must</i> be
56   * synchronized externally. (A structural modification is any operation that
57   * adds or deletes one or more elements, or explicitly resizes the backing
58   * array; merely setting the value of an element is not a structural
59   * modification.) This is typically accomplished by synchronizing on some object
60   * that naturally encapsulates the list. If no such object exists, the list
61   * should be "wrapped" using the
62   * {@link java.util.Collections#synchronizedList(List) Collections.synchronizedList}
63   * method. This is best done at creation time, to prevent accidental
64   * unsynchronized access to the list:
65   * 
66   * <pre>
67   *    List list = Collections.synchronizedList(new ArrayList(...));
68   * </pre>
69   * 
70   * <p>
71   * The iterators returned by this class's <tt>iterator</tt> and
72   * <tt>listIterator</tt> methods are <i>fail-fast</i>: if the list is
73   * structurally modified at any time after the iterator is created, in any way
74   * except through the iterator's own <tt>remove</tt> or <tt>add</tt>
75   * methods, the iterator will throw a {@link ConcurrentModificationException}.
76   * Thus, in the face of concurrent modification, the iterator fails quickly and
77   * cleanly, rather than risking arbitrary, non-deterministic behavior at an
78   * undetermined time in the future.
79   * <p>
80   * Note that the fail-fast behavior of an iterator cannot be guaranteed as it
81   * is, generally speaking, impossible to make any hard guarantees in the
82   * presence of unsynchronized concurrent modification. Fail-fast iterators throw
83   * <tt>ConcurrentModificationException</tt> on a best-effort basis. Therefore,
84   * it would be wrong to write a program that depended on this exception for its
85   * correctness: <i>the fail-fast behavior of iterators should be used only to
86   * detect bugs.</i>
87   * <p>
88   * This class is a member of the <a href="{@docRoot}/../guide/collections/index.html">
89   * Java Collections Framework</a>.
90   * 
91   * @see Collection
92   * @see List
93   * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
94   * @param <E>
95   * @since 0.91
96   */
97  public class RevertedArrayList<E> extends AbstractList<E> implements List<E>,
98  		RandomAccess, Cloneable, java.io.Serializable {
99  
100 	/***
101 	 * The array buffer into which the elements of the ArrayList are stored. The
102 	 * capacity of the ArrayList is the length of this array buffer.
103 	 */
104 	private transient E[] elementData;
105 
106 	/***
107 	 * The size of the ArrayList (the number of elements it contains).
108 	 * 
109 	 * @serial
110 	 */
111 	private int size;
112 
113 	/***
114 	 * Constructs an empty list with the specified initial capacity.
115 	 * 
116 	 * @param initialCapacity
117 	 *          the initial capacity of the list
118 	 */
119 	@SuppressWarnings("unchecked")
120 	public RevertedArrayList(int initialCapacity) {
121 		super();
122 		if (initialCapacity < 0)
123 			throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
124 		this.elementData = (E[]) new Object[initialCapacity];
125 	}
126 
127 	/***
128 	 * Constructs an empty list with an initial capacity of ten.
129 	 */
130 	public RevertedArrayList() {
131 		this(10);
132 	}
133 
134 	/***
135 	 * Constructs a list containing the elements of the specified collection, in
136 	 * the order they are returned by the collection's iterator. The
137 	 * <tt>ArrayList</tt> instance has an initial capacity of 110% the size of
138 	 * the specified collection.
139 	 * 
140 	 * @param c
141 	 *          the collection whose elements are to be placed into this list
142 	 */
143 	@SuppressWarnings("unchecked")
144 	public RevertedArrayList(Collection<? extends E> c) {
145 		size = c.size();
146 		// Allow 10% room for growth
147 		int capacity = (int) Math.min((size * 110L) / 100, Integer.MAX_VALUE);
148 		elementData = (E[]) c.toArray(new Object[capacity]);
149 	}
150 
151 	/***
152 	 * Increases the capacity of this <tt>ArrayList</tt> instance, if necessary,
153 	 * to ensure that it can hold at least the number of elements specified by the
154 	 * minimum capacity argument.
155 	 * 
156 	 * @param minCapacity
157 	 *          the desired minimum capacity
158 	 */
159 	@SuppressWarnings("unchecked")
160 	public void ensureCapacity(int minCapacity) {
161 		modCount++;
162 		int oldCapacity = elementData.length;
163 		if (minCapacity > oldCapacity) {
164 			Object[] oldData = elementData;
165 			int newCapacity = (oldCapacity * 3) / 2 + 1;
166 			if (newCapacity < minCapacity)
167 				newCapacity = minCapacity;
168 			elementData = (E[]) new Object[newCapacity];
169 			System.arraycopy(oldData, 0, elementData, 0, size);
170 		}
171 	}
172 
173 	@Override
174 	public int size() {
175 		return size;
176 	}
177 
178 	@Override
179 	public boolean isEmpty() {
180 		return size == 0;
181 	}
182 
183 	@Override
184 	public boolean contains(Object o) {
185 		return indexOf(o) >= 0;
186 	}
187 
188 	@Override
189 	public int indexOf(Object elem) {
190 		if (elem == null) {
191 			for (int i = 0; i < size; i++)
192 				if (elementData[i] == null)
193 					return i;
194 		} else {
195 			for (int i = 0; i < size; i++)
196 				if (elem.equals(elementData[i]))
197 					return i;
198 		}
199 		return -1;
200 	}
201 
202 	@Override
203 	public int lastIndexOf(Object elem) {
204 		if (elem == null) {
205 			for (int i = size - 1; i >= 0; i--)
206 				if (elementData[i] == null)
207 					return i;
208 		} else {
209 			for (int i = size - 1; i >= 0; i--)
210 				if (elem.equals(elementData[i]))
211 					return i;
212 		}
213 		return -1;
214 	}
215 
216 	@SuppressWarnings("unchecked")
217 	@Override
218 	public RevertedArrayList clone() {
219 		try {
220 			RevertedArrayList<E> v = (RevertedArrayList<E>) super.clone();
221 			v.elementData = (E[]) new Object[size];
222 			System.arraycopy(elementData, 0, v.elementData, 0, size);
223 			v.modCount = 0;
224 			return v;
225 		} catch (CloneNotSupportedException e) {
226 			// this shouldn't happen, since we are Cloneable
227 			throw new InternalError();
228 		}
229 	}
230 
231 	@Override
232 	public Object[] toArray() {
233 		Object[] result = new Object[size];
234 		System.arraycopy(elementData, 0, result, 0, size);
235 		return result;
236 	}
237 
238 	@SuppressWarnings("unchecked")
239 	@Override
240 	public <T> T[] toArray(T[] a) {
241 		T[] aTmp = a;
242 		if (a.length < size)
243 			aTmp = (T[]) java.lang.reflect.Array.newInstance(a.getClass()
244 					.getComponentType(), size);
245 		System.arraycopy(elementData, 0, aTmp, 0, size);
246 		if (aTmp.length > size)
247 			aTmp[size] = null;
248 		return aTmp;
249 	}
250 
251 	// Positional Access Operations
252 
253 	@Override
254 	public E get(int index) {
255 		rangeCheck(index);
256 
257 		return elementData[size - index - 1];
258 	}
259 
260 	@Override
261 	public E set(int index, E element) {
262 		rangeCheck(index);
263 
264 		E oldValue = elementData[index];
265 		elementData[index] = element;
266 		return oldValue;
267 	}
268 
269 	@Override
270 	public boolean add(E e) {
271 		ensureCapacity(size + 1); // Increments modCount!!
272 		elementData[size++] = e;
273 		return true;
274 	}
275 
276 	@Override
277 	public void add(int index, E element) {
278 		if (index > size || index < 0)
279 			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
280 
281 		ensureCapacity(size + 1); // Increments modCount!!
282 		System.arraycopy(elementData, index, elementData, index + 1, size - index);
283 		elementData[index] = element;
284 		size++;
285 	}
286 
287 	@Override
288 	public E remove(int index) {
289 		rangeCheck(index);
290 
291 		modCount++;
292 		E oldValue = elementData[index];
293 
294 		int numMoved = size - index - 1;
295 		if (numMoved > 0)
296 			System.arraycopy(elementData, index + 1, elementData, index, numMoved);
297 		elementData[--size] = null; // Let gc do its work
298 
299 		return oldValue;
300 	}
301 
302 	@Override
303 	public boolean remove(Object o) {
304 		if (o == null) {
305 			for (int index = 0; index < size; index++)
306 				if (elementData[index] == null) {
307 					fastRemove(index);
308 					return true;
309 				}
310 		} else {
311 			for (int index = 0; index < size; index++)
312 				if (o.equals(elementData[index])) {
313 					fastRemove(index);
314 					return true;
315 				}
316 		}
317 		return false;
318 	}
319 
320 	/*
321 	 * Private remove method that skips bounds checking and does not return the
322 	 * value removed.
323 	 */
324 	private void fastRemove(int index) {
325 		modCount++;
326 		int numMoved = size - index - 1;
327 		if (numMoved > 0)
328 			System.arraycopy(elementData, index + 1, elementData, index, numMoved);
329 		elementData[--size] = null; // Let gc do its work
330 	}
331 
332 	@Override
333 	public void clear() {
334 		modCount++;
335 
336 		// Let gc do its work
337 		for (int i = 0; i < size; i++)
338 			elementData[i] = null;
339 
340 		size = 0;
341 	}
342 
343 	@Override
344 	public boolean addAll(Collection<? extends E> c) {
345 		Object[] a = c.toArray();
346 		int numNew = a.length;
347 		ensureCapacity(size + numNew); // Increments modCount
348 		System.arraycopy(a, 0, elementData, size, numNew);
349 		size += numNew;
350 		return numNew != 0;
351 	}
352 
353 	@Override
354 	public boolean addAll(int index, Collection<? extends E> c) {
355 		if (index > size || index < 0)
356 			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
357 
358 		Object[] a = c.toArray();
359 		int numNew = a.length;
360 		ensureCapacity(size + numNew); // Increments modCount
361 
362 		int numMoved = size - index;
363 		if (numMoved > 0)
364 			System.arraycopy(elementData, index, elementData, index + numNew,
365 					numMoved);
366 
367 		System.arraycopy(a, 0, elementData, index, numNew);
368 		size += numNew;
369 		return numNew != 0;
370 	}
371 
372 	@Override
373 	protected void removeRange(int fromIndex, int toIndex) {
374 		modCount++;
375 		int numMoved = size - toIndex;
376 		System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);
377 
378 		// Let gc do its work
379 		int newSize = size - (toIndex - fromIndex);
380 		while (size != newSize)
381 			elementData[--size] = null;
382 	}
383 
384 	/***
385 	 * Checks if the given index is in range. If not, throws an appropriate
386 	 * runtime exception. This method does *not* check if the index is negative:
387 	 * It is always used immediately prior to an array access, which throws an
388 	 * ArrayIndexOutOfBoundsException if index is negative.
389 	 */
390 	private void rangeCheck(int index) {
391 		if (index >= size)
392 			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
393 	}
394 
395 	/***
396 	 * Save the state of the <tt>ArrayList</tt> instance to a stream (that is,
397 	 * serialize it).
398 	 * 
399 	 * @serialData The length of the array backing the <tt>ArrayList</tt>
400 	 *             instance is emitted (int), followed by all of its elements
401 	 *             (each an <tt>Object</tt>) in the proper order.
402 	 */
403 	private void writeObject(java.io.ObjectOutputStream s)
404 			throws java.io.IOException {
405 		// Write out element count, and any hidden stuff
406 		int expectedModCount = modCount;
407 		s.defaultWriteObject();
408 
409 		// Write out array length
410 		s.writeInt(elementData.length);
411 
412 		// Write out all elements in the proper order.
413 		for (int i = 0; i < size; i++)
414 			s.writeObject(elementData[i]);
415 
416 		if (modCount != expectedModCount) {
417 			throw new ConcurrentModificationException();
418 		}
419 
420 	}
421 
422 	/***
423 	 * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
424 	 * deserialize it).
425 	 */
426 	@SuppressWarnings("unchecked")
427 	private void readObject(java.io.ObjectInputStream s)
428 			throws java.io.IOException, ClassNotFoundException {
429 		// Read in size, and any hidden stuff
430 		s.defaultReadObject();
431 
432 		// Read in array length and allocate array
433 		int arrayLength = s.readInt();
434 		elementData = (E[]) new Object[arrayLength];
435 		Object[] a = elementData;
436 
437 		// Read in all elements in the proper order.
438 		for (int i = 0; i < size; i++)
439 			a[i] = s.readObject();
440 	}
441 }