/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.util;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import org.h2.mvstore.DataUtils;
/**
* An immutable array.
*
* @param <K> the type
*/
public final class ImmutableArray2<K> implements Iterable<K> {
private static final ImmutableArray2<?> EMPTY = new ImmutableArray2<Object>(
new Object[0], 0);
/**
* The array.
*/
private final K[] array;
private final int length;
private AtomicBoolean canExtend;
private ImmutableArray2(K[] array, int len) {
this.array = array;
this.length = len;
}
private ImmutableArray2(K[] array, int len, boolean canExtend) {
this.array = array;
this.length = len;
if (canExtend) {
this.canExtend = new AtomicBoolean(true);
}
}
/**
* Get the entry at this index.
*
* @param index the index
* @return the entry
*/
public K get(int index) {
if (index >= length) {
throw new IndexOutOfBoundsException();
}
return array[index];
}
/**
* Get the length.
*
* @return the length
*/
public int length() {
return length;
}
/**
* Set the entry at this index.
*
* @param index the index
* @param obj the object
* @return the new immutable array
*/
public ImmutableArray2<K> set(int index, K obj) {
K[] a2 = Arrays.copyOf(array, length);
a2[index] = obj;
return new ImmutableArray2<K>(a2, length);
}
/**
* Insert an entry at this index.
*
* @param index the index
* @param obj the object
* @return the new immutable array
*/
public ImmutableArray2<K> insert(int index, K obj) {
int len = length + 1;
int newLen = len;
boolean extendable;
if (index == len - 1) {
AtomicBoolean x = canExtend;
if (x != null) {
// can set it to null early - we anyway
// reset the flag, so it is no longer useful
canExtend = null;
if (array.length > index && x.getAndSet(false)) {
array[index] = obj;
return new ImmutableArray2<K>(array, len, true);
}
}
extendable = true;
newLen = len + 4;
} else {
extendable = false;
}
@SuppressWarnings("unchecked")
K[] a2 = (K[]) new Object[newLen];
DataUtils.copyWithGap(array, a2, length, index);
a2[index] = obj;
return new ImmutableArray2<K>(a2, len, extendable);
}
/**
* Remove the entry at this index.
*
* @param index the index
* @return the new immutable array
*/
public ImmutableArray2<K> remove(int index) {
int len = length - 1;
if (index == len) {
return new ImmutableArray2<K>(array, len);
}
@SuppressWarnings("unchecked")
K[] a2 = (K[]) new Object[len];
DataUtils.copyExcept(array, a2, length, index);
return new ImmutableArray2<K>(a2, len);
}
/**
* Get a sub-array.
*
* @param fromIndex the index of the first entry
* @param toIndex the end index, plus one
* @return the new immutable array
*/
public ImmutableArray2<K> subArray(int fromIndex, int toIndex) {
int len = toIndex - fromIndex;
if (fromIndex == 0) {
return new ImmutableArray2<K>(array, len);
}
@SuppressWarnings("unchecked")
K[] a2 = (K[]) new Object[len];
System.arraycopy(array, fromIndex, a2, 0, toIndex - fromIndex);
return new ImmutableArray2<K>(a2, len);
}
/**
* Create an immutable array.
*
* @param array the data
* @return the new immutable array
*/
public static <K> ImmutableArray2<K> create(K... array) {
return new ImmutableArray2<K>(array, array.length);
}
/**
* Get the data.
*
* @return the data
*/
public K[] array() {
return array;
}
@Override
public String toString() {
StringBuilder buff = new StringBuilder();
for (K obj : this) {
buff.append(' ').append(obj);
}
return buff.toString();
}
/**
* Get an empty immutable array.
*
* @param <K> the key type
* @return the array
*/
@SuppressWarnings("unchecked")
public static <K> ImmutableArray2<K> empty() {
return (ImmutableArray2<K>) EMPTY;
}
/**
* Get an iterator over all entries.
*
* @return the iterator
*/
@Override
public Iterator<K> iterator() {
return new Iterator<K>() {
ImmutableArray2<K> a = ImmutableArray2.this;
int index;
@Override
public boolean hasNext() {
return index < a.length();
}
@Override
public K next() {
return a.get(index++);
}
@Override
public void remove() {
throw DataUtils.newUnsupportedOperationException("remove");
}
};
}
}