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 }