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 }