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