001 // Copyright 2006-08 Regents of the University of California. May be used
002 // under the terms of the revised BSD license. See LICENSING for details.
003 /**
004 * @author Adrian Mettler
005 */
006 package org.joe_e.array;
007
008 import org.joe_e.Immutable;
009 import org.joe_e.JoeE;
010 import org.joe_e.reflect.Reflection;
011
012 /**
013 * An immutable array containing immutable objects.
014 *
015 * @param <E> the element type of objects contained in the array
016 */
017 public class ImmutableArray<E> extends ConstArray<E> implements Immutable {
018 static private final long serialVersionUID = 1L;
019
020
021 /**
022 * Package-scope back-door constructor for use by subclasses that
023 * override all methods that make use of the field arr. Nullity of arr is
024 * used to distinguish between instances with which this class must interact
025 * by using the public interface rather than through their arr field.
026 */
027 ImmutableArray(Object[] arr) {
028 super(arr);
029 }
030
031 /**
032 * Construct a <code>ImmutableArray</code>.
033 * @param values each value
034 * @throws ClassCastException if the runtime component type of
035 * <code>values</code> is not immutable in the overlay type system
036 */
037 static public <T> ImmutableArray<T> array(final T... values) {
038 final Class<?> e = values.getClass().getComponentType();
039 if (!JoeE.isSubtypeOf(e, Immutable.class)) {
040 throw new ClassCastException(Reflection.getName(e) +
041 " is not Immutable");
042 }
043 return new ImmutableArray<T>(values.clone());
044 }
045
046 /*
047 * See the comment in ConstArray for why the following methods exist.
048 */
049
050 /**
051 * Construct an empty <code>ConstArray</code>.
052 */
053 static public <T> ImmutableArray<T> array() {
054 return new ImmutableArray<T>(new Object[]{});
055 }
056
057 /**
058 * Checks an array to see that all elements are immutable; if so wraps
059 * it WITHOUT a defensive copy
060 */
061 static private <T> ImmutableArray<T> array2(final Object... values) {
062 for (Object v: values) {
063 Class<?> vType = v.getClass();
064 if (!JoeE.isSubtypeOf(vType, Immutable.class)) {
065 throw new ClassCastException(Reflection.getName(vType) +
066 " is not Immutable");
067 }
068 }
069 return new ImmutableArray<T>(values);
070 }
071
072 /**
073 * Construct a <code>ConstArray</code> with one element.
074 * @param value the value
075 */
076 static public <T> ImmutableArray<T> array(final T value) {
077 return array2(value);
078 }
079
080 /**
081 * Construct a <code>ConstArray</code> with two elements.
082 * @param value1 the first value
083 * @param value2 the second value
084 */
085 static public <T> ImmutableArray<T> array(final T value1, final T value2) {
086 return array2(value1, value2);
087 }
088
089 /**
090 * Construct an <code>ImmutableArray</code> with three elements.
091 * @param value1 the first value
092 * @param value2 the second value
093 * @param value3 the third value
094 */
095 static public <T> ImmutableArray<T> array(final T value1, final T value2,
096 final T value3) {
097 return array2(value1, value2, value3);
098 }
099
100 /**
101 * Construct an <code>ImmutableArray</code> with four elements.
102 * @param value1 the first value
103 * @param value2 the second value
104 * @param value3 the third value
105 * @param value4 the fourth value
106 */
107 static public <T> ImmutableArray<T> array(final T value1, final T value2,
108 final T value3, final T value4) {
109 return array2(value1, value2, value3, value4);
110 }
111
112 /**
113 * Is this object's equals() implementation known to be reflexive?
114 */
115 private transient boolean reflexive = false;
116
117 public boolean equals(final Object x) {
118 return this == x ? reflexive || (reflexive = super.equals(x)) :
119 super.equals(x);
120 }
121
122 /**
123 * Return a new <code>PowerlessArray</code> that contains the same elements
124 * as this one but with a new element added to the end.
125 * @param newE an element to add
126 * @return the new array
127 * @throws ClassCastException if <code>newE</code> is not immutable
128 */
129 public ImmutableArray<E> with(final E newE) {
130 if (!JoeE.instanceOf(newE, Immutable.class)) {
131 throw new ClassCastException(Reflection.getName(newE.getClass()) +
132 "is not Immutable");
133 }
134 // We use a new Object array here, because we don't know the static type
135 // of E that was used; it may not match the dynamic component type of
136 // arr due to array covariance.
137 final Object[] newArr = new Object[arr.length + 1];
138 System.arraycopy(arr, 0, newArr, 0, arr.length);
139 newArr[arr.length] = newE;
140 return new ImmutableArray<E>(newArr);
141 }
142
143 /**
144 * Return a new <code>ImmutableArray</code> that contains the same elements
145 * as this one excluding the element at a specified index
146 * @param i the index of the element to exclude
147 * @return the new array
148 */
149 public ImmutableArray<E> without(final int i) {
150 final Object[] newArr = new Object[arr.length - 1];
151 System.arraycopy(arr, 0, newArr, 0, i);
152 System.arraycopy(arr, i + 1, newArr, i, newArr.length - i);
153 return new ImmutableArray<E>(newArr);
154 }
155
156
157 /**
158 * An {@link ImmutableArray} factory.
159 */
160 public static class Builder<E> extends ConstArray.Builder<E> {
161 /**
162 * Construct an instance with the default internal array length.
163 */
164 Builder() {
165 super();
166 }
167
168 /**
169 * Construct an instance.
170 * @param estimate estimated array length
171 */
172 Builder(int estimate) {
173 super(estimate);
174 }
175
176 /**
177 * Appends an element to the Array
178 * @param newE the element to append
179 * @throws ClassCastException if the <code>newE</code> is not immutable
180 * @throws NegativeArraySizeException if the resulting internal array
181 * would exceed the maximum length of a Java array. The builder is
182 * unmodified.
183 */
184 public void append(E newE) {
185 if (!JoeE.instanceOf(newE, Immutable.class)) {
186 throw new ClassCastException(Reflection.getName(newE.getClass())
187 + "is not Immutable");
188 }
189
190 appendInternal(newE);
191 }
192
193 /**
194 * Appends all elements from a Java array to the Array
195 * @param newEs the element to append
196 * @throws ClassCastException if the <code>newEs</code> is not immutable
197 * @throws IndexOutOfBoundsException if the resulting internal array
198 * would exceed the maximum length of a Java array. The builder is
199 * unmodified.
200 */
201 public void append(E[] newEs) {
202 append(newEs, 0, newEs.length);
203 }
204
205 /**
206 * Appends a range of elements from a Java array to the Array
207 * @param newEs the source array
208 * @param off the index of the first element to append
209 * @param len the number of elements to append
210 * @throws ClassCastException if the <code>newEs</code> is not immutable
211 * @throws IndexOutOfBoundsException if an out-of-bounds index would
212 * be referenced or the resulting internal array would exceed the
213 * maximum length of a Java array. The builder is unmodified.
214 */
215 public void append(E[] newEs, int off, int len) {
216 final Class<?> e = newEs.getClass().getComponentType();
217 if (!JoeE.isSubtypeOf(e, Immutable.class)) {
218 throw new ClassCastException(Reflection.getName(e) +
219 " is not Immutable");
220 }
221
222 appendInternal(newEs, off, len);
223 }
224
225 /**
226 * Create a snapshot of the current content.
227 * @return an <code>ImmutableArray<E></code> containing the elements so far
228 */
229 public ImmutableArray<E> snapshot() {
230 final Object[] arr;
231 if (size == buffer.length) {
232 arr = buffer;
233 } else {
234 arr = new Object[size];
235 System.arraycopy(buffer, 0, arr, 0, size);
236 }
237 return new ImmutableArray<E>(arr);
238 }
239 }
240
241 /**
242 * Get an <code>ImmutableArray.Builder</code>. This is equivalent to the
243 * constructor.
244 * @return a new builder instance, with the default internal array length
245 */
246 public static <E> Builder<E> builder() {
247 return new Builder<E>(0);
248 }
249
250 /**
251 * Get an <code>ImmutableArray.Builder</code>. This is equivalent to the
252 * constructor.
253 * @param estimate estimated array length
254 * @return a new builder instance
255 */
256 public static <E> Builder<E> builder(final int estimate) {
257 return new Builder<E>(estimate);
258 }
259 }