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