/* * 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.Iterator; import org.h2.mvstore.DataUtils; /** * An immutable array. * * @param <K> the type */ public abstract class ImmutableArray3<K> implements Iterable<K> { private static final int MAX_LEVEL = 4; private static final ImmutableArray3<?> EMPTY = new Plain<Object>(new Object[0]); /** * Get the length. * * @return the length */ public abstract int length(); /** * Get the entry at this index. * * @param index the index * @return the entry */ public abstract K get(int index); /** * Set the entry at this index. * * @param index the index * @param obj the object * @return the new immutable array */ public abstract ImmutableArray3<K> set(int index, K obj); /** * Insert an entry at this index. * * @param index the index * @param obj the object * @return the new immutable array */ public abstract ImmutableArray3<K> insert(int index, K obj); /** * Remove the entry at this index. * * @param index the index * @return the new immutable array */ public abstract ImmutableArray3<K> remove(int index); /** * 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 ImmutableArray3<K> subArray(int fromIndex, int toIndex) { int len = toIndex - fromIndex; @SuppressWarnings("unchecked") K[] array = (K[]) new Object[len]; for (int i = 0; i < len; i++) { array[i] = get(fromIndex + i); } return new Plain<K>(array); } /** * Create an immutable array. * * @param array the data * @return the new immutable array */ public static <K> ImmutableArray3<K> create(K... array) { return new Plain<K>(array); } /** * Get the data. * * @return the data */ public K[] array() { int len = length(); @SuppressWarnings("unchecked") K[] array = (K[]) new Object[len]; for (int i = 0; i < len; i++) { array[i] = get(i); } return array; } /** * Get the level of "abstraction". * * @return the level */ abstract int level(); @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> ImmutableArray3<K> empty() { return (ImmutableArray3<K>) EMPTY; } /** * Get an iterator over all entries. * * @return the iterator */ @Override public Iterator<K> iterator() { return new Iterator<K>() { ImmutableArray3<K> a = ImmutableArray3.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"); } }; } /** * An immutable array backed by an array. * * @param <K> the type */ static class Plain<K> extends ImmutableArray3<K> { /** * The array. */ private final K[] array; public Plain(K[] array) { this.array = array; } @Override public K get(int index) { return array[index]; } @Override public int length() { return array.length; } @Override public ImmutableArray3<K> set(int index, K obj) { return new Set<K>(this, index, obj); } @Override public ImmutableArray3<K> insert(int index, K obj) { return new Insert<K>(this, index, obj); } @Override public ImmutableArray3<K> remove(int index) { return new Remove<K>(this, index); } /** * Get a plain array with the given entry updated. * * @param <K> the type * @param base the base type * @param index the index * @param obj the object * @return the immutable array */ static <K> ImmutableArray3<K> set(ImmutableArray3<K> base, int index, K obj) { int len = base.length(); @SuppressWarnings("unchecked") K[] array = (K[]) new Object[len]; for (int i = 0; i < len; i++) { array[i] = i == index ? obj : base.get(i); } return new Plain<K>(array); } /** * Get a plain array with the given entry inserted. * * @param <K> the type * @param base the base type * @param index the index * @param obj the object * @return the immutable array */ static <K> ImmutableArray3<K> insert(ImmutableArray3<K> base, int index, K obj) { int len = base.length() + 1; @SuppressWarnings("unchecked") K[] array = (K[]) new Object[len]; for (int i = 0; i < len; i++) { array[i] = i == index ? obj : i < index ? base.get(i) : base.get(i - 1); } return new Plain<K>(array); } /** * Get a plain array with the given entry removed. * * @param <K> the type * @param base the base type * @param index the index * @return the immutable array */ static <K> ImmutableArray3<K> remove(ImmutableArray3<K> base, int index) { int len = base.length() - 1; @SuppressWarnings("unchecked") K[] array = (K[]) new Object[len]; for (int i = 0; i < len; i++) { array[i] = i < index ? base.get(i) : base.get(i + 1); } return new Plain<K>(array); } @Override int level() { return 0; } } /** * An immutable array backed by another immutable array, with one element * changed. * * @param <K> the type */ static class Set<K> extends ImmutableArray3<K> { private final int index; private final ImmutableArray3<K> base; private final K obj; Set(ImmutableArray3<K> base, int index, K obj) { this.base = base; this.index = index; this.obj = obj; } @Override public int length() { return base.length(); } @Override public K get(int index) { return this.index == index ? obj : base.get(index); } @Override public ImmutableArray3<K> set(int index, K obj) { if (index == this.index) { return new Set<K>(base, index, obj); } else if (level() < MAX_LEVEL) { return new Set<K>(this, index, obj); } return Plain.set(this, index, obj); } @Override public ImmutableArray3<K> insert(int index, K obj) { if (level() < MAX_LEVEL) { return new Insert<K>(this, index, obj); } return Plain.insert(this, index, obj); } @Override public ImmutableArray3<K> remove(int index) { if (level() < MAX_LEVEL) { return new Remove<K>(this, index); } return Plain.remove(this, index); } @Override int level() { return base.level() + 1; } } /** * An immutable array backed by another immutable array, with one element * added. * * @param <K> the type */ static class Insert<K> extends ImmutableArray3<K> { private final int index; private final ImmutableArray3<K> base; private final K obj; Insert(ImmutableArray3<K> base, int index, K obj) { this.base = base; this.index = index; this.obj = obj; } @Override public ImmutableArray3<K> set(int index, K obj) { if (level() < MAX_LEVEL) { return new Set<K>(this, index, obj); } return Plain.set(this, index, obj); } @Override public ImmutableArray3<K> insert(int index, K obj) { if (level() < MAX_LEVEL) { return new Insert<K>(this, index, obj); } return Plain.insert(this, index, obj); } @Override public ImmutableArray3<K> remove(int index) { if (index == this.index) { return base; } else if (level() < MAX_LEVEL) { return new Remove<K>(this, index); } return Plain.remove(this, index); } @Override public int length() { return base.length() + 1; } @Override public K get(int index) { if (index == this.index) { return obj; } else if (index < this.index) { return base.get(index); } return base.get(index - 1); } @Override int level() { return base.level() + 1; } } /** * An immutable array backed by another immutable array, with one element * removed. * * @param <K> the type */ static class Remove<K> extends ImmutableArray3<K> { private final int index; private final ImmutableArray3<K> base; Remove(ImmutableArray3<K> base, int index) { this.base = base; this.index = index; } @Override public ImmutableArray3<K> set(int index, K obj) { if (level() < MAX_LEVEL) { return new Set<K>(this, index, obj); } return Plain.set(this, index, obj); } @Override public ImmutableArray3<K> insert(int index, K obj) { if (index == this.index) { return base.set(index, obj); } else if (level() < MAX_LEVEL) { return new Insert<K>(this, index, obj); } return Plain.insert(this, index, obj); } @Override public ImmutableArray3<K> remove(int index) { if (level() < MAX_LEVEL) { return new Remove<K>(this, index); } return Plain.remove(this, index); } @Override public int length() { return base.length() - 1; } @Override public K get(int index) { if (index < this.index) { return base.get(index); } return base.get(index + 1); } @Override int level() { return base.level() + 1; } } }