Begin versioning.
[fits.git] / src / nom / tam / util / BufferedFile.java
1 package nom.tam.util;\r
2 \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
7  * derived software.\r
8  */\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
15  * <p>\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
23  * <p>\r
24  * Testing and timing routines are available in\r
25  * the nom.tam.util.test.BufferedFileTester class.\r
26  *\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
38  *\r
39  */\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
45 \r
46 public class BufferedFile\r
47         implements ArrayDataInput, ArrayDataOutput, RandomAccess {\r
48 \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
65 \r
66     /** Create a read-only buffered file */\r
67     public BufferedFile(String filename) throws IOException {\r
68         this(filename, "r", 32768);\r
69     }\r
70 \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
75      */\r
76     public BufferedFile(String filename, String mode) throws IOException {\r
77         this(filename, mode, 32768);\r
78     }\r
79 \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
83     }\r
84 \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
88     }\r
89 \r
90     /** Create a buffered file with the given mode and a specified\r
91      *  buffer size.\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
98      *                  constructors.\r
99      */\r
100     public BufferedFile(String filename, String mode, int bufferSize) throws IOException {\r
101 \r
102         File file = new File(filename);\r
103         initialize(file, mode, bufferSize);\r
104     }\r
105 \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
109     }\r
110 \r
111     protected void initialize(File file, String mode, int bufferSize) throws IOException {\r
112 \r
113         raf = new RandomAccessFile(file, mode);\r
114         buffer = new byte[bufferSize];\r
115         bufferOffset = 0;\r
116         bufferLength = 0;\r
117         fileOffset = 0;\r
118         this.bufferSize = bufferSize;\r
119 \r
120     }\r
121 \r
122     /** Create a buffered file using a mapped\r
123 \r
124 \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
129      */\r
130     public int read(byte[] buf) throws IOException {\r
131         return read(buf, 0, buf.length);\r
132     }\r
133 \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
139      */\r
140     public int read(byte[] buf, int offset, int len) throws IOException {\r
141 \r
142         checkBuffer(-1);\r
143         int total = 0;\r
144 \r
145         // Ensure that the entire buffer is read.\r
146         while (len > 0) {\r
147 \r
148             if (bufferOffset < bufferLength) {\r
149 \r
150                 int get = len;\r
151                 if (bufferOffset + get > bufferLength) {\r
152                     get = bufferLength - bufferOffset;\r
153                 }\r
154                 System.arraycopy(buffer, bufferOffset, buf, offset, get);\r
155                 len -= get;\r
156                 bufferOffset += get;\r
157                 offset += get;\r
158                 total += get;\r
159                 continue;\r
160 \r
161             } else {\r
162 \r
163                 // This might be pretty long, but we know that the\r
164                 // old buffer is exhausted.\r
165                 try {\r
166                     if (len > bufferSize) {\r
167                         checkBuffer(bufferSize);\r
168                     } else {\r
169                         checkBuffer(len);\r
170                     }\r
171                 } catch (EOFException e) {\r
172                     if (bufferLength > 0) {\r
173                         System.arraycopy(buffer, 0, buf, offset, bufferLength);\r
174                         total += bufferLength;\r
175                         bufferLength = 0;\r
176                     }\r
177                     if (total == 0) {\r
178                         throw e;\r
179                     } else {\r
180                         return total;\r
181                     }\r
182                 }\r
183             }\r
184         }\r
185 \r
186         return total;\r
187     }\r
188 \r
189     /** This should only be used when a small number of\r
190      * bytes is required (substantially smaller than\r
191      * bufferSize.\r
192      */\r
193     private void checkBuffer(int needBytes) throws IOException {\r
194 \r
195         // Check if the buffer has some pending output.\r
196         if (!doingInput && bufferOffset > 0) {\r
197             flush();\r
198         }\r
199         doingInput = true;\r
200 \r
201         if (bufferOffset + needBytes < bufferLength) {\r
202             return;\r
203         }\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
206          */\r
207         int len = bufferLength - bufferOffset;\r
208 \r
209         /* Note that new location that the beginning of the buffer\r
210          * corresponds to.\r
211          */\r
212         fileOffset += bufferOffset;\r
213         if (len > 0) {\r
214             System.arraycopy(buffer, bufferOffset, buffer, 0, len);\r
215         }\r
216         needBytes -= len;\r
217         bufferLength = len;\r
218         bufferOffset = 0;\r
219 \r
220         while (needBytes > 0) {\r
221             len = raf.read(buffer, bufferLength, bufferSize - bufferLength);\r
222             if (len < 0) {\r
223                 throw new EOFException();\r
224             }\r
225             needBytes -= len;\r
226             bufferLength += len;\r
227         }\r
228     }\r
229 \r
230     /** Read a byte */\r
231     public int read() throws IOException {\r
232         checkBuffer(1);\r
233         bufferOffset += 1;\r
234         return buffer[bufferOffset - 1];\r
235     }\r
236 \r
237     /** Skip from the current position.\r
238      *  @param offset The number of bytes from the\r
239      *                current position.  This may\r
240      *                be negative.\r
241      */\r
242     public long skip(long offset) throws IOException {\r
243 \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
249             seek(0);\r
250         } else {\r
251             seek(fileOffset + bufferOffset + offset);\r
252         }\r
253         return offset;\r
254     }\r
255 \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
259      */\r
260     public void seek(long offsetFromStart) throws IOException {\r
261 \r
262         if (!doingInput) {\r
263             // Have to flush before a seek...\r
264             flush();\r
265         }\r
266 \r
267         // Are we within the current buffer?\r
268         if (fileOffset <= offsetFromStart && offsetFromStart < fileOffset + bufferLength) {\r
269             bufferOffset = (int) (offsetFromStart - fileOffset);\r
270         } else {\r
271 \r
272             // Seek to the desired location.\r
273             if (offsetFromStart < 0) {\r
274                 offsetFromStart = 0;\r
275             }\r
276 \r
277             fileOffset = offsetFromStart;\r
278             raf.seek(fileOffset);\r
279 \r
280             // Invalidate the current buffer.\r
281             bufferLength = 0;\r
282             bufferOffset = 0;\r
283         }\r
284     }\r
285 \r
286     /** Read a boolean\r
287      * @return a boolean generated from the next\r
288      *                     byte in the input.\r
289      */\r
290     public boolean readBoolean() throws IOException {\r
291         return convertToBoolean();\r
292     }\r
293 \r
294     /** Get a boolean from the buffer */\r
295     private boolean convertToBoolean() throws IOException {\r
296         checkBuffer(1);\r
297         bufferOffset += 1;\r
298         return buffer[bufferOffset - 1] == 1;\r
299     }\r
300 \r
301     /** Read a byte\r
302      *  @return the next byte in the input.\r
303      */\r
304     public byte readByte() throws IOException {\r
305         checkBuffer(1);\r
306         bufferOffset += 1;\r
307         return buffer[bufferOffset - 1];\r
308     }\r
309 \r
310     /** Read an unsigned byte.\r
311      *  @return the unsigned value of the next byte as\r
312      *          an integer.\r
313      */\r
314     public int readUnsignedByte() throws IOException {\r
315         checkBuffer(1);\r
316         bufferOffset += 1;\r
317         return buffer[bufferOffset - 1] | 0x00ff;\r
318     }\r
319 \r
320     /** Read an int\r
321      *  @return an integer read from the input.\r
322      */\r
323     public int readInt() throws IOException {\r
324         return convertToInt();\r
325     }\r
326 \r
327     /** Get an integer value from the buffer */\r
328     private int convertToInt() throws IOException {\r
329         checkBuffer(4);\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
332         bufferOffset += 4;\r
333         return i;\r
334     }\r
335 \r
336     /** Read a short\r
337      *  @return a short read from the input.\r
338      */\r
339     public short readShort() throws IOException {\r
340         return convertToShort();\r
341     }\r
342 \r
343     /** Get a short from the buffer */\r
344     private short convertToShort() throws IOException {\r
345         checkBuffer(2);\r
346         short s = (short) (buffer[bufferOffset] << 8 | (buffer[bufferOffset + 1] & 0xFF));\r
347         bufferOffset += 2;\r
348         return s;\r
349     }\r
350 \r
351     /** Read an unsigned short.\r
352      *  @return an unsigned short value as an integer.\r
353      */\r
354     public int readUnsignedShort() throws IOException {\r
355         return readShort() & 0xFFFF;\r
356     }\r
357 \r
358     /** Read a char\r
359      *  @return a char read from the input.\r
360      */\r
361     public char readChar() throws IOException {\r
362         return convertToChar();\r
363     }\r
364 \r
365     /** Get a char from the buffer */\r
366     private char convertToChar() throws IOException {\r
367         checkBuffer(2);\r
368         char c = (char) (buffer[bufferOffset] << 8 | (buffer[bufferOffset + 1] & 0xFF));\r
369         bufferOffset += 2;\r
370         return c;\r
371     }\r
372 \r
373     /** Read a long.\r
374      *  @return a long value read from the input.\r
375      */\r
376     public long readLong() throws IOException {\r
377         return convertToLong();\r
378     }\r
379 \r
380     /** Get a long value from the buffer */\r
381     private long convertToLong() throws IOException {\r
382         checkBuffer(8);\r
383         int x = bufferOffset;\r
384 \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
387         bufferOffset += 8;\r
388 \r
389         return (((long) i1) << 32) | (((long) i2) & 0x00000000ffffffffL);\r
390     }\r
391 \r
392     /** Read a float.\r
393      *  @return a float value read from the input.\r
394      */\r
395     public float readFloat() throws IOException {\r
396         return Float.intBitsToFloat(convertToInt());\r
397     }\r
398 \r
399     /** Read a double.\r
400      *  @return a double value read from the input.\r
401      */\r
402     public double readDouble() throws IOException {\r
403         return Double.longBitsToDouble(convertToLong());\r
404     }\r
405 \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
410      */\r
411     public void readFully(byte[] b) throws IOException {\r
412         readFully(b, 0, b.length);\r
413     }\r
414 \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
419      */\r
420     public void readFully(byte[] b, int off, int len) throws IOException {\r
421 \r
422         if (off < 0 || len < 0 || off + len > b.length) {\r
423             throw new IOException("Attempt to read outside byte array");\r
424         }\r
425 \r
426         if (read(b, off, len) < len) {\r
427             throw new EOFException();\r
428         }\r
429     }\r
430 \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
437      */\r
438     public int skipBytes(int toSkip) throws IOException {\r
439         return (int) skipBytes((long) toSkip);\r
440     }\r
441 \r
442     public long skipBytes(long toSkip) throws IOException {\r
443 \r
444         // Note that we allow negative skips...\r
445         if (skip(toSkip) < toSkip) {\r
446             throw new EOFException();\r
447         } else {\r
448             return toSkip;\r
449         }\r
450     }\r
451 \r
452     /** Read a string encoded as a UTF.\r
453      *  @return the string.\r
454      */\r
455     public String readUTF() throws IOException {\r
456         checkBuffer(-1);\r
457         raf.seek(fileOffset + bufferOffset);\r
458         String utf = raf.readUTF();\r
459         fileOffset = raf.getFilePointer();\r
460 \r
461         // Invalidate the buffer.\r
462         bufferLength = 0;\r
463         bufferOffset = 0;\r
464 \r
465         return utf;\r
466     }\r
467 \r
468     /** Read a line of input.\r
469      *  @return the next line.\r
470      */\r
471     public String readLine() throws IOException {\r
472 \r
473         checkBuffer(-1);\r
474         raf.seek(fileOffset + bufferOffset);\r
475         String line = raf.readLine();\r
476         fileOffset = raf.getFilePointer();\r
477 \r
478         // Invalidate the buffer.\r
479         bufferLength = 0;\r
480         bufferOffset = 0;\r
481 \r
482         return line;\r
483     }\r
484 \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
489      *\r
490      *\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
493      */\r
494     public int readArray(Object o) throws IOException {\r
495         return (int) readLArray(o);\r
496     }\r
497 \r
498     /** This routine provides efficient reading of arrays of any primitive\r
499      *  type.\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
503      */\r
504     public long readLArray(Object o) throws IOException {\r
505 \r
506 \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
512 \r
513         primitiveArrayCount = 0;\r
514         return primitiveArrayRecurse(o);\r
515     }\r
516 \r
517     protected long primitiveArrayRecurse(Object o) throws IOException {\r
518 \r
519         if (o == null) {\r
520             return primitiveArrayCount;\r
521         }\r
522 \r
523         String className = o.getClass().getName();\r
524 \r
525         if (className.charAt(0) != '[') {\r
526             throw new IOException("Invalid object passed to BufferedDataInputStream.readArray:" + className);\r
527         }\r
528 \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
533             }\r
534         } else {\r
535 \r
536             // This is a one-d array.  Process it using our special functions.\r
537             switch (className.charAt(1)) {\r
538                 case 'Z':\r
539                     primitiveArrayCount += read((boolean[]) o, 0, ((boolean[]) o).length);\r
540                     break;\r
541                 case 'B':\r
542                     int len = read((byte[]) o, 0, ((byte[]) o).length);\r
543                     break;\r
544                 case 'C':\r
545                     primitiveArrayCount += read((char[]) o, 0, ((char[]) o).length);\r
546                     break;\r
547                 case 'S':\r
548                     primitiveArrayCount += read((short[]) o, 0, ((short[]) o).length);\r
549                     break;\r
550                 case 'I':\r
551                     primitiveArrayCount += read((int[]) o, 0, ((int[]) o).length);\r
552                     break;\r
553                 case 'J':\r
554                     primitiveArrayCount += read((long[]) o, 0, ((long[]) o).length);\r
555                     break;\r
556                 case 'F':\r
557                     primitiveArrayCount += read((float[]) o, 0, ((float[]) o).length);\r
558                     break;\r
559                 case 'D':\r
560                     primitiveArrayCount += read((double[]) o, 0, ((double[]) o).length);\r
561                     break;\r
562                 case 'L':\r
563 \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
569                         }\r
570                     } else {\r
571                         throw new IOException("Invalid object passed to BufferedFile.readPrimitiveArray: " + className);\r
572                     }\r
573                     break;\r
574                 default:\r
575                     throw new IOException("Invalid object passed to BufferedDataInputStream.readArray: " + className);\r
576             }\r
577         }\r
578         return primitiveArrayCount;\r
579     }\r
580 \r
581     public int read(boolean[] b) throws IOException {\r
582         return read(b, 0, b.length);\r
583     }\r
584 \r
585     public int read(boolean[] b, int start, int length) throws IOException {\r
586 \r
587         int i = start;\r
588         try {\r
589             for (; i < start + length; i += 1) {\r
590                 b[i] = convertToBoolean();\r
591             }\r
592             return length;\r
593         } catch (EOFException e) {\r
594             return eofCheck(e, start, i, 1);\r
595         }\r
596     }\r
597 \r
598     public int read(short[] s) throws IOException {\r
599         return read(s, 0, s.length);\r
600     }\r
601 \r
602     public int read(short[] s, int start, int length) throws IOException {\r
603 \r
604         int i = start;\r
605         try {\r
606             for (; i < start + length; i += 1) {\r
607                 s[i] = convertToShort();\r
608             }\r
609             return length * 2;\r
610         } catch (EOFException e) {\r
611             return eofCheck(e, start, i, 2);\r
612         }\r
613     }\r
614 \r
615     public int read(char[] c) throws IOException {\r
616         return read(c, 0, c.length);\r
617     }\r
618 \r
619     public int read(char[] c, int start, int length) throws IOException {\r
620 \r
621         int i = start;\r
622         try {\r
623             for (; i < start + length; i += 1) {\r
624                 c[i] = convertToChar();\r
625             }\r
626             return length * 2;\r
627         } catch (EOFException e) {\r
628             return eofCheck(e, start, i, 2);\r
629         }\r
630     }\r
631 \r
632     public int read(int[] i) throws IOException {\r
633         return read(i, 0, i.length);\r
634     }\r
635 \r
636     public int read(int[] i, int start, int length) throws IOException {\r
637 \r
638         int ii = start;\r
639         try {\r
640             for (; ii < start + length; ii += 1) {\r
641                 i[ii] = convertToInt();\r
642             }\r
643             return length * 4;\r
644         } catch (EOFException e) {\r
645             return eofCheck(e, start, ii, 4);\r
646         }\r
647     }\r
648 \r
649     public int read(long[] l) throws IOException {\r
650         return read(l, 0, l.length);\r
651     }\r
652 \r
653     public int read(long[] l, int start, int length) throws IOException {\r
654 \r
655         int i = start;\r
656         try {\r
657             for (; i < start + length; i += 1) {\r
658                 l[i] = convertToLong();\r
659             }\r
660             return length * 8;\r
661         } catch (EOFException e) {\r
662             return eofCheck(e, start, i, 8);\r
663         }\r
664 \r
665     }\r
666 \r
667     public int read(float[] f) throws IOException {\r
668         return read(f, 0, f.length);\r
669     }\r
670 \r
671     public int read(float[] f, int start, int length) throws IOException {\r
672 \r
673         int i = start;\r
674         try {\r
675             for (; i < start + length; i += 1) {\r
676                 f[i] = Float.intBitsToFloat(convertToInt());\r
677             }\r
678             return length * 4;\r
679         } catch (EOFException e) {\r
680             return eofCheck(e, start, i, 4);\r
681         }\r
682     }\r
683 \r
684     public int read(double[] d) throws IOException {\r
685         return read(d, 0, d.length);\r
686     }\r
687 \r
688     public int read(double[] d, int start, int length) throws IOException {\r
689 \r
690         int i = start;\r
691         try {\r
692             for (; i < start + length; i += 1) {\r
693                 d[i] = Double.longBitsToDouble(convertToLong());\r
694             }\r
695             return length * 8;\r
696         } catch (EOFException e) {\r
697             return eofCheck(e, start, i, 8);\r
698         }\r
699     }\r
700 \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
705             throw e;\r
706         } else {\r
707             return (index - start) * length;\r
708         }\r
709     }\r
710 \r
711     /**** Output Routines ****/\r
712     private void needBuffer(int need) throws IOException {\r
713 \r
714         if (doingInput) {\r
715 \r
716             fileOffset += bufferOffset;\r
717             raf.seek(fileOffset);\r
718 \r
719             doingInput = false;\r
720 \r
721             bufferOffset = 0;\r
722             bufferLength = 0;\r
723         }\r
724 \r
725         if (bufferOffset + need >= bufferSize) {\r
726             raf.write(buffer, 0, bufferOffset);\r
727             fileOffset += bufferOffset;\r
728             bufferOffset = 0;\r
729         }\r
730     }\r
731 \r
732     public void write(int buf) throws IOException {\r
733         convertFromByte(buf);\r
734     }\r
735 \r
736     public void write(byte[] buf) throws IOException {\r
737         write(buf, 0, buf.length);\r
738     }\r
739 \r
740     public void write(byte[] buf, int offset, int length) throws IOException {\r
741 \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
747         } else {\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
750              * we're done.\r
751              */\r
752             flush();\r
753 \r
754             raf.write(buf, offset, length);\r
755 \r
756             fileOffset += length;\r
757 \r
758             doingInput = false;\r
759             bufferOffset = 0;\r
760             bufferLength = 0;\r
761         }\r
762     }\r
763 \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
768      */\r
769     public void flush() throws IOException {\r
770 \r
771         if (!doingInput && bufferOffset > 0) {\r
772             raf.write(buffer, 0, bufferOffset);\r
773             fileOffset += bufferOffset;\r
774             bufferOffset = 0;\r
775             bufferLength = 0;\r
776         }\r
777     }\r
778 \r
779     /** Clear up any pending output at cleanup.\r
780      */\r
781     protected void finalize() {\r
782         try {\r
783             if (getFD().valid()) {\r
784                 flush();\r
785                 close();\r
786             }\r
787         } catch (Exception e) {\r
788         }\r
789     }\r
790 \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
794      */\r
795     public void writeBoolean(boolean b) throws IOException {\r
796         convertFromBoolean(b);\r
797     }\r
798 \r
799     private void convertFromBoolean(boolean b) throws IOException {\r
800         needBuffer(1);\r
801         if (b) {\r
802             buffer[bufferOffset] = (byte) 1;\r
803         } else {\r
804             buffer[bufferOffset] = (byte) 0;\r
805         }\r
806         bufferOffset += 1;\r
807     }\r
808 \r
809     /** Write a byte value.\r
810      */\r
811     public void writeByte(int b) throws IOException {\r
812         convertFromByte(b);\r
813     }\r
814 \r
815     private void convertFromByte(int b) throws IOException {\r
816         needBuffer(1);\r
817         buffer[bufferOffset++] = (byte) b;\r
818     }\r
819 \r
820     /** Write an integer value.\r
821      */\r
822     public void writeInt(int i) throws IOException {\r
823         convertFromInt(i);\r
824     }\r
825 \r
826     private void convertFromInt(int i) throws IOException {\r
827 \r
828         needBuffer(4);\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
833     }\r
834 \r
835     /** Write a short value.\r
836      */\r
837     public void writeShort(int s) throws IOException {\r
838 \r
839         convertFromShort(s);\r
840     }\r
841 \r
842     private void convertFromShort(int s) throws IOException {\r
843         needBuffer(2);\r
844 \r
845         buffer[bufferOffset++] = (byte) (s >>> 8);\r
846         buffer[bufferOffset++] = (byte) s;\r
847     }\r
848 \r
849     /** Write a char value.\r
850      */\r
851     public void writeChar(int c) throws IOException {\r
852         convertFromChar(c);\r
853     }\r
854 \r
855     private void convertFromChar(int c) throws IOException {\r
856         needBuffer(2);\r
857         buffer[bufferOffset++] = (byte) (c >>> 8);\r
858         buffer[bufferOffset++] = (byte) c;\r
859     }\r
860 \r
861     /** Write a long value.\r
862      */\r
863     public void writeLong(long l) throws IOException {\r
864         convertFromLong(l);\r
865     }\r
866 \r
867     private void convertFromLong(long l) throws IOException {\r
868         needBuffer(8);\r
869 \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
878     }\r
879 \r
880     /** Write a float value.\r
881      */\r
882     public void writeFloat(float f) throws IOException {\r
883         convertFromInt(Float.floatToIntBits(f));\r
884     }\r
885 \r
886     /** Write a double value.\r
887      */\r
888     public void writeDouble(double d) throws IOException {\r
889         convertFromLong(Double.doubleToLongBits(d));\r
890     }\r
891 \r
892     /** Write a string using the local protocol to convert char's to bytes.\r
893      *\r
894      * @param s   The string to be written.\r
895      */\r
896     public void writeBytes(String s) throws IOException {\r
897         write(s.getBytes(), 0, s.length());\r
898     }\r
899 \r
900     /** Write a string as an array of chars.\r
901      */\r
902     public void writeChars(String s) throws IOException {\r
903 \r
904         int len = s.length();\r
905         for (int i = 0; i < len; i += 1) {\r
906             convertFromChar(s.charAt(i));\r
907         }\r
908     }\r
909 \r
910     /** Write a string as a UTF.\r
911      */\r
912     public void writeUTF(String s) throws IOException {\r
913         flush();\r
914         raf.writeUTF(s);\r
915         fileOffset = raf.getFilePointer();\r
916     }\r
917 \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
924      *\r
925      * If the array is an array of objects, then write will\r
926      * be called for each element of the array.\r
927      *\r
928      * @param o  The object to be written.  It must be an array of a primitive\r
929      *           type, Object, or String.\r
930      */\r
931     public void writeArray(Object o) throws IOException {\r
932         String className = o.getClass().getName();\r
933 \r
934         if (className.charAt(0) != '[') {\r
935             throw new IOException("Invalid object passed to BufferedFile.writeArray:" + className);\r
936         }\r
937 \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
942             }\r
943         } else {\r
944 \r
945             // This is a one-d array.  Process it using our special functions.\r
946             switch (className.charAt(1)) {\r
947                 case 'Z':\r
948                     write((boolean[]) o, 0, ((boolean[]) o).length);\r
949                     break;\r
950                 case 'B':\r
951                     write((byte[]) o, 0, ((byte[]) o).length);\r
952                     break;\r
953                 case 'C':\r
954                     write((char[]) o, 0, ((char[]) o).length);\r
955                     break;\r
956                 case 'S':\r
957                     write((short[]) o, 0, ((short[]) o).length);\r
958                     break;\r
959                 case 'I':\r
960                     write((int[]) o, 0, ((int[]) o).length);\r
961                     break;\r
962                 case 'J':\r
963                     write((long[]) o, 0, ((long[]) o).length);\r
964                     break;\r
965                 case 'F':\r
966                     write((float[]) o, 0, ((float[]) o).length);\r
967                     break;\r
968                 case 'D':\r
969                     write((double[]) o, 0, ((double[]) o).length);\r
970                     break;\r
971                 case 'L':\r
972 \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
980                         }\r
981                     } else {\r
982                         throw new IOException("Invalid object passed to BufferedFile.write: " + className);\r
983                     }\r
984                     break;\r
985                 default:\r
986                     throw new IOException("Invalid object passed to BufferedFile.write: " + className);\r
987             }\r
988         }\r
989 \r
990     }\r
991 \r
992     /** Write an array of booleans.\r
993      */\r
994     public void write(boolean[] b) throws IOException {\r
995         write(b, 0, b.length);\r
996     }\r
997 \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
1001         }\r
1002     }\r
1003 \r
1004     /** Write an array of shorts.\r
1005      */\r
1006     public void write(short[] s) throws IOException {\r
1007         write(s, 0, s.length);\r
1008     }\r
1009 \r
1010     public void write(short[] s, int start, int length) throws IOException {\r
1011 \r
1012         for (int i = start; i < start + length; i += 1) {\r
1013             convertFromShort(s[i]);\r
1014         }\r
1015     }\r
1016 \r
1017     /** Write an array of char's.\r
1018      */\r
1019     public void write(char[] c) throws IOException {\r
1020         write(c, 0, c.length);\r
1021     }\r
1022 \r
1023     public void write(char[] c, int start, int length) throws IOException {\r
1024 \r
1025         for (int i = start; i < start + length; i += 1) {\r
1026             convertFromChar(c[i]);\r
1027         }\r
1028     }\r
1029 \r
1030     /** Write an array of int's.\r
1031      */\r
1032     public void write(int[] i) throws IOException {\r
1033         write(i, 0, i.length);\r
1034     }\r
1035 \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
1039         }\r
1040     }\r
1041 \r
1042     /** Write an array of longs.\r
1043      */\r
1044     public void write(long[] l) throws IOException {\r
1045         write(l, 0, l.length);\r
1046     }\r
1047 \r
1048     public void write(long[] l, int start, int length) throws IOException {\r
1049 \r
1050         for (int i = start; i < start + length; i += 1) {\r
1051             convertFromLong(l[i]);\r
1052         }\r
1053     }\r
1054 \r
1055     /** Write an array of floats.\r
1056      */\r
1057     public void write(float[] f) throws IOException {\r
1058         write(f, 0, f.length);\r
1059     }\r
1060 \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
1064         }\r
1065     }\r
1066 \r
1067     /** Write an array of doubles.\r
1068      */\r
1069     public void write(double[] d) throws IOException {\r
1070         write(d, 0, d.length);\r
1071     }\r
1072 \r
1073     public void write(double[] d, int start, int length) throws IOException {\r
1074 \r
1075         for (int i = start; i < start + length; i += 1) {\r
1076             convertFromLong(Double.doubleToLongBits(d[i]));\r
1077         }\r
1078     }\r
1079 \r
1080     /** Write an array of Strings -- equivalent to calling writeBytes for each string.\r
1081      */\r
1082     public void write(String[] s) throws IOException {\r
1083         write(s, 0, s.length);\r
1084     }\r
1085 \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
1088             writeBytes(s[i]);\r
1089         }\r
1090     }\r
1091 \r
1092     /** Close the file */\r
1093     public void close() throws IOException {\r
1094         flush();\r
1095         raf.close();\r
1096     }\r
1097 \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
1101      */\r
1102     public FileDescriptor getFD() throws IOException {\r
1103         return raf.getFD();\r
1104     }\r
1105 \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
1114      */\r
1115     public java.nio.channels.FileChannel getChannel() {\r
1116         return raf.getChannel();\r
1117     }\r
1118 \r
1119     /** Get the current length of the file.\r
1120      */\r
1121     public long length() throws IOException {\r
1122         flush();\r
1123         return raf.length();\r
1124     }\r
1125 \r
1126     /** Get the current offset into the file.\r
1127      */\r
1128     public long getFilePointer() {\r
1129         return fileOffset + bufferOffset;\r
1130     }\r
1131 \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
1136      *\r
1137      * @param newLength The number of bytes at which the file\r
1138      *                  is set.\r
1139      *\r
1140      */\r
1141     public void setLength(long newLength) throws IOException {\r
1142 \r
1143         flush();\r
1144         raf.setLength(newLength);\r
1145         if (newLength < fileOffset) {\r
1146             fileOffset = newLength;\r
1147         }\r
1148 \r
1149     }\r
1150 }\r