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
130 if (ch < 0 && currentFile < raFiles.length - 1) {
131
132 previousFilesLength += filesLength[currentFile];
133
134 currentFile++;
135
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
308 if (line == null && currentFile < raFiles.length - 1) {
309
310 previousFilesLength += filesLength[currentFile];
311
312 currentFile++;
313
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
417 currentFile--;
418 previousFilesLength -= filesLength[currentFile];
419 seek(pos);
420 } else {
421
422 raFiles[0].seek(pos);
423 }
424 } else if (pos > previousFilesLength + filesLength[currentFile]) {
425 if (currentFile < raFiles.length - 1) {
426
427 previousFilesLength += filesLength[currentFile];
428 currentFile++;
429 seek(pos);
430 } else {
431
432 raFiles[currentFile].seek(pos - previousFilesLength);
433 }
434 } else {
435
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
463 return (int) (newpos - oldpos);
464 }
465 }