1 package nom.tam.util;
\r
3 /* Copyright: Thomas McGlynn 1997-1999.
\r
4 * This code may be used for any purpose, non-commercial
\r
5 * or commercial so long as this copyright notice is retained
\r
6 * in the source code or included in or referred to in any
\r
9 /** This class is intended for high performance I/O in scientific applications.
\r
10 * It adds buffering to the RandomAccessFile and also
\r
11 * provides efficient handling of arrays. Primitive arrays
\r
12 * may be written using a single method call. Large buffers
\r
13 * are used to minimize synchronization overheads since methods
\r
14 * of this class are not synchronized.
\r
16 * Note that although this class supports most of the
\r
17 * contract of RandomAccessFile it does not (and can not)
\r
18 * extend that class since many of the methods of
\r
19 * RandomAccessFile are final. In practice this
\r
20 * method works much like the StreamFilter classes.
\r
21 * All methods are implemented in this class but
\r
22 * some are simply delegated to an underlying RandomAccessFile member.
\r
24 * Testing and timing routines are available in
\r
25 * the nom.tam.util.test.BufferedFileTester class.
\r
27 * Version 1.1 October 12, 2000: Fixed handling of EOF in array reads
\r
28 * so that a partial array will be returned when an EOF is detected.
\r
29 * Excess bytes that cannot be used to construct array elements will
\r
30 * be discarded (e.g., if there are 2 bytes left and the user is
\r
31 * reading an int array).
\r
32 * Version 1.2 December 8, 2002: Added getChannel method.
\r
33 * Version 1.3 March 2, 2007: Added File based constructors.
\r
34 * Version 1.4 July 20, 2009: Added support for >2G Object reads.
\r
35 * This is still a bit problematic in that we do not support
\r
36 * primitive arrays larger than 2 GB/atomsize. However except
\r
37 * in the case of bytes this is not currently a major issue.
\r
40 import java.io.RandomAccessFile;
\r
41 import java.io.File;
\r
42 import java.io.FileDescriptor;
\r
43 import java.io.IOException;
\r
44 import java.io.EOFException;
\r
46 public class BufferedFile
\r
47 implements ArrayDataInput, ArrayDataOutput, RandomAccess {
\r
49 /** The current offset into the buffer */
\r
50 private int bufferOffset;
\r
51 /** The number of valid characters in the buffer */
\r
52 private int bufferLength;
\r
53 /** The declared length of the buffer array */
\r
54 private int bufferSize;
\r
55 /** Counter used in reading arrays */
\r
56 private long primitiveArrayCount;
\r
57 /** The data buffer. */
\r
58 private byte[] buffer;
\r
59 /** The underlying access to the file system */
\r
60 private RandomAccessFile raf;
\r
61 /** The offset of the beginning of the current buffer */
\r
62 private long fileOffset;
\r
63 /** Is the buffer being used for input or output */
\r
64 private boolean doingInput;
\r
66 /** Create a read-only buffered file */
\r
67 public BufferedFile(String filename) throws IOException {
\r
68 this(filename, "r", 32768);
\r
71 /** Create a buffered file with the given mode.
\r
72 * @param filename The file to be accessed.
\r
73 * @param mode A string composed of "r" and "w" for
\r
74 * read and write access.
\r
76 public BufferedFile(String filename, String mode) throws IOException {
\r
77 this(filename, mode, 32768);
\r
80 /** Create a buffered file from a File descriptor */
\r
81 public BufferedFile(File file) throws IOException {
\r
82 this(file, "r", 32768);
\r
85 /** Create a buffered file from a File descriptor */
\r
86 public BufferedFile(File file, String mode) throws IOException {
\r
87 this(file, mode, 32768);
\r
90 /** Create a buffered file with the given mode and a specified
\r
92 * @param filename The file to be accessed.
\r
93 * @param mode A string composed of "r" and "w" indicating
\r
94 * read or write access.
\r
95 * @param buffer The buffer size to be used. This should be
\r
96 * substantially larger than 100 bytes and
\r
97 * defaults to 32768 bytes in the other
\r
100 public BufferedFile(String filename, String mode, int bufferSize) throws IOException {
\r
102 File file = new File(filename);
\r
103 initialize(file, mode, bufferSize);
\r
106 /** Create a buffered file from a file descriptor */
\r
107 public BufferedFile(File file, String mode, int bufferSize) throws IOException {
\r
108 initialize(file, mode, bufferSize);
\r
111 protected void initialize(File file, String mode, int bufferSize) throws IOException {
\r
113 raf = new RandomAccessFile(file, mode);
\r
114 buffer = new byte[bufferSize];
\r
118 this.bufferSize = bufferSize;
\r
122 /** Create a buffered file using a mapped
\r
125 /** Read an entire byte array.
\r
126 * Note BufferedFile will return a partially filled array
\r
127 * only at an end-of-file.
\r
128 * @param buf The array to be filled.
\r
130 public int read(byte[] buf) throws IOException {
\r
131 return read(buf, 0, buf.length);
\r
134 /** Read into a segment of a byte array.
\r
135 * @param buf The array to be filled.
\r
136 * @param offset The starting location for input.
\r
137 * @param length The number of bytes to be read. Fewer bytes
\r
138 * will be read if an EOF is reached.
\r
140 public int read(byte[] buf, int offset, int len) throws IOException {
\r
145 // Ensure that the entire buffer is read.
\r
148 if (bufferOffset < bufferLength) {
\r
151 if (bufferOffset + get > bufferLength) {
\r
152 get = bufferLength - bufferOffset;
\r
154 System.arraycopy(buffer, bufferOffset, buf, offset, get);
\r
156 bufferOffset += get;
\r
163 // This might be pretty long, but we know that the
\r
164 // old buffer is exhausted.
\r
166 if (len > bufferSize) {
\r
167 checkBuffer(bufferSize);
\r
171 } catch (EOFException e) {
\r
172 if (bufferLength > 0) {
\r
173 System.arraycopy(buffer, 0, buf, offset, bufferLength);
\r
174 total += bufferLength;
\r
189 /** This should only be used when a small number of
\r
190 * bytes is required (substantially smaller than
\r
193 private void checkBuffer(int needBytes) throws IOException {
\r
195 // Check if the buffer has some pending output.
\r
196 if (!doingInput && bufferOffset > 0) {
\r
201 if (bufferOffset + needBytes < bufferLength) {
\r
204 /* Move the last few bytes to the beginning of the buffer
\r
205 * and read in enough data to fill the current demand.
\r
207 int len = bufferLength - bufferOffset;
\r
209 /* Note that new location that the beginning of the buffer
\r
212 fileOffset += bufferOffset;
\r
214 System.arraycopy(buffer, bufferOffset, buffer, 0, len);
\r
217 bufferLength = len;
\r
220 while (needBytes > 0) {
\r
221 len = raf.read(buffer, bufferLength, bufferSize - bufferLength);
\r
223 throw new EOFException();
\r
226 bufferLength += len;
\r
231 public int read() throws IOException {
\r
234 return buffer[bufferOffset - 1];
\r
237 /** Skip from the current position.
\r
238 * @param offset The number of bytes from the
\r
239 * current position. This may
\r
242 public long skip(long offset) throws IOException {
\r
244 if (offset > 0 && fileOffset + bufferOffset + offset > raf.length()) {
\r
245 offset = raf.length() - fileOffset - bufferOffset;
\r
246 seek(raf.length());
\r
247 } else if (fileOffset + bufferOffset + offset < 0) {
\r
248 offset = -(fileOffset + bufferOffset);
\r
251 seek(fileOffset + bufferOffset + offset);
\r
256 /** Move to the current offset from the beginning of the file.
\r
257 * A user may move past the end of file but this
\r
258 * does not extend the file unless data is written there.
\r
260 public void seek(long offsetFromStart) throws IOException {
\r
263 // Have to flush before a seek...
\r
267 // Are we within the current buffer?
\r
268 if (fileOffset <= offsetFromStart && offsetFromStart < fileOffset + bufferLength) {
\r
269 bufferOffset = (int) (offsetFromStart - fileOffset);
\r
272 // Seek to the desired location.
\r
273 if (offsetFromStart < 0) {
\r
274 offsetFromStart = 0;
\r
277 fileOffset = offsetFromStart;
\r
278 raf.seek(fileOffset);
\r
280 // Invalidate the current buffer.
\r
287 * @return a boolean generated from the next
\r
288 * byte in the input.
\r
290 public boolean readBoolean() throws IOException {
\r
291 return convertToBoolean();
\r
294 /** Get a boolean from the buffer */
\r
295 private boolean convertToBoolean() throws IOException {
\r
298 return buffer[bufferOffset - 1] == 1;
\r
302 * @return the next byte in the input.
\r
304 public byte readByte() throws IOException {
\r
307 return buffer[bufferOffset - 1];
\r
310 /** Read an unsigned byte.
\r
311 * @return the unsigned value of the next byte as
\r
314 public int readUnsignedByte() throws IOException {
\r
317 return buffer[bufferOffset - 1] | 0x00ff;
\r
321 * @return an integer read from the input.
\r
323 public int readInt() throws IOException {
\r
324 return convertToInt();
\r
327 /** Get an integer value from the buffer */
\r
328 private int convertToInt() throws IOException {
\r
330 int x = bufferOffset;
\r
331 int i = buffer[x] << 24 | (buffer[x + 1] & 0xFF) << 16 | (buffer[x + 2] & 0xFF) << 8 | (buffer[x + 3] & 0xFF);
\r
337 * @return a short read from the input.
\r
339 public short readShort() throws IOException {
\r
340 return convertToShort();
\r
343 /** Get a short from the buffer */
\r
344 private short convertToShort() throws IOException {
\r
346 short s = (short) (buffer[bufferOffset] << 8 | (buffer[bufferOffset + 1] & 0xFF));
\r
351 /** Read an unsigned short.
\r
352 * @return an unsigned short value as an integer.
\r
354 public int readUnsignedShort() throws IOException {
\r
355 return readShort() & 0xFFFF;
\r
359 * @return a char read from the input.
\r
361 public char readChar() throws IOException {
\r
362 return convertToChar();
\r
365 /** Get a char from the buffer */
\r
366 private char convertToChar() throws IOException {
\r
368 char c = (char) (buffer[bufferOffset] << 8 | (buffer[bufferOffset + 1] & 0xFF));
\r
374 * @return a long value read from the input.
\r
376 public long readLong() throws IOException {
\r
377 return convertToLong();
\r
380 /** Get a long value from the buffer */
\r
381 private long convertToLong() throws IOException {
\r
383 int x = bufferOffset;
\r
385 int i1 = buffer[x] << 24 | (buffer[x + 1] & 0xFF) << 16 | (buffer[x + 2] & 0xFF) << 8 | (buffer[x + 3] & 0xFF);
\r
386 int i2 = buffer[x + 4] << 24 | (buffer[x + 5] & 0xFF) << 16 | (buffer[x + 6] & 0xFF) << 8 | (buffer[x + 7] & 0xFF);
\r
389 return (((long) i1) << 32) | (((long) i2) & 0x00000000ffffffffL);
\r
393 * @return a float value read from the input.
\r
395 public float readFloat() throws IOException {
\r
396 return Float.intBitsToFloat(convertToInt());
\r
400 * @return a double value read from the input.
\r
402 public double readDouble() throws IOException {
\r
403 return Double.longBitsToDouble(convertToLong());
\r
406 /** Read a byte array fully.
\r
407 * Since the read method of this class reads an entire
\r
408 * buffer, the only difference with readFully is that
\r
409 * readFully will signal an EOF if the buffer cannot be filled.
\r
411 public void readFully(byte[] b) throws IOException {
\r
412 readFully(b, 0, b.length);
\r
415 /** Read a byte array fully.
\r
416 * Since the read method of this class reads an entire
\r
417 * buffer, the only difference with readFully is that
\r
418 * readFully will signal an EOF if the buffer cannot be filled.
\r
420 public void readFully(byte[] b, int off, int len) throws IOException {
\r
422 if (off < 0 || len < 0 || off + len > b.length) {
\r
423 throw new IOException("Attempt to read outside byte array");
\r
426 if (read(b, off, len) < len) {
\r
427 throw new EOFException();
\r
431 /** Skip the number of bytes.
\r
432 * This differs from the skip method in that
\r
433 * it will throw an EOF if a forward skip cannot
\r
434 * be fully accomplished... (However that isn't
\r
435 * supposed to happen with a random access file,
\r
436 * so there is probably no operational difference).
\r
438 public int skipBytes(int toSkip) throws IOException {
\r
439 return (int) skipBytes((long) toSkip);
\r
442 public long skipBytes(long toSkip) throws IOException {
\r
444 // Note that we allow negative skips...
\r
445 if (skip(toSkip) < toSkip) {
\r
446 throw new EOFException();
\r
452 /** Read a string encoded as a UTF.
\r
453 * @return the string.
\r
455 public String readUTF() throws IOException {
\r
457 raf.seek(fileOffset + bufferOffset);
\r
458 String utf = raf.readUTF();
\r
459 fileOffset = raf.getFilePointer();
\r
461 // Invalidate the buffer.
\r
468 /** Read a line of input.
\r
469 * @return the next line.
\r
471 public String readLine() throws IOException {
\r
474 raf.seek(fileOffset + bufferOffset);
\r
475 String line = raf.readLine();
\r
476 fileOffset = raf.getFilePointer();
\r
478 // Invalidate the buffer.
\r
485 /** This routine provides efficient reading of arrays of any primitive type.
\r
486 * @deprecated The readLArray(Object) routine should be used to
\r
487 * ensure that large arrays which read more than
\r
488 * two-gigabytes return the proper value.
\r
491 * @param o The object to be read. It must be an array of a primitive type,
\r
492 * or an array of Object's.
\r
494 public int readArray(Object o) throws IOException {
\r
495 return (int) readLArray(o);
\r
498 /** This routine provides efficient reading of arrays of any primitive
\r
500 * @param o The object to be read. It must be an arraof of a primtive type
\r
501 * (or any dimension), or an array of Objects which contains
\r
502 * pointers to primitive arrays or other object arrays.
\r
504 public long readLArray(Object o) throws IOException {
\r
507 // Note that we assume that only a single thread is
\r
508 // doing a primitive Array read at any given time. Otherwise
\r
509 // primitiveArrayCount can be wrong and also the
\r
510 // input data can be mixed up. If this assumption is not
\r
511 // true we need to synchronize this call.
\r
513 primitiveArrayCount = 0;
\r
514 return primitiveArrayRecurse(o);
\r
517 protected long primitiveArrayRecurse(Object o) throws IOException {
\r
520 return primitiveArrayCount;
\r
523 String className = o.getClass().getName();
\r
525 if (className.charAt(0) != '[') {
\r
526 throw new IOException("Invalid object passed to BufferedDataInputStream.readArray:" + className);
\r
529 // Is this a multidimensional array? If so process recursively.
\r
530 if (className.charAt(1) == '[') {
\r
531 for (int i = 0; i < ((Object[]) o).length; i += 1) {
\r
532 primitiveArrayRecurse(((Object[]) o)[i]);
\r
536 // This is a one-d array. Process it using our special functions.
\r
537 switch (className.charAt(1)) {
\r
539 primitiveArrayCount += read((boolean[]) o, 0, ((boolean[]) o).length);
\r
542 int len = read((byte[]) o, 0, ((byte[]) o).length);
\r
545 primitiveArrayCount += read((char[]) o, 0, ((char[]) o).length);
\r
548 primitiveArrayCount += read((short[]) o, 0, ((short[]) o).length);
\r
551 primitiveArrayCount += read((int[]) o, 0, ((int[]) o).length);
\r
554 primitiveArrayCount += read((long[]) o, 0, ((long[]) o).length);
\r
557 primitiveArrayCount += read((float[]) o, 0, ((float[]) o).length);
\r
560 primitiveArrayCount += read((double[]) o, 0, ((double[]) o).length);
\r
564 // Handle an array of Objects by recursion. Anything
\r
565 // else is an error.
\r
566 if (className.equals("[Ljava.lang.Object;")) {
\r
567 for (int i = 0; i < ((Object[]) o).length; i += 1) {
\r
568 primitiveArrayRecurse(((Object[]) o)[i]);
\r
571 throw new IOException("Invalid object passed to BufferedFile.readPrimitiveArray: " + className);
\r
575 throw new IOException("Invalid object passed to BufferedDataInputStream.readArray: " + className);
\r
578 return primitiveArrayCount;
\r
581 public int read(boolean[] b) throws IOException {
\r
582 return read(b, 0, b.length);
\r
585 public int read(boolean[] b, int start, int length) throws IOException {
\r
589 for (; i < start + length; i += 1) {
\r
590 b[i] = convertToBoolean();
\r
593 } catch (EOFException e) {
\r
594 return eofCheck(e, start, i, 1);
\r
598 public int read(short[] s) throws IOException {
\r
599 return read(s, 0, s.length);
\r
602 public int read(short[] s, int start, int length) throws IOException {
\r
606 for (; i < start + length; i += 1) {
\r
607 s[i] = convertToShort();
\r
610 } catch (EOFException e) {
\r
611 return eofCheck(e, start, i, 2);
\r
615 public int read(char[] c) throws IOException {
\r
616 return read(c, 0, c.length);
\r
619 public int read(char[] c, int start, int length) throws IOException {
\r
623 for (; i < start + length; i += 1) {
\r
624 c[i] = convertToChar();
\r
627 } catch (EOFException e) {
\r
628 return eofCheck(e, start, i, 2);
\r
632 public int read(int[] i) throws IOException {
\r
633 return read(i, 0, i.length);
\r
636 public int read(int[] i, int start, int length) throws IOException {
\r
640 for (; ii < start + length; ii += 1) {
\r
641 i[ii] = convertToInt();
\r
644 } catch (EOFException e) {
\r
645 return eofCheck(e, start, ii, 4);
\r
649 public int read(long[] l) throws IOException {
\r
650 return read(l, 0, l.length);
\r
653 public int read(long[] l, int start, int length) throws IOException {
\r
657 for (; i < start + length; i += 1) {
\r
658 l[i] = convertToLong();
\r
661 } catch (EOFException e) {
\r
662 return eofCheck(e, start, i, 8);
\r
667 public int read(float[] f) throws IOException {
\r
668 return read(f, 0, f.length);
\r
671 public int read(float[] f, int start, int length) throws IOException {
\r
675 for (; i < start + length; i += 1) {
\r
676 f[i] = Float.intBitsToFloat(convertToInt());
\r
679 } catch (EOFException e) {
\r
680 return eofCheck(e, start, i, 4);
\r
684 public int read(double[] d) throws IOException {
\r
685 return read(d, 0, d.length);
\r
688 public int read(double[] d, int start, int length) throws IOException {
\r
692 for (; i < start + length; i += 1) {
\r
693 d[i] = Double.longBitsToDouble(convertToLong());
\r
696 } catch (EOFException e) {
\r
697 return eofCheck(e, start, i, 8);
\r
701 /** See if an exception should be thrown during an array read. */
\r
702 private int eofCheck(EOFException e, int start, int index, int length)
\r
703 throws EOFException {
\r
704 if (start == index) {
\r
707 return (index - start) * length;
\r
711 /**** Output Routines ****/
\r
712 private void needBuffer(int need) throws IOException {
\r
716 fileOffset += bufferOffset;
\r
717 raf.seek(fileOffset);
\r
719 doingInput = false;
\r
725 if (bufferOffset + need >= bufferSize) {
\r
726 raf.write(buffer, 0, bufferOffset);
\r
727 fileOffset += bufferOffset;
\r
732 public void write(int buf) throws IOException {
\r
733 convertFromByte(buf);
\r
736 public void write(byte[] buf) throws IOException {
\r
737 write(buf, 0, buf.length);
\r
740 public void write(byte[] buf, int offset, int length) throws IOException {
\r
742 if (length < bufferSize) {
\r
743 /* If we can use the buffer do so... */
\r
744 needBuffer(length);
\r
745 System.arraycopy(buf, offset, buffer, bufferOffset, length);
\r
746 bufferOffset += length;
\r
748 /* Otherwise flush the buffer and write the data directly.
\r
749 * Make sure that we indicate that the buffer is clean when
\r
754 raf.write(buf, offset, length);
\r
756 fileOffset += length;
\r
758 doingInput = false;
\r
764 /** Flush output buffer if necessary.
\r
765 * This method is not present in RandomAccessFile
\r
766 * but users may need to call flush to ensure
\r
767 * that data has been written.
\r
769 public void flush() throws IOException {
\r
771 if (!doingInput && bufferOffset > 0) {
\r
772 raf.write(buffer, 0, bufferOffset);
\r
773 fileOffset += bufferOffset;
\r
779 /** Clear up any pending output at cleanup.
\r
781 protected void finalize() {
\r
783 if (getFD().valid()) {
\r
787 } catch (Exception e) {
\r
791 /** Write a boolean value
\r
792 * @param b The value to be written. Externally true is represented as
\r
793 * a byte of 1 and false as a byte value of 0.
\r
795 public void writeBoolean(boolean b) throws IOException {
\r
796 convertFromBoolean(b);
\r
799 private void convertFromBoolean(boolean b) throws IOException {
\r
802 buffer[bufferOffset] = (byte) 1;
\r
804 buffer[bufferOffset] = (byte) 0;
\r
809 /** Write a byte value.
\r
811 public void writeByte(int b) throws IOException {
\r
812 convertFromByte(b);
\r
815 private void convertFromByte(int b) throws IOException {
\r
817 buffer[bufferOffset++] = (byte) b;
\r
820 /** Write an integer value.
\r
822 public void writeInt(int i) throws IOException {
\r
826 private void convertFromInt(int i) throws IOException {
\r
829 buffer[bufferOffset++] = (byte) (i >>> 24);
\r
830 buffer[bufferOffset++] = (byte) (i >>> 16);
\r
831 buffer[bufferOffset++] = (byte) (i >>> 8);
\r
832 buffer[bufferOffset++] = (byte) i;
\r
835 /** Write a short value.
\r
837 public void writeShort(int s) throws IOException {
\r
839 convertFromShort(s);
\r
842 private void convertFromShort(int s) throws IOException {
\r
845 buffer[bufferOffset++] = (byte) (s >>> 8);
\r
846 buffer[bufferOffset++] = (byte) s;
\r
849 /** Write a char value.
\r
851 public void writeChar(int c) throws IOException {
\r
852 convertFromChar(c);
\r
855 private void convertFromChar(int c) throws IOException {
\r
857 buffer[bufferOffset++] = (byte) (c >>> 8);
\r
858 buffer[bufferOffset++] = (byte) c;
\r
861 /** Write a long value.
\r
863 public void writeLong(long l) throws IOException {
\r
864 convertFromLong(l);
\r
867 private void convertFromLong(long l) throws IOException {
\r
870 buffer[bufferOffset++] = (byte) (l >>> 56);
\r
871 buffer[bufferOffset++] = (byte) (l >>> 48);
\r
872 buffer[bufferOffset++] = (byte) (l >>> 40);
\r
873 buffer[bufferOffset++] = (byte) (l >>> 32);
\r
874 buffer[bufferOffset++] = (byte) (l >>> 24);
\r
875 buffer[bufferOffset++] = (byte) (l >>> 16);
\r
876 buffer[bufferOffset++] = (byte) (l >>> 8);
\r
877 buffer[bufferOffset++] = (byte) l;
\r
880 /** Write a float value.
\r
882 public void writeFloat(float f) throws IOException {
\r
883 convertFromInt(Float.floatToIntBits(f));
\r
886 /** Write a double value.
\r
888 public void writeDouble(double d) throws IOException {
\r
889 convertFromLong(Double.doubleToLongBits(d));
\r
892 /** Write a string using the local protocol to convert char's to bytes.
\r
894 * @param s The string to be written.
\r
896 public void writeBytes(String s) throws IOException {
\r
897 write(s.getBytes(), 0, s.length());
\r
900 /** Write a string as an array of chars.
\r
902 public void writeChars(String s) throws IOException {
\r
904 int len = s.length();
\r
905 for (int i = 0; i < len; i += 1) {
\r
906 convertFromChar(s.charAt(i));
\r
910 /** Write a string as a UTF.
\r
912 public void writeUTF(String s) throws IOException {
\r
915 fileOffset = raf.getFilePointer();
\r
918 /** This routine provides efficient writing of arrays of any primitive type.
\r
919 * The String class is also handled but it is an error to invoke this
\r
920 * method with an object that is not an array of these types. If the
\r
921 * array is multidimensional, then it calls itself recursively to write
\r
922 * the entire array. Strings are written using the standard
\r
923 * 1 byte format (i.e., as in writeBytes).
\r
925 * If the array is an array of objects, then write will
\r
926 * be called for each element of the array.
\r
928 * @param o The object to be written. It must be an array of a primitive
\r
929 * type, Object, or String.
\r
931 public void writeArray(Object o) throws IOException {
\r
932 String className = o.getClass().getName();
\r
934 if (className.charAt(0) != '[') {
\r
935 throw new IOException("Invalid object passed to BufferedFile.writeArray:" + className);
\r
938 // Is this a multidimensional array? If so process recursively.
\r
939 if (className.charAt(1) == '[') {
\r
940 for (int i = 0; i < ((Object[]) o).length; i += 1) {
\r
941 writeArray(((Object[]) o)[i]);
\r
945 // This is a one-d array. Process it using our special functions.
\r
946 switch (className.charAt(1)) {
\r
948 write((boolean[]) o, 0, ((boolean[]) o).length);
\r
951 write((byte[]) o, 0, ((byte[]) o).length);
\r
954 write((char[]) o, 0, ((char[]) o).length);
\r
957 write((short[]) o, 0, ((short[]) o).length);
\r
960 write((int[]) o, 0, ((int[]) o).length);
\r
963 write((long[]) o, 0, ((long[]) o).length);
\r
966 write((float[]) o, 0, ((float[]) o).length);
\r
969 write((double[]) o, 0, ((double[]) o).length);
\r
973 // Handle two exceptions: an array of strings, or an
\r
974 // array of objects. .
\r
975 if (className.equals("[Ljava.lang.String;")) {
\r
976 write((String[]) o, 0, ((String[]) o).length);
\r
977 } else if (className.equals("[Ljava.lang.Object;")) {
\r
978 for (int i = 0; i < ((Object[]) o).length; i += 1) {
\r
979 writeArray(((Object[]) o)[i]);
\r
982 throw new IOException("Invalid object passed to BufferedFile.write: " + className);
\r
986 throw new IOException("Invalid object passed to BufferedFile.write: " + className);
\r
992 /** Write an array of booleans.
\r
994 public void write(boolean[] b) throws IOException {
\r
995 write(b, 0, b.length);
\r
998 public void write(boolean[] b, int start, int length) throws IOException {
\r
999 for (int i = start; i < start + length; i += 1) {
\r
1000 convertFromBoolean(b[i]);
\r
1004 /** Write an array of shorts.
\r
1006 public void write(short[] s) throws IOException {
\r
1007 write(s, 0, s.length);
\r
1010 public void write(short[] s, int start, int length) throws IOException {
\r
1012 for (int i = start; i < start + length; i += 1) {
\r
1013 convertFromShort(s[i]);
\r
1017 /** Write an array of char's.
\r
1019 public void write(char[] c) throws IOException {
\r
1020 write(c, 0, c.length);
\r
1023 public void write(char[] c, int start, int length) throws IOException {
\r
1025 for (int i = start; i < start + length; i += 1) {
\r
1026 convertFromChar(c[i]);
\r
1030 /** Write an array of int's.
\r
1032 public void write(int[] i) throws IOException {
\r
1033 write(i, 0, i.length);
\r
1036 public void write(int[] i, int start, int length) throws IOException {
\r
1037 for (int ii = start; ii < start + length; ii += 1) {
\r
1038 convertFromInt(i[ii]);
\r
1042 /** Write an array of longs.
\r
1044 public void write(long[] l) throws IOException {
\r
1045 write(l, 0, l.length);
\r
1048 public void write(long[] l, int start, int length) throws IOException {
\r
1050 for (int i = start; i < start + length; i += 1) {
\r
1051 convertFromLong(l[i]);
\r
1055 /** Write an array of floats.
\r
1057 public void write(float[] f) throws IOException {
\r
1058 write(f, 0, f.length);
\r
1061 public void write(float[] f, int start, int length) throws IOException {
\r
1062 for (int i = start; i < start + length; i += 1) {
\r
1063 convertFromInt(Float.floatToIntBits(f[i]));
\r
1067 /** Write an array of doubles.
\r
1069 public void write(double[] d) throws IOException {
\r
1070 write(d, 0, d.length);
\r
1073 public void write(double[] d, int start, int length) throws IOException {
\r
1075 for (int i = start; i < start + length; i += 1) {
\r
1076 convertFromLong(Double.doubleToLongBits(d[i]));
\r
1080 /** Write an array of Strings -- equivalent to calling writeBytes for each string.
\r
1082 public void write(String[] s) throws IOException {
\r
1083 write(s, 0, s.length);
\r
1086 public void write(String[] s, int start, int length) throws IOException {
\r
1087 for (int i = start; i < start + length; i += 1) {
\r
1092 /** Close the file */
\r
1093 public void close() throws IOException {
\r
1098 /** Get the file descriptor associated with
\r
1099 * this stream. Note that this returns the file
\r
1100 * descriptor of the associated RandomAccessFile.
\r
1102 public FileDescriptor getFD() throws IOException {
\r
1103 return raf.getFD();
\r
1106 /** Get the channel associated with
\r
1107 * this file. Note that this returns the channel
\r
1108 * of the associated RandomAccessFile.
\r
1109 * Note that since the BufferedFile buffers the I/O's to the
\r
1110 * underlying file, the offset of the channel may be
\r
1111 * different than the offset of the BufferedFile. This
\r
1112 * is different than for a RandomAccessFile where the
\r
1113 * offsets are guaranteed to be the same.
\r
1115 public java.nio.channels.FileChannel getChannel() {
\r
1116 return raf.getChannel();
\r
1119 /** Get the current length of the file.
\r
1121 public long length() throws IOException {
\r
1123 return raf.length();
\r
1126 /** Get the current offset into the file.
\r
1128 public long getFilePointer() {
\r
1129 return fileOffset + bufferOffset;
\r
1132 /** Set the length of the file. This method calls
\r
1133 * the method of the same name in RandomAccessFile which
\r
1134 * is only available in JDK1.2 and greater. This method
\r
1135 * may be deleted for compilation with earlier versions.
\r
1137 * @param newLength The number of bytes at which the file
\r
1141 public void setLength(long newLength) throws IOException {
\r
1144 raf.setLength(newLength);
\r
1145 if (newLength < fileOffset) {
\r
1146 fileOffset = newLength;
\r