KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > mail > util > SharedFileInputStream


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21
22 /*
23  * @(#)SharedFileInputStream.java 1.8 05/08/29
24  *
25  * Copyright 1994-2005 Sun Microsystems, Inc. All Rights Reserved.
26  */

27
28 package javax.mail.util;
29
30 import java.io.*;
31 import javax.mail.internet.SharedInputStream JavaDoc;
32
33 /**
34  * A <code>SharedFileInputStream</code> is a
35  * <code>BufferedInputStream</code> that buffers
36  * data from the file and supports the <code>mark</code>
37  * and <code>reset</code> methods. It also supports the
38  * <code>newStream</code> method that allows you to create
39  * other streams that represent subsets of the file.
40  * A <code>RandomAccessFile</code> object is used to
41  * access the file data.
42  *
43  * @author Bill Shannon
44  * @since JavaMail 1.4
45  */

46 public class SharedFileInputStream extends BufferedInputStream
47                 implements SharedInputStream JavaDoc {
48
49     private static int defaultBufferSize = 2048;
50
51     /**
52      * The file containing the data.
53      * Shared by all related SharedFileInputStreams.
54      */

55     protected RandomAccessFile in;
56
57     /**
58      * The normal size of the read buffer.
59      */

60     protected int bufsize;
61
62     /**
63      * The file offset that corresponds to the first byte in
64      * the read buffer.
65      */

66     protected long bufpos;
67
68     /**
69      * The file offset of the start of data in this subset of the file.
70      */

71     protected long start = 0;
72
73     /**
74      * The amount of data in this subset of the file.
75      */

76     protected long datalen;
77
78     /**
79      * True if this is a top level stream created directly by "new".
80      * False if this is a derived stream created by newStream.
81      */

82     private boolean master = true;
83
84     /**
85      * A shared class that keeps track of the references
86      * to a particular file so it can be closed when the
87      * last reference is gone.
88      */

89     class SharedFile {
90     private int cnt;
91     private RandomAccessFile in;
92
93     SharedFile(String JavaDoc file) throws IOException {
94         this.in = new RandomAccessFile(file, "r");
95     }
96
97     SharedFile(File file) throws IOException {
98         this.in = new RandomAccessFile(file, "r");
99     }
100
101     public RandomAccessFile open() {
102         cnt++;
103         return in;
104     }
105
106     public synchronized void close() throws IOException {
107         if (cnt > 0 && --cnt <= 0)
108         in.close();
109     }
110
111     public synchronized void forceClose() throws IOException {
112         if (cnt > 0) {
113         // normal case, close exceptions propagated
114
cnt = 0;
115         in.close();
116         } else {
117         // should already be closed, ignore exception
118
try {
119             in.close();
120         } catch (IOException ioex) { }
121         }
122     }
123
124     protected void finalize() throws Throwable JavaDoc {
125         super.finalize();
126         in.close();
127     }
128     }
129
130     private SharedFile sf;
131
132     /**
133      * Check to make sure that this stream has not been closed
134      */

135     private void ensureOpen() throws IOException {
136     if (in == null)
137         throw new IOException("Stream closed");
138     }
139
140     /**
141      * Creates a <code>SharedFileInputStream</code>
142      * for the file.
143      *
144      * @param file the file
145      */

146     public SharedFileInputStream(File file) throws IOException {
147     this(file, defaultBufferSize);
148     }
149
150     /**
151      * Creates a <code>SharedFileInputStream</code>
152      * for the named file
153      *
154      * @param file the file
155      */

156     public SharedFileInputStream(String JavaDoc file) throws IOException {
157     this(file, defaultBufferSize);
158     }
159
160     /**
161      * Creates a <code>SharedFileInputStream</code>
162      * with the specified buffer size.
163      *
164      * @param file the file
165      * @param size the buffer size.
166      * @exception IllegalArgumentException if size <= 0.
167      */

168     public SharedFileInputStream(File file, int size) throws IOException {
169     super(null); // XXX - will it NPE?
170
if (size <= 0)
171             throw new IllegalArgumentException JavaDoc("Buffer size <= 0");
172     init(new SharedFile(file), size);
173     }
174
175     /**
176      * Creates a <code>SharedFileInputStream</code>
177      * with the specified buffer size.
178      *
179      * @param file the file
180      * @param size the buffer size.
181      * @exception IllegalArgumentException if size <= 0.
182      */

183     public SharedFileInputStream(String JavaDoc file, int size) throws IOException {
184     super(null); // XXX - will it NPE?
185
if (size <= 0)
186             throw new IllegalArgumentException JavaDoc("Buffer size <= 0");
187     init(new SharedFile(file), size);
188     }
189
190     private void init(SharedFile sf, int size) throws IOException {
191     this.sf = sf;
192     this.in = sf.open();
193     this.start = 0;
194     this.datalen = in.length(); // XXX - file can't grow
195
this.bufsize = size;
196     buf = new byte[size];
197     }
198
199     /**
200      * Used internally by the <code>newStream</code> method.
201      */

202     private SharedFileInputStream(SharedFile sf, long start, long len,
203                 int bufsize) {
204     super(null);
205     this.master = false;
206     this.sf = sf;
207     this.in = sf.open();
208     this.start = start;
209     this.bufpos = start;
210     this.datalen = len;
211     this.bufsize = bufsize;
212     buf = new byte[bufsize];
213     }
214
215     /**
216      * Fills the buffer with more data, taking into account
217      * shuffling and other tricks for dealing with marks.
218      * Assumes that it is being called by a synchronized method.
219      * This method also assumes that all data has already been read in,
220      * hence pos > count.
221      */

222     private void fill() throws IOException {
223     if (markpos < 0) {
224         pos = 0; /* no mark: throw away the buffer */
225         bufpos += count;
226     } else if (pos >= buf.length) /* no room left in buffer */
227         if (markpos > 0) { /* can throw away early part of the buffer */
228         int sz = pos - markpos;
229         System.arraycopy(buf, markpos, buf, 0, sz);
230         pos = sz;
231         bufpos += markpos;
232         markpos = 0;
233         } else if (buf.length >= marklimit) {
234         markpos = -1; /* buffer got too big, invalidate mark */
235         pos = 0; /* drop buffer contents */
236         bufpos += count;
237         } else { /* grow buffer */
238         int nsz = pos * 2;
239         if (nsz > marklimit)
240             nsz = marklimit;
241         byte nbuf[] = new byte[nsz];
242         System.arraycopy(buf, 0, nbuf, 0, pos);
243         buf = nbuf;
244         }
245         count = pos;
246     in.seek(bufpos + pos);
247     // limit to datalen
248
int len = buf.length - pos;
249     if (bufpos - start + pos + len > datalen)
250         len = (int)(datalen - (bufpos - start + pos));
251     int n = in.read(buf, pos, len);
252         if (n > 0)
253             count = n + pos;
254     }
255
256     /**
257      * See the general contract of the <code>read</code>
258      * method of <code>InputStream</code>.
259      *
260      * @return the next byte of data, or <code>-1</code> if the end of the
261      * stream is reached.
262      * @exception IOException if an I/O error occurs.
263      */

264     public synchronized int read() throws IOException {
265         ensureOpen();
266     if (pos >= count) {
267         fill();
268         if (pos >= count)
269         return -1;
270     }
271     return buf[pos++] & 0xff;
272     }
273
274     /**
275      * Read characters into a portion of an array, reading from the underlying
276      * stream at most once if necessary.
277      */

278     private int read1(byte[] b, int off, int len) throws IOException {
279     int avail = count - pos;
280     if (avail <= 0) {
281         if (false) {
282         /* If the requested length is at least as large as the buffer, and
283            if there is no mark/reset activity, do not bother to copy the
284            bytes into the local buffer. In this way buffered streams will
285            cascade harmlessly. */

286         if (len >= buf.length && markpos < 0) {
287         // XXX - seek, update bufpos - how?
288
return in.read(b, off, len);
289         }
290         }
291         fill();
292         avail = count - pos;
293         if (avail <= 0) return -1;
294     }
295     int cnt = (avail < len) ? avail : len;
296     System.arraycopy(buf, pos, b, off, cnt);
297     pos += cnt;
298     return cnt;
299     }
300
301     /**
302      * Reads bytes from this stream into the specified byte array,
303      * starting at the given offset.
304      *
305      * <p> This method implements the general contract of the corresponding
306      * <code>{@link java.io.InputStream#read(byte[], int, int) read}</code>
307      * method of the <code>{@link java.io.InputStream}</code> class.
308      *
309      * @param b destination buffer.
310      * @param off offset at which to start storing bytes.
311      * @param len maximum number of bytes to read.
312      * @return the number of bytes read, or <code>-1</code> if the end of
313      * the stream has been reached.
314      * @exception IOException if an I/O error occurs.
315      */

316     public synchronized int read(byte b[], int off, int len)
317     throws IOException
318     {
319         ensureOpen();
320         if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
321         throw new IndexOutOfBoundsException JavaDoc();
322     } else if (len == 0) {
323         return 0;
324     }
325
326     int n = read1(b, off, len);
327     if (n <= 0) return n;
328     while ((n < len) /* && (in.available() > 0) */) {
329         int n1 = read1(b, off + n, len - n);
330         if (n1 <= 0) break;
331         n += n1;
332     }
333     return n;
334     }
335
336     /**
337      * See the general contract of the <code>skip</code>
338      * method of <code>InputStream</code>.
339      *
340      * @param n the number of bytes to be skipped.
341      * @return the actual number of bytes skipped.
342      * @exception IOException if an I/O error occurs.
343      */

344     public synchronized long skip(long n) throws IOException {
345         ensureOpen();
346     if (n <= 0) {
347         return 0;
348     }
349     long avail = count - pos;
350      
351         if (avail <= 0) {
352             // If no mark position set then don't keep in buffer
353
/*
354             if (markpos <0)
355                 return in.skip(n);
356         */

357             
358             // Fill in buffer to save bytes for reset
359
fill();
360             avail = count - pos;
361             if (avail <= 0)
362                 return 0;
363         }
364         
365         long skipped = (avail < n) ? avail : n;
366         pos += skipped;
367         return skipped;
368     }
369
370     /**
371      * Returns the number of bytes that can be read from this input
372      * stream without blocking.
373      *
374      * @return the number of bytes that can be read from this input
375      * stream without blocking.
376      * @exception IOException if an I/O error occurs.
377      */

378     public synchronized int available() throws IOException {
379         ensureOpen();
380     return (count - pos) + in_available();
381     }
382
383     private int in_available() throws IOException {
384     // XXX - overflow
385
return (int)((start + datalen) - (bufpos + count));
386     }
387
388     /**
389      * See the general contract of the <code>mark</code>
390      * method of <code>InputStream</code>.
391      *
392      * @param readlimit the maximum limit of bytes that can be read before
393      * the mark position becomes invalid.
394      * @see #reset()
395      */

396     public synchronized void mark(int readlimit) {
397     marklimit = readlimit;
398     markpos = pos;
399     }
400
401     /**
402      * See the general contract of the <code>reset</code>
403      * method of <code>InputStream</code>.
404      * <p>
405      * If <code>markpos</code> is <code>-1</code>
406      * (no mark has been set or the mark has been
407      * invalidated), an <code>IOException</code>
408      * is thrown. Otherwise, <code>pos</code> is
409      * set equal to <code>markpos</code>.
410      *
411      * @exception IOException if this stream has not been marked or
412      * if the mark has been invalidated.
413      * @see #mark(int)
414      */

415     public synchronized void reset() throws IOException {
416         ensureOpen();
417     if (markpos < 0)
418         throw new IOException("Resetting to invalid mark");
419     pos = markpos;
420     }
421
422     /**
423      * Tests if this input stream supports the <code>mark</code>
424      * and <code>reset</code> methods. The <code>markSupported</code>
425      * method of <code>SharedFileInputStream</code> returns
426      * <code>true</code>.
427      *
428      * @return a <code>boolean</code> indicating if this stream type supports
429      * the <code>mark</code> and <code>reset</code> methods.
430      * @see java.io.InputStream#mark(int)
431      * @see java.io.InputStream#reset()
432      */

433     public boolean markSupported() {
434     return true;
435     }
436
437     /**
438      * Closes this input stream and releases any system resources
439      * associated with the stream.
440      *
441      * @exception IOException if an I/O error occurs.
442      */

443     public void close() throws IOException {
444         if (in == null)
445             return;
446     try {
447         if (master)
448         sf.forceClose();
449         else
450         sf.close();
451     } finally {
452         sf = null;
453         in = null;
454         buf = null;
455     }
456     }
457
458     /**
459      * Return the current position in the InputStream, as an
460      * offset from the beginning of the InputStream.
461      *
462      * @return the current position
463      */

464     public long getPosition() {
465 //System.out.println("getPosition: start " + start + " pos " + pos + " bufpos " + bufpos + " = " + (bufpos + pos - start));
466
if (in == null)
467         throw new RuntimeException JavaDoc("Stream closed");
468     return bufpos + pos - start;
469     }
470
471     /**
472      * Return a new InputStream representing a subset of the data
473      * from this InputStream, starting at <code>start</code> (inclusive)
474      * up to <code>end</code> (exclusive). <code>start</code> must be
475      * non-negative. If <code>end</code> is -1, the new stream ends
476      * at the same place as this stream. The returned InputStream
477      * will also implement the SharedInputStream interface.
478      *
479      * @param start the starting position
480      * @param end the ending position + 1
481      * @return the new stream
482      */

483     public InputStream JavaDoc newStream(long start, long end) {
484     if (in == null)
485         throw new RuntimeException JavaDoc("Stream closed");
486     if (start < 0)
487         throw new IllegalArgumentException JavaDoc("start < 0");
488     if (end == -1)
489         end = datalen;
490     return new SharedFileInputStream(sf,
491             this.start + (int)start, (int)(end - start), bufsize);
492     }
493
494     // for testing...
495
/*
496     public static void main(String[] argv) throws Exception {
497     SharedFileInputStream is = new SharedFileInputStream(argv[0]);
498     java.util.Random r = new java.util.Random();
499     int b;
500     while ((b = is.read()) >= 0) {
501         System.out.write(b);
502         if (r.nextDouble() < 0.3) {
503         InputStream is2 = is.newStream(is.getPosition(), -1);
504         int b2;
505         while ((b2 = is2.read()) >= 0)
506             ;
507         }
508     }
509     }
510     */

511
512     /**
513      * Force this stream to close.
514      */

515     protected void finalize() throws Throwable JavaDoc {
516     super.finalize();
517     close();
518     }
519 }
520
Popular Tags