KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > schlichtherle > io > rof > BufferedReadOnlyFile


1 /*
2  * BufferedReadOnlyFile.java
3  *
4  * Created on 5. Oktober 2005, 17:22
5  */

6 /*
7  * Copyright 2006 Schlichtherle IT Services
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */

21
22 package de.schlichtherle.io.rof;
23
24 import java.io.File JavaDoc;
25 import java.io.FileNotFoundException JavaDoc;
26 import java.io.IOException JavaDoc;
27
28 /**
29  * This class implements a {@link ReadOnlyFile} in order to provide buffered
30  * random read only access to another <code>ReadOnlyFile</code>.
31  * <p>
32  * <b>Note:</b> This class implements its own virtual file pointer.
33  * Thus, if you would like to access the underlying <code>ReadOnlyFile</code>
34  * again after you have finished working with an instance of this class,
35  * you should synchronize their file pointers using the pattern as described
36  * in {@link FilterReadOnlyFile}.
37  *
38  * @author Christian Schlichtherle
39  */

40 public class BufferedReadOnlyFile extends FilterReadOnlyFile {
41
42     /**
43      * The default buffer length of the window to the file.
44      */

45     private static final int WINDOW_LEN = 4096;
46
47     /** Returns the smaller parameter. */
48     protected static final long min(long a, long b) {
49         return a < b ? a : b;
50     }
51
52     /** Returns the greater parameter. */
53     protected static final long max(long a, long b) {
54         return a < b ? b : a;
55     }
56
57     private long length;
58
59     /**
60      * The virtual file pointer in the file data.
61      * This is relative to the start of the file.
62      */

63     private long fp;
64
65     /**
66      * The current offset in the read only file where the buffer window starts.
67      * This is always a multiple of the buffer window size.
68      */

69     private long windowOff;
70
71     /**
72      * The buffer window to the file data.
73      */

74     private final byte[] window;
75
76     private boolean closed;
77
78     /**
79      * Creates a new instance of <tt>BufferedReadOnlyFile</tt>.
80      *
81      * @param file The file to read.
82      *
83      * @throws NullPointerException If any of the parameters is <tt>null</tt>.
84      * @throws FileNotFoundException If the file cannot get opened for reading.
85      * @throws IOException On any other I/O related issue.
86      */

87     public BufferedReadOnlyFile(
88             final File JavaDoc file)
89     throws NullPointerException JavaDoc,
90             FileNotFoundException JavaDoc,
91             IOException JavaDoc {
92         this(null, file, WINDOW_LEN);
93     }
94
95     /**
96      * Creates a new instance of <tt>BufferedReadOnlyFile</tt>.
97      *
98      * @param file The file to read.
99      * @param windowLen The size of the buffer window in bytes.
100      *
101      * @throws NullPointerException If any of the parameters is <tt>null</tt>.
102      * @throws FileNotFoundException If the file cannot get opened for reading.
103      * @throws IOException On any other I/O related issue.
104      */

105     public BufferedReadOnlyFile(
106             final File JavaDoc file,
107             final int windowLen)
108     throws NullPointerException JavaDoc,
109             FileNotFoundException JavaDoc,
110             IOException JavaDoc {
111         this(null, file, windowLen);
112     }
113
114     /**
115      * Creates a new instance of <tt>BufferedReadOnlyFile</tt>.
116      *
117      * @param rof The read only file to read.
118      *
119      * @throws NullPointerException If any of the parameters is <tt>null</tt>.
120      * @throws FileNotFoundException If the file cannot get opened for reading.
121      * @throws IOException On any other I/O related issue.
122      */

123     public BufferedReadOnlyFile(
124             final ReadOnlyFile rof)
125     throws NullPointerException JavaDoc,
126             FileNotFoundException JavaDoc,
127             IOException JavaDoc {
128         this(rof, null, WINDOW_LEN);
129     }
130
131     /**
132      * Creates a new instance of <tt>BufferedReadOnlyFile</tt>.
133      *
134      * @param rof The read only file to read.
135      * @param windowLen The size of the buffer window in bytes.
136      *
137      * @throws NullPointerException If any of the parameters is <tt>null</tt>.
138      * @throws FileNotFoundException If the file cannot get opened for reading.
139      * @throws IOException On any other I/O related issue.
140      */

141     public BufferedReadOnlyFile(
142             final ReadOnlyFile rof,
143             final int windowLen)
144     throws NullPointerException JavaDoc,
145             FileNotFoundException JavaDoc,
146             IOException JavaDoc {
147         this(rof, null, windowLen);
148     }
149
150     private BufferedReadOnlyFile(
151             ReadOnlyFile rof,
152             final File JavaDoc file,
153             final int windowLen)
154     throws NullPointerException JavaDoc,
155             FileNotFoundException JavaDoc,
156             IOException JavaDoc {
157         super(rof);
158
159         // Check parameters (fail fast).
160
if (rof == null) {
161             if (file == null)
162                 throw new NullPointerException JavaDoc();
163             rof = createReadOnlyFile(file);
164         } else { // rof != null
165
assert file == null;
166         }
167         if (windowLen <= 0)
168             throw new IllegalArgumentException JavaDoc();
169
170         super.rof = rof;
171         length = rof.length();
172         fp = rof.getFilePointer();
173         window = new byte[windowLen];
174         invalidateWindow();
175         
176         assert window.length > 0;
177     }
178
179     /**
180      * A factory method called by the constructor to get a read only file
181      * to access the contents of the RAES file.
182      * This method is only used if the constructor isn't called with a read only
183      * file as its parameter.
184      *
185      * @throws FileNotFoundException If the file cannot get opened for reading.
186      * @throws IOException On any other I/O related issue.
187      */

188     protected ReadOnlyFile createReadOnlyFile(File JavaDoc file)
189     throws IOException JavaDoc {
190         return new SimpleReadOnlyFile(file);
191         //return new FastReadOnlyFile(file);
192
//return new ChannelReadOnlyFile(file);
193
}
194
195     public long length() throws IOException JavaDoc {
196         final long newLength = rof.length();
197         if (newLength != length) {
198             length = newLength;
199             invalidateWindow();
200         }
201
202         return length;
203     }
204
205     public long getFilePointer() throws IOException JavaDoc {
206         ensureOpen();
207
208         return fp;
209     }
210
211     public void seek(final long fp) throws IOException JavaDoc {
212         if (fp < 0)
213             throw new IOException JavaDoc("File pointer must not be negative!");
214
215         ensureOpen();
216
217         final long length = length();
218         if (fp > length)
219             throw new IOException JavaDoc("File pointer (" + fp
220                     + ") is larger than file length (" + length + ")!");
221
222         this.fp = fp;
223     }
224
225     public int read() throws IOException JavaDoc {
226         // Check state.
227
ensureOpen();
228         if (fp >= length())
229             return -1;
230
231         // Position window and return its data.
232
positionWindow();
233         return window[(int) (fp++ % window.length)];
234     }
235
236     public int read(final byte[] buf, final int off, final int len)
237     throws IOException JavaDoc {
238         // Check parameters.
239
if (buf == null)
240             throw new NullPointerException JavaDoc("buf");
241         final int offPlusLen = off + len;
242         if ((off | len | offPlusLen | buf.length - offPlusLen) < 0)
243         throw new IndexOutOfBoundsException JavaDoc();
244         if (len == 0)
245             return 0; // be fault-tolerant and compatible to RandomAccessFile
246

247         // Check state.
248
ensureOpen();
249         final long length = length();
250         if (fp >= length)
251             return -1;
252
253         // Setup.
254
final int windowLen = window.length;
255         int read = 0; // amount of decrypted data copied to buf
256

257         {
258             // Partial read of window data at the start.
259
final int o = (int) (fp % windowLen);
260             if (o != 0) {
261                 // The file pointer is not on a window boundary.
262
positionWindow();
263                 read = (int) min(len, windowLen - o);
264                 read = (int) min(read, length - fp);
265                 System.arraycopy(window, o, buf, off, read);
266                 fp += read;
267             }
268         }
269
270         {
271             // Full read of window data in the center.
272
while (read + windowLen < len && fp + windowLen <= length) {
273                 // The file pointer is starting and ending on window boundaries.
274
positionWindow();
275                 System.arraycopy(window, 0, buf, off + read, windowLen);
276                 read += windowLen;
277                 fp += windowLen;
278             }
279         }
280         
281         // Partial read of window data at the end.
282
if (read < len && fp < length) {
283             // The file pointer is not on a window boundary.
284
positionWindow();
285             final int n = (int) min(len - read, length - fp);
286             System.arraycopy(window, 0, buf, off + read, n);
287             read += n;
288             fp += n;
289         }
290         
291         // Assert that at least one byte has been read if len isn't zero.
292
// Note that EOF has been tested before.
293
assert read > 0;
294         return read;
295     }
296
297     public int skipBytes(int n) throws IOException JavaDoc {
298         if (n <= 0)
299             return 0; // for compatibility to RandomAccessFile in case of closed
300

301         // Check state.
302
ensureOpen();
303
304         if (fp >= length())
305             return 0;
306         final long remaining = length() - fp;
307         if (n > remaining)
308             n = (int) remaining;
309         fp += n;
310
311         return n;
312     }
313
314     /** Throws an IOException if this read only file has been closed. */
315     private final void ensureOpen() throws IOException JavaDoc {
316         if (closed)
317             throw new IOException JavaDoc("RaesReadOnlyFile has been closed!");
318     }
319
320     /**
321      * Closes this read only file.
322      * As a side effect, this will set the reference to the underlying read
323      * only file ({@link #rof} to <tt>null</tt>.
324      */

325     public void close() throws IOException JavaDoc {
326         // Order is important here!
327
if (!closed) {
328             closed = true;
329             rof.close();
330         }
331     }
332
333     //
334
// Buffer window operations.
335
//
336

337     /**
338      * Ensures that the window is positioned so that the block containing
339      * the current virtual file pointer in the encrypted file is entirely
340      * contained in it.
341      *
342      * @throws IOException On any I/O related issue.
343      * The window is invalidated in this case.
344      */

345     private void positionWindow() throws IOException JavaDoc {
346         // Check window position.
347
final long fp = this.fp;
348         final int windowLen = window.length;
349         final long nextWindowOff = windowOff + windowLen;
350         if (windowOff <= fp && fp < nextWindowOff)
351             return;
352     
353         try {
354             // Move window in the encrypted file.
355
windowOff = (fp / windowLen) * windowLen; // round down to multiple of window size
356
if (windowOff != nextWindowOff)
357                 rof.seek(windowOff);
358
359             // Fill window until end of file or buffer.
360
// This should normally complete in one loop cycle, but we do not
361
// depend on this as it would be a violation of ReadOnlyFile's
362
// contract.
363
int n = 0;
364             do {
365                 int read = rof.read(window, n, windowLen - n);
366                 if (read < 0)
367                     break;
368                 n += read;
369             } while (n < windowLen);
370         } catch (IOException JavaDoc ioe) {
371             windowOff = -windowLen - 1; // force seek() at next positionWindow()
372
throw ioe;
373         }
374     }
375
376     /**
377      * Forces the window to be reloaded on the next call to
378      * {@link #positionWindow()}.
379      */

380     private final void invalidateWindow() {
381         windowOff = Long.MIN_VALUE;
382     }
383 }
384
Popular Tags