/*
* JOSMng - a Java Open Street Map editor, the next generation.
*
* Copyright (C) 2008 Petr Nejedly
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.openstreetmap.josm.tools;
import java.util.AbstractList;
import java.util.RandomAccess;
/**
* A List implementation initially based on given array, but never modifying
* the array directly. On the first modification, the implementation will
* create its own copy of the array, and after that it behaves mostly as
* an ArrayList.
*
* @author nenik
*/
public class CopyList extends AbstractList implements RandomAccess, Cloneable {
private E[] array;
private int size;
private boolean pristine;
/**
* Create a List over given array.
* @param array The initial List content. The array is never modified
* by the {@code CopyList}.
*/
public CopyList(E[] array) {
this(array, array.length);
}
private CopyList(E[] array, int size) {
this.array = array;
this.size = size;
pristine = true;
}
// read-only access:
public @Override E get(int index) {
rangeCheck(index);
return array[index];
}
public @Override int size() {
return size;
}
// modification:
public @Override E set(int index, E element) {
rangeCheck(index);
changeCheck();
E old = array[index];
array[index] = element;
return old;
}
// full resizable semantics:
public @Override void add(int index, E element) {
// range check
ensureCapacity(size+1);
changeCheck();
System.arraycopy(array, index, array, index+1, size-index);
array[index] = element;
size++;
}
public @Override E remove(int index) {
rangeCheck(index);
changeCheck();
modCount++;
E element = array[index];
if (index < size-1) {
System.arraycopy(array, index+1, array, index, size-index-1);
} else {
array[index] = null;
}
size--;
return element;
}
// speed optimizations:
public @Override boolean add(E element) {
ensureCapacity(size+1);
changeCheck();
array[size++] = element;
return true;
}
public @Override void clear() {
modCount++;
// clean up the array
while (size > 0) array[--size] = null;
}
// helpers:
/**
* Returns another independent copy-on-write copy of this List
* instance. Neither the elements nor the backing storage are copied.
*
* @return a clone of this CopyList instance
*/
public @Override Object clone() {
return new CopyList(array, size);
}
private void rangeCheck(int index) {
if (index >= size || index < 0) throw new IndexOutOfBoundsException();
}
private void changeCheck() {
if (pristine) {
array = array.clone();
pristine = false;
}
}
@SuppressWarnings("unchecked")
private void ensureCapacity(int target) {
modCount++;
if (target > array.length) {
E[] old = array;
int newCapacity = Math.max(target, (array.length * 3)/2 + 1);
array = (E[]) new Object[newCapacity];
System.arraycopy(old, 0, array, 0, size);
pristine = false;
}
}
}