diff --git a/jme3-core/src/main/java/com/jme3/util/SafeArrayList.java b/jme3-core/src/main/java/com/jme3/util/SafeArrayList.java index 27f129f2f..a0657c753 100644 --- a/jme3-core/src/main/java/com/jme3/util/SafeArrayList.java +++ b/jme3-core/src/main/java/com/jme3/util/SafeArrayList.java @@ -43,7 +43,7 @@ import java.util.*; * the list is changing.

* *

All modifications, including set() operations will cause a copy of the - * data to be created that replaces the old version. Because this list is + * data to be created that replaces the old version. Because this list is * not designed for threading concurrency it further optimizes the "many modifications" * case by buffering them as a normal ArrayList until the next time the contents * are accessed.

@@ -63,16 +63,16 @@ import java.util.*; * Even after ListIterator.remove() or Iterator.remove() is called, this change * is not reflected in the iterator instance as it is still refering to its * original snapshot. - * + * * * @version $Revision$ * @author Paul Speed */ -public class SafeArrayList implements List { - +public class SafeArrayList implements List, Cloneable { + // Implementing List directly to avoid accidentally acquiring // incorrect or non-optimal behavior from AbstractList. For - // example, the default iterator() method will not work for + // example, the default iterator() method will not work for // this list. // Note: given the particular use-cases this was intended, @@ -81,30 +81,48 @@ public class SafeArrayList implements List { // SafeArrayList-specific methods could then be exposed // for the classes like Node and Spatial to use to manage // the list. This was the callers couldn't remove a child - // without it being detached properly, for example. + // without it being detached properly, for example. - private Class elementType; + private Class elementType; private List buffer; private E[] backingArray; private int size = 0; - + public SafeArrayList(Class elementType) { - this.elementType = elementType; + this.elementType = elementType; } - + public SafeArrayList(Class elementType, Collection c) { - this.elementType = elementType; + this.elementType = elementType; addAll(c); } + public SafeArrayList clone() { + try { + SafeArrayList clone = (SafeArrayList)super.clone(); + + // Clone whichever backing store is currently active + if( backingArray != null ) { + clone.backingArray = backingArray.clone(); + } + if( buffer != null ) { + clone.buffer = (List)((ArrayList)buffer).clone(); + } + + return clone; + } catch( CloneNotSupportedException e ) { + throw new AssertionError(); + } + } + protected final T[] createArray(Class type, int size) { - return (T[])java.lang.reflect.Array.newInstance(type, size); + return (T[])java.lang.reflect.Array.newInstance(type, size); } - + protected final E[] createArray(int size) { - return createArray(elementType, size); + return createArray(elementType, size); } - + /** * Returns a current snapshot of this List's backing array that * is guaranteed not to change through further List manipulation. @@ -114,10 +132,10 @@ public class SafeArrayList implements List { public final E[] getArray() { if( backingArray != null ) return backingArray; - + if( buffer == null ) { backingArray = createArray(0); - } else { + } else { // Only keep the array or the buffer but never both at // the same time. 1) it saves space, 2) it keeps the rest // of the code safer. @@ -126,35 +144,35 @@ public class SafeArrayList implements List { } return backingArray; } - + protected final List getBuffer() { if( buffer != null ) return buffer; - + if( backingArray == null ) { buffer = new ArrayList(); - } else { + } else { // Only keep the array or the buffer but never both at // the same time. 1) it saves space, 2) it keeps the rest - // of the code safer. + // of the code safer. buffer = new ArrayList( Arrays.asList(backingArray) ); backingArray = null; } return buffer; } - + public final int size() { - return size; + return size; } - + public final boolean isEmpty() { return size == 0; } - + public boolean contains(Object o) { return indexOf(o) >= 0; } - + public Iterator iterator() { return listIterator(); } @@ -162,70 +180,70 @@ public class SafeArrayList implements List { public Object[] toArray() { return getArray(); } - + public T[] toArray(T[] a) { - + E[] array = getArray(); if (a.length < array.length) { return (T[])Arrays.copyOf(array, array.length, a.getClass()); - } - + } + System.arraycopy( array, 0, a, 0, array.length ); - + if (a.length > array.length) { a[array.length] = null; } - + return a; } - + public boolean add(E e) { boolean result = getBuffer().add(e); size = getBuffer().size(); return result; } - + public boolean remove(Object o) { boolean result = getBuffer().remove(o); size = getBuffer().size(); return result; } - + public boolean containsAll(Collection c) { return Arrays.asList(getArray()).containsAll(c); } - + public boolean addAll(Collection c) { boolean result = getBuffer().addAll(c); size = getBuffer().size(); return result; } - + public boolean addAll(int index, Collection c) { boolean result = getBuffer().addAll(index, c); size = getBuffer().size(); return result; } - + public boolean removeAll(Collection c) { boolean result = getBuffer().removeAll(c); size = getBuffer().size(); return result; } - + public boolean retainAll(Collection c) { boolean result = getBuffer().retainAll(c); size = getBuffer().size(); return result; } - + public void clear() { getBuffer().clear(); size = 0; } - + public boolean equals(Object o) { - if( o == this ) + if( o == this ) return true; if( !(o instanceof List) ) //covers null too return false; @@ -240,9 +258,9 @@ public class SafeArrayList implements List { if( o1 == null || !o1.equals(o2) ) return false; } - return !(i1.hasNext() || !i2.hasNext()); + return !(i1.hasNext() || !i2.hasNext()); } - + public int hashCode() { // Exactly the hash code described in the List interface, basically E[] array = getArray(); @@ -252,30 +270,30 @@ public class SafeArrayList implements List { } return result; } - + public final E get(int index) { if( backingArray != null ) return backingArray[index]; if( buffer != null ) return buffer.get(index); - throw new IndexOutOfBoundsException( "Index:" + index + ", Size:0" ); + throw new IndexOutOfBoundsException( "Index:" + index + ", Size:0" ); } - + public E set(int index, E element) { return getBuffer().set(index, element); } - + public void add(int index, E element) { getBuffer().add(index, element); size = getBuffer().size(); } - + public E remove(int index) { E result = getBuffer().remove(index); size = getBuffer().size(); return result; } - + public int indexOf(Object o) { E[] array = getArray(); for( int i = 0; i < array.length; i++ ) { @@ -289,7 +307,7 @@ public class SafeArrayList implements List { } return -1; } - + public int lastIndexOf(Object o) { E[] array = getArray(); for( int i = array.length - 1; i >= 0; i-- ) { @@ -303,29 +321,29 @@ public class SafeArrayList implements List { } return -1; } - + public ListIterator listIterator() { return new ArrayIterator(getArray(), 0); } - + public ListIterator listIterator(int index) { return new ArrayIterator(getArray(), index); } - + public List subList(int fromIndex, int toIndex) { - + // So far JME doesn't use subList that I can see so I'm nerfing it. List raw = Arrays.asList(getArray()).subList(fromIndex, toIndex); return Collections.unmodifiableList(raw); } - + public String toString() { - + E[] array = getArray(); if( array.length == 0 ) { return "[]"; } - + StringBuilder sb = new StringBuilder(); sb.append('['); for( int i = 0; i < array.length; i++ ) { @@ -337,63 +355,63 @@ public class SafeArrayList implements List { sb.append(']'); return sb.toString(); } - + protected class ArrayIterator implements ListIterator { private E[] array; private int next; private int lastReturned; - + protected ArrayIterator( E[] array, int index ) { this.array = array; this.next = index; this.lastReturned = -1; } - + public boolean hasNext() { return next != array.length; } - + public E next() { if( !hasNext() ) throw new NoSuchElementException(); lastReturned = next++; return array[lastReturned]; } - + public boolean hasPrevious() { - return next != 0; - } - + return next != 0; + } + public E previous() { if( !hasPrevious() ) throw new NoSuchElementException(); lastReturned = --next; return array[lastReturned]; } - + public int nextIndex() { - return next; + return next; } - + public int previousIndex() { return next - 1; } - + public void remove() { // This operation is not so easy to do but we will fake it. // The issue is that the backing list could be completely // different than the one this iterator is a snapshot of. - // We'll just remove(element) which in most cases will be + // We'll just remove(element) which in most cases will be // correct. If the list had earlier .equals() equivalent // elements then we'll remove one of those instead. Either // way, none of those changes are reflected in this iterator. SafeArrayList.this.remove( array[lastReturned] ); } - + public void set(E e) { throw new UnsupportedOperationException(); } - + public void add(E e) { throw new UnsupportedOperationException(); }