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 java.io.IOException; 009 import java.io.ObjectInputStream; 010 import java.io.ObjectOutputStream; 011 import java.util.Arrays; 012 import java.lang.reflect.Array; 013 014 /** 015 * An immutable array of <code>short</code>. 016 */ 017 public final class ShortArray extends PowerlessArray<Short> { 018 static private final long serialVersionUID = 1L; 019 020 private /* final */ transient short[] shorts; 021 022 ShortArray(short... shorts) { 023 // Use back door constructor that sets backing store to null. 024 // This lets ConstArray's methods know not to use the backing 025 // store for accessing this object. 026 super(null); 027 this.shorts = shorts; 028 } 029 030 /** 031 * Constructs an array of <code>short</code>s. 032 * @param shorts each element 033 */ 034 static public ShortArray array(final short... shorts) { 035 return new ShortArray(shorts.clone()); 036 } 037 038 /* 039 * The following are necessary because otherwise calls with <=4 arguments 040 * are resolved to the superclass PowerlessArray 041 */ 042 043 /** 044 * Construct an empty <code>ShortArray</code> 045 */ 046 @SuppressWarnings("unchecked") // the warning here seems completely bogus 047 static public ShortArray array() { 048 return new ShortArray(new short[]{}); 049 } 050 051 /** 052 * Construct a <code>ShortArray</code> with one element. 053 * @param value the value 054 */ 055 static public ShortArray array(short value) { 056 return new ShortArray(new short[]{value}); 057 } 058 059 /** 060 * Construct a <code>ShortArray</code> with two elements. 061 * @param value1 the first value 062 * @param value2 the second value 063 */ 064 static public ShortArray array(short value1, short value2) { 065 return new ShortArray(new short[]{value1, value2}); 066 } 067 068 /** 069 * Construct a <code>ShortArray</code> with three elements. 070 * @param value1 the first value 071 * @param value2 the second value 072 * @param value3 the third value 073 */ 074 static public ShortArray array(short value1, short value2, short value3) { 075 return new ShortArray(new short[]{value1, value2, value3}); 076 } 077 078 /** 079 * Construct a <code>ShortArray</code> with four elements. 080 * @param value1 the first value 081 * @param value2 the second value 082 * @param value3 the third value 083 * @param value4 the fourth value 084 */ 085 static public ShortArray array(short value1, short value2, short value3, 086 short value4) { 087 return new ShortArray(new short[]{value1, value2, value3, value4}); 088 } 089 090 // java.io.Serializable interface 091 092 /* 093 * Serialization hacks to prevent the contents from being serialized as a 094 * mutable array. This improves efficiency for projects that serialize 095 * Joe-E objects using Java's serialization API by avoiding treatment of 096 * immutable state as mutable. These methods can otherwise be ignored. 097 */ 098 private void writeObject(final ObjectOutputStream out) throws IOException { 099 out.defaultWriteObject(); 100 101 out.writeInt(shorts.length); 102 for (short c : shorts) { 103 out.writeShort(c); 104 } 105 } 106 107 private void readObject(final ObjectInputStream in) throws 108 IOException, ClassNotFoundException { 109 in.defaultReadObject(); 110 111 final int length = in.readInt(); 112 shorts = new short[length]; 113 for (int i = 0; i < length; ++i) { 114 shorts[i] = in.readShort(); 115 } 116 } 117 118 /* 119 * Methods that must be overriden, as the implementation in ConstArray 120 * would try to use arr, which is null. 121 */ 122 123 // java.lang.Object interface 124 125 /** 126 * Test for equality with another object 127 * @return true if the other object is a {@link ConstArray} with the same 128 * contents as this array 129 */ 130 public boolean equals(final Object other) { 131 if (other instanceof ShortArray) { 132 // Simple case: just compare shortArr fields 133 return Arrays.equals(shorts, ((ShortArray)other).shorts); 134 } else if (other instanceof ConstArray<?>) { 135 // Other array does not have contents in shortArr: 136 // check that length matches, and then compare elements one-by-one 137 final ConstArray<?> otherArray = (ConstArray<?>)other; 138 if (otherArray.length() != shorts.length) { 139 return false; 140 } 141 for (int i = 0; i < shorts.length; ++i) { 142 final Object otherElement = otherArray.get(i); 143 if (!(otherElement instanceof Short) || 144 ((Short)otherElement).shortValue() != shorts[i]) { 145 return false; 146 } 147 } 148 return true; 149 } else { 150 // Only a ConstArray can be equal to a ShortArray 151 return false; 152 } 153 } 154 155 /** 156 * Computes a digest of the array for hashing. The hash code is the same 157 * as {@link java.util.Arrays#hashCode(Object[])} called on a Java array 158 * containing the same elements. 159 * @return a hash code based on the contents of this array 160 */ 161 public int hashCode() { 162 // Because wrappers for primitive types return the same hashCode as 163 // their primitive values, a ShortArray has the same hashCode as a 164 // ConstArray<Short> with the same contents. 165 return Arrays.hashCode(shorts); 166 } 167 168 /** 169 * Return a string representation of the array 170 */ 171 public String toString() { 172 return Arrays.toString(shorts); 173 } 174 175 // org.joe_e.ConstArray interface 176 177 /** 178 * Gets the length of the array. 179 */ 180 public int length() { 181 return shorts.length; 182 } 183 184 /** 185 * Creates a <code>Short</code> for a specified <code>short</code>. 186 * @param i position of the element to return 187 * @throws ArrayIndexOutOfBoundsException <code>i</code> is out of bounds 188 */ 189 public Short get(int i) { 190 return shorts[i]; 191 } 192 193 /** 194 * Return a mutable copy of the array 195 * @param prototype prototype of the array to copy into 196 * @return an array containing the contents of this <code>ConstArray</code> 197 * of the same type as <code>prototype</code> 198 * @throws ArrayStoreException if an element cannot be stored in the array 199 */ 200 @SuppressWarnings("unchecked") 201 public <T> T[] toArray(T[] prototype) { 202 final int len = length(); 203 if (prototype.length < len) { 204 final Class<?> t = prototype.getClass().getComponentType(); 205 prototype = (T[])Array.newInstance(t, len); 206 } 207 208 for (int i = 0; i < len; ++i) { 209 prototype[i] = (T) (Short) shorts[i]; 210 } 211 return prototype; 212 } 213 214 /** 215 * Creates a <code>ShortArray<code> with an appended <code>Short</code>. 216 * @param newShort the element to append 217 * @throws NullPointerException <code>newShort</code> is null 218 */ 219 public ShortArray with(final Short newShort) { 220 return with(newShort.shortValue()); 221 } 222 223 /* 224 * Convenience (more efficient) methods with short 225 */ 226 227 /** 228 * Gets the <code>short</code> at a specified position. 229 * @param i position of the element to return 230 * @throws ArrayIndexOutOfBoundsException <code>i</code> is out of bounds 231 */ 232 public short getShort(final int i) { 233 return shorts[i]; 234 } 235 236 /** 237 * Creates a mutable copy of the <code>short</code> array 238 */ 239 public short[] toShortArray() { 240 return shorts.clone(); 241 } 242 243 /** 244 * Creates a <code>ShortArray</code> with an appended <code>short</code>. 245 * @param newShort the element to append 246 */ 247 public ShortArray with(final short newShort) { 248 final short[] newShorts = new short[shorts.length + 1]; 249 System.arraycopy(shorts, 0, newShorts, 0, shorts.length); 250 newShorts[shorts.length] = newShort; 251 return new ShortArray(newShorts); 252 } 253 254 /** 255 * Return a new <code>ShortArray</code> that contains the same elements 256 * as this one excluding the element at a specified index 257 * @param i the index of the element to exclude 258 * @return the new array 259 */ 260 public ShortArray without(final int i) { 261 final short[] newArr = new short[shorts.length - 1]; 262 System.arraycopy(shorts, 0, newArr, 0, i); 263 System.arraycopy(shorts, i + 1, newArr, i, newArr.length - i); 264 return new ShortArray(newArr); 265 } 266 267 /** 268 * A {@link ShortArray} factory. 269 */ 270 public static final class Builder extends 271 PowerlessArray.Builder<Short> { 272 private short[] shortBuffer; 273 274 /** 275 * Construct an instance with the default internal array length. 276 */ 277 Builder() { 278 this(0); 279 } 280 281 /** 282 * Construct an instance. 283 * @param estimate estimated array length 284 */ 285 Builder(int estimate) { 286 shortBuffer = new short[estimate > 0 ? estimate : 32]; 287 size = 0; 288 } 289 290 // ArrayBuilder<Short> interface 291 /** 292 * Append a <code>Short</code> 293 * @param newShort the element to add 294 * @throws NegativeArraySizeException if the resulting internal array 295 * would exceed the maximum length of a Java array. The builder is 296 * unmodified. 297 */ 298 public void append(Short newShort) { 299 append ((short) newShort); 300 } 301 302 /** 303 * Append an array of <code>Short</code>s 304 * @param newShorts the elements to add 305 * @throws IndexOutOfBoundsException if the resulting internal array 306 * would exceed the maximum length of a Java array. The builder is 307 * unmodified. 308 */ 309 public void append(final Short[] newShorts) { 310 append(newShorts, 0, newShorts.length); 311 } 312 313 /** 314 * Append a range of elements from an array of <code>Short</code>s 315 * @param newShorts the array to add elements from 316 * @param off the index of the first element to add 317 * @param len the number of elements to add 318 * @throws IndexOutOfBoundsException if an out-of-bounds index would 319 * be referenced or the resulting internal array would exceed the 320 * maximum length of a Java array. The builder is unmodified. 321 */ 322 public void append(final Short[] newShorts, 323 final int off, final int len) { 324 int newSize = size + len; 325 if (newSize < 0 || off < 0 || len < 0 || off + len < 0 326 || off + len > newShorts.length) { 327 throw new IndexOutOfBoundsException(); 328 } 329 if (newSize > shortBuffer.length) { 330 int newLength = Math.max(newSize, 2 * shortBuffer.length); 331 System.arraycopy(shortBuffer, 0, 332 shortBuffer = new short[newLength], 0, size); 333 } 334 335 for (int i = 0; i < len; ++i) { 336 shortBuffer[size + i] = newShorts[off + i]; 337 } 338 size = newSize; 339 } 340 341 /** 342 * Create a snapshot of the current content. 343 * @return a <code>ShortArray</code> containing the elements so far 344 */ 345 public ShortArray snapshot() { 346 final short[] arr; 347 if (size == shortBuffer.length) { 348 arr = shortBuffer; 349 } else { 350 arr = new short[size]; 351 System.arraycopy(shortBuffer, 0, arr, 0, size); 352 } 353 return new ShortArray(arr); 354 } 355 356 /* 357 * Convenience (more efficient) methods with short 358 */ 359 /** 360 * Append a <code>short</code> 361 * @param newShort the element to add 362 * @throws NegativeArraySizeException if the resulting internal array 363 * would exceed the maximum length of a Java array. The builder is 364 * unmodified. 365 */ 366 public void append(final short newShort) { 367 if (size == shortBuffer.length) { 368 System.arraycopy(shortBuffer, 0, 369 shortBuffer = new short[2 * size], 0, size); 370 } 371 shortBuffer[size++] = newShort; 372 } 373 374 /** 375 * Append an array of <code>short</code>s 376 * @param newShorts the elements to add 377 * @throws IndexOutOfBoundsException if the resulting internal array 378 * would exceed the maximum length of a Java array. The builder is 379 * unmodified. 380 */ 381 public void append(final short[] newShorts) { 382 append(newShorts, 0, newShorts.length); 383 } 384 385 /** 386 * Append a range of elements from an array of <code>short</code>s 387 * @param newShorts the array to add elements from 388 * @param off the index of the first element to add 389 * @param len the number of elements to add 390 * @throws IndexOutOfBoundsException if an out-of-bounds index would 391 * be referenced or the resulting internal array would exceed the 392 * maximum length of a Java array. The builder is unmodified. 393 */ 394 public void append(final short[] newShorts, final int off, 395 final int len) { 396 int newSize = size + len; 397 if (newSize < 0 || off < 0 || len < 0 || off + len < 0 398 || off + len > newShorts.length) { 399 throw new IndexOutOfBoundsException(); 400 } 401 if (newSize > shortBuffer.length) { 402 int newLength = Math.max(newSize, 2 * shortBuffer.length); 403 System.arraycopy(shortBuffer, 0, 404 shortBuffer = new short[newLength], 0, size); 405 } 406 System.arraycopy(newShorts, off, shortBuffer, size, len); 407 size = newSize; 408 } 409 } 410 411 /* If one only invokes static methods statically, this is sound, since 412 * ByteArray extends PowerlessArray<Byte> and thus this method is 413 * only required to return something of a type covariant with 414 * PowerlessArray.Builder<Byte>. Unfortunately, this is not completely 415 * sound because it is possible to invoke static methods on instances, e.g. 416 * ConstArray.Builder<String> = (ConstArray (ShortArray.array())).builder() 417 * Invocations of append() can then throw ClassCastExceptions. 418 * 419 * I can't see a way to avoid this other than to de-genericize everything. 420 */ 421 422 /** 423 * Get a <code>ShortArray.Builder</code>. This is equivalent to the 424 * constructor. 425 * @return a new builder instance, with the default internal array length 426 */ 427 @SuppressWarnings("unchecked") 428 public static Builder builder() { 429 return new Builder(0); 430 } 431 432 /** 433 * Get a <code>ShortArray.Builder</code>. This is equivalent to the 434 * constructor. 435 * @param estimate estimated array length 436 * @return a new builder instance 437 */ 438 @SuppressWarnings("unchecked") 439 public static Builder builder(final int estimate) { 440 return new Builder(estimate); 441 } 442 }