View Javadoc
1   package net.logAnalyzer.utils;
2   
3   import java.io.DataInput;
4   import java.io.DataInputStream;
5   import java.io.EOFException;
6   import java.io.File;
7   import java.io.FileNotFoundException;
8   import java.io.IOException;
9   import java.io.RandomAccessFile;
10  
11  /***
12   * This class is a wrapper is used to read a set of files. This implementation
13   * acts as a wrapper supporting a small number of methods of the class
14   * {@link java.io.RandomAccessFile}.
15   * 
16   * @author Karim REFEYTON
17   * @version 0.1
18   */
19  public final class FilesSetReader implements DataInput {
20  
21      /***
22       * Current file index.
23       */
24      private int currentFile = 0;
25  
26      /***
27       * Files length.
28       */
29      private long[] filesLength = null;
30  
31      /***
32       * Sum of the length of the previous files.
33       */
34      private long previousFilesLength = 0;
35  
36      /***
37       * RandomAccesFiles.
38       */
39      private RandomAccessFile[] raFiles = null;
40  
41      /***
42       * Total files length.
43       */
44      private long totalLength = 0;
45  
46      /***
47       * Default construtor. Constructs an empty file set.
48       */
49      public FilesSetReader() {
50          currentFile = 0;
51          filesLength = new long[] {};
52          previousFilesLength = 0;
53          raFiles = new RandomAccessFile[] {};
54          totalLength = 0;
55      }
56  
57      /***
58       * Returns the current offset in this log file.
59       * 
60       * @return The offset from the beginning of the file, in bytes, at which the
61       *         next read or write occurs.
62       * @throws IOException
63       *             If an I/O error occurs.
64       * @see RandomAccessFile#getFilePointer()
65       */
66      public long getFilePointer() throws IOException {
67          return previousFilesLength + raFiles[currentFile].getFilePointer();
68      }
69  
70      /***
71       * Opens each input file as a new {@link RandomAccessFile} with mode "r".
72       * 
73       * @param inputFiles
74       *            Files to read.
75       * @throws FileNotFoundException
76       *             If one or more files exist but is a directory rather than
77       *             regular files, or cannot be opened or created for any other
78       *             reason.
79       * @see RandomAccessFile#RandomAccessFile(java.io.File, java.lang.String)
80       */
81      public void setFiles(File[] inputFiles) throws FileNotFoundException {
82          filesLength = new long[inputFiles.length];
83          raFiles = new RandomAccessFile[inputFiles.length];
84          for (int i = 0; i < raFiles.length; i++) {
85              filesLength[i] = inputFiles[i].length();
86              totalLength += filesLength[i];
87              raFiles[i] = new RandomAccessFile(inputFiles[i], "r");
88          }
89      }
90  
91      /***
92       * Closes this random access file stream and releases any system resources
93       * associated with the stream.
94       * 
95       * @throws IOException
96       *             If an I/O error occurs.
97       * @see java.io.Closeable#close()
98       */
99      public void close() throws IOException {
100         for (int i = 0; i < raFiles.length; i++) {
101             raFiles[i].close();
102         }
103     }
104 
105     /***
106      * Returns the total length of files in the set.
107      * 
108      * @return Total length of files in the set.
109      */
110     public long length() {
111         return totalLength;
112     }
113 
114     /***
115      * Reads a byte of data from this file using {@link RandomAccessFile#read()}
116      * on the current file of the set of files. If the end of current file is
117      * reached and the set contains more than one file, tries to read from the
118      * next file.
119      * 
120      * @return the next byte of data, or <code>-1</code> if the end of the
121      *         file has been reached.
122      * @throws IOException
123      *             if an I/O error occurs. Not thrown if end-of-file has been
124      *             reached.
125      * @see RandomAccessFile#read()
126      */
127     public int read() throws IOException {
128         int ch = raFiles[currentFile].read();
129         // If the end of the current file is reached...
130         if (ch < 0 && currentFile < raFiles.length - 1) {
131             // Resets the pointer
132             previousFilesLength += filesLength[currentFile];
133             // Changes to the next file
134             currentFile++;
135             // Reads the first line of the new current file
136             return read();
137         } else {
138             return ch;
139         }
140     }
141 
142     /***
143      * Reads one input byte using {@link #read()} and returns <code>true</code>
144      * if that byte is nonzero, <code>false</code> if that byte is zero.
145      * 
146      * @return the <code>boolean</code> value read.
147      * @throws EOFException
148      *             if this file has reached the end.
149      * @throws IOException
150      *             if an I/O error occurs.
151      * @see java.io.DataInput#readBoolean()
152      */
153     public boolean readBoolean() throws IOException {
154         int ch = read();
155         if (ch < 0) {
156             throw new EOFException();
157         }
158         return (ch != 0);
159     }
160 
161     /***
162      * Reads one input byte using {@link #read()} and casts it to
163      * <code>byte</code>.
164      * 
165      * @return the <code>byte</code> value read.
166      * @throws EOFException
167      *             if this file has reached the end.
168      * @throws IOException
169      *             if an I/O error occurs.
170      * @see java.io.DataInput#readByte()
171      */
172     public byte readByte() throws IOException {
173         int ch = read();
174         if (ch < 0) {
175             throw new EOFException();
176         }
177         return (byte) ch;
178     }
179 
180     /***
181      * Reads an unsigned short using {@link #readUnsignedShort()} and casts it
182      * to <code>char</code>.
183      * 
184      * @return the <code>char</code> value read.
185      * @throws EOFException
186      *             if this file has reached the end.
187      * @throws IOException
188      *             if an I/O error occurs.
189      * @see java.io.DataInput#readChar()
190      */
191     public char readChar() throws IOException {
192         return (char) readUnsignedShort();
193     }
194 
195     /***
196      * Reads a <code>double</code> from this file. This method reads a
197      * <code>long</code> value, starting at the current file pointer, as if by
198      * the <code>readLong</code> method and then converts that
199      * <code>long</code> to a <code>double</code> using
200      * {@link Double#longBitsToDouble(long)}.
201      * 
202      * @return the <code>double</code> value read.
203      * @throws IOException
204      *             if an I/O error occurs.
205      * @see java.io.DataInput#readDouble()
206      */
207     public double readDouble() throws IOException {
208         return Double.longBitsToDouble(readLong());
209     }
210 
211     /***
212      * Reads a <code>float</code> from this file. This method reads a
213      * <code>long</code> value, starting at the current file pointer, as if by
214      * the <code>readLong</code> method and then converts that
215      * <code>long</code> to a <code>float</code> using
216      * {@link Float#intBitsToFloat(int)}.
217      * 
218      * @return the <code>float</code> value read.
219      * @throws IOException
220      *             if an I/O error occurs.
221      * @see java.io.DataInput#readFloat()
222      */
223     public float readFloat() throws IOException {
224         return Float.intBitsToFloat(readInt());
225     }
226 
227     /***
228      * Reads exactly <code>len</code> bytes from this file into the byte array
229      * unsing {@link  #readByte()}, starting at the current file pointer. This
230      * method reads repeatedly from the file until the requested number of bytes
231      * are read. This method blocks until the requested number of bytes are
232      * read, the end of the stream is detected, or an exception is thrown.
233      * 
234      * @param b
235      *            the buffer into which the data is read.
236      * @param off
237      *            the start offset of the data.
238      * @param len
239      *            the number of bytes to read.
240      * @throws EOFException
241      *             if this file reaches the end before reading all the bytes.
242      * @throws IOException
243      *             if an I/O error occurs.
244      * @see DataInput#readFully(byte[], int, int)
245      */
246     public void readFully(byte[] b, int off, int len) throws IOException {
247         skipBytes(off);
248         int n = 0;
249         do {
250             b[n++] = readByte();
251         } while (n < len);
252     }
253 
254     /***
255      * Reads <code>b.length</code> bytes from this file into the byte array
256      * using {@link #readFully(byte[], int, int)}, starting at the current file
257      * pointer.
258      * 
259      * @param b
260      *            the buffer into which the data is read.
261      * @throws EOFException
262      *             if this file reaches the end before reading all the bytes.
263      * @throws IOException
264      *             if an I/O error occurs.
265      * @see DataInput#readFully(byte[])
266      */
267     public void readFully(byte[] b) throws IOException {
268         readFully(b, 0, b.length);
269     }
270 
271     /***
272      * Reads four input bytes using {@link #read()} and combines them to
273      * <code>int</code>.
274      * 
275      * @return the <code>int</code> value read.
276      * @throws EOFException
277      *             if this file has reached the end.
278      * @throws IOException
279      *             if an I/O error occurs.
280      * 
281      * @see DataInput#readInt()
282      */
283     public int readInt() throws IOException {
284         int ch1 = read();
285         int ch2 = read();
286         int ch3 = read();
287         int ch4 = read();
288         if ((ch1 | ch2 | ch3 | ch4) < 0) {
289             throw new EOFException();
290         }
291         return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
292     }
293 
294     /***
295      * Reads the next line of text from this file. If the end of current file is
296      * reached and the set contains more than one file, tries to read from the
297      * next file.
298      * 
299      * @return The next line of text from this file, or null if end of file is
300      *         encountered before even one byte is read.
301      * @throws IOException
302      *             If an I/O error occurs.
303      * @see DataInput#readLine()
304      */
305     public String readLine() throws IOException {
306         String line = raFiles[currentFile].readLine();
307         // If the end of the current file is reached...
308         if (line == null && currentFile < raFiles.length - 1) {
309             // Resets the pointer
310             previousFilesLength += filesLength[currentFile];
311             // Changes to the next file
312             currentFile++;
313             // Reads the first line of the new current file
314             return readLine();
315         } else {
316             return line;
317         }
318     }
319 
320     /***
321      * Reads eight input bytes using {@link #read()} twice and combines them to
322      * <code>long</code>.
323      * 
324      * @return the <code>long</code> value read.
325      * @throws EOFException
326      *             if this file has reached the end.
327      * @throws IOException
328      *             if an I/O error occurs.
329      * 
330      * @see DataInput#readLong()
331      */
332     public long readLong() throws IOException {
333         return ((long) (readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
334     }
335 
336     /***
337      * Reads an unsigned short using {@link #readUnsignedShort()} and casts it
338      * to <code>short</code>.
339      * 
340      * @return the <code>short</code> value read.
341      * @throws EOFException
342      *             if this file has reached the end.
343      * @throws IOException
344      *             if an I/O error occurs.
345      * 
346      * @see DataInput#readShort()
347      */
348     public short readShort() throws IOException {
349         return (short) readUnsignedShort();
350     }
351 
352     /***
353      * Reads a single input byte using {@link #read()} and returns it.
354      * 
355      * @return the <code>int</code> value read.
356      * @throws EOFException
357      *             if this file has reached the end.
358      * @throws IOException
359      *             if an I/O error occurs.
360      * 
361      * @see DataInput#readUnsignedByte()
362      */
363     public int readUnsignedByte() throws IOException {
364         int ch = read();
365         if (ch < 0) {
366             throw new EOFException();
367         }
368         return ch;
369     }
370 
371     /***
372      * Reads two input bytes using {@link #read()} and combines them to
373      * <code>int</code> as an unsigned short.
374      * 
375      * @return the <code>int</code> value read.
376      * @throws EOFException
377      *             if this file has reached the end.
378      * @throws IOException
379      *             if an I/O error occurs.
380      * 
381      * @see DataInput#readUnsignedShort()
382      */
383     public int readUnsignedShort() throws IOException {
384         int ch1 = read();
385         int ch2 = read();
386         if ((ch1 | ch2) < 0) {
387             throw new EOFException();
388         }
389         return (ch1 << 8) + (ch2 << 0);
390     }
391 
392     /***
393      * Reads in a string that has been encoded using a <a
394      * href="#modified-utf-8">modified UTF-8</a> format.
395      * 
396      * @see DataInput#readUTF()
397      */
398     public String readUTF() throws IOException {
399         return DataInputStream.readUTF(this);
400     }
401 
402     /***
403      * Sets the file-pointer offset, measured from the beginning of this file,
404      * at which the next read or write occurs.
405      * 
406      * @param pos
407      *            The offset position, measured in bytes from the beginning of
408      *            the file, at which to set the file pointer.
409      * @throws IOException
410      *             If an I/O error occurs.
411      * @see RandomAccessFile#seek(long)
412      */
413     public void seek(long pos) throws IOException {
414         if (pos < previousFilesLength) {
415             if (currentFile > 0) {
416                 // The position is in a previous file
417                 currentFile--;
418                 previousFilesLength -= filesLength[currentFile];
419                 seek(pos);
420             } else {
421                 // The position is in the first file
422                 raFiles[0].seek(pos);
423             }
424         } else if (pos > previousFilesLength + filesLength[currentFile]) {
425             if (currentFile < raFiles.length - 1) {
426                 // The position is in the next file
427                 previousFilesLength += filesLength[currentFile];
428                 currentFile++;
429                 seek(pos);
430             } else {
431                 // The position is over the last file
432                 raFiles[currentFile].seek(pos - previousFilesLength);
433             }
434         } else {
435             // The position is in the current file
436             raFiles[currentFile].seek(pos - previousFilesLength);
437         }
438     }
439 
440     /***
441      * Attempts to skip over <code>n</code> bytes of input discarding the
442      * skipped bytes.
443      * 
444      * @param n
445      *            the number of bytes to be skipped.
446      * @return the actual number of bytes skipped.
447      * @exception IOException
448      *                if an I/O error occurs.
449      * @see DataInput#skipBytes(int)
450      */
451     public int skipBytes(int n) throws IOException {
452         if (n <= 0) {
453             return 0;
454         }
455         long oldpos = getFilePointer();
456         long newpos = oldpos + n;
457         if (newpos > length()) {
458             newpos = length();
459         }
460         seek(newpos);
461 
462         // Returns the number of skipped bytes
463         return (int) (newpos - oldpos);
464     }
465 }