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 }