KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > codec > FileCacheSeekableStream


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

18 package org.apache.batik.ext.awt.image.codec;
19
20 import java.io.File JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.io.RandomAccessFile JavaDoc;
24
25 /**
26  * A subclass of <code>SeekableStream</code> that may be used to wrap
27  * a regular <code>InputStream</code>. Seeking backwards is supported
28  * by means of a file cache. In circumstances that do not allow the
29  * creation of a temporary file (for example, due to security
30  * consideration or the absence of local disk), the
31  * <code>MemoryCacheSeekableStream</code> class may be used instead.
32  *
33  * <p> The <code>mark()</code> and <code>reset()</code> methods are
34  * supported.
35  *
36  * <p><b> This class is not a committed part of the JAI API. It may
37  * be removed or changed in future releases of JAI.</b>
38  */

39 public final class FileCacheSeekableStream extends SeekableStream {
40
41     /** The source stream. */
42     private InputStream JavaDoc stream;
43
44     /** The cache File. */
45     private File JavaDoc cacheFile;
46
47     /** The cache as a RandomAcessFile. */
48     private RandomAccessFile JavaDoc cache;
49
50     /** The length of the read buffer. */
51     private int bufLen = 1024;
52
53     /** The read buffer. */
54     private byte[] buf = new byte[bufLen];
55
56     /** Number of bytes in the cache. */
57     private long length = 0;
58
59     /** Next byte to be read. */
60     private long pointer = 0;
61
62     /** True if we've encountered the end of the source stream. */
63     private boolean foundEOF = false;
64
65     /**
66      * Constructs a <code>MemoryCacheSeekableStream</code> that takes
67      * its source data from a regular <code>InputStream</code>.
68      * Seeking backwards is supported by means of an file cache.
69      *
70      * <p> An <code>IOException</code> will be thrown if the
71      * attempt to create the cache file fails for any reason.
72      */

73     public FileCacheSeekableStream(InputStream JavaDoc stream)
74         throws IOException JavaDoc {
75         this.stream = stream;
76         this.cacheFile = File.createTempFile("jai-FCSS-", ".tmp");
77         cacheFile.deleteOnExit();
78         this.cache = new RandomAccessFile JavaDoc(cacheFile, "rw");
79     }
80
81     /**
82      * Ensures that at least <code>pos</code> bytes are cached,
83      * or the end of the source is reached. The return value
84      * is equal to the smaller of <code>pos</code> and the
85      * length of the source file.
86      */

87     private long readUntil(long pos) throws IOException JavaDoc {
88         // We've already got enough data cached
89
if (pos < length) {
90             return pos;
91         }
92         // pos >= length but length isn't getting any bigger, so return it
93
if (foundEOF) {
94             return length;
95         }
96
97         long len = pos - length;
98         cache.seek(length);
99         while (len > 0) {
100             // Copy a buffer's worth of data from the source to the cache
101
// bufLen will always fit into an int so this is safe
102
int nbytes = stream.read(buf, 0, (int)Math.min(len, bufLen));
103             if (nbytes == -1) {
104                 foundEOF = true;
105                 return length;
106             }
107
108             cache.setLength(cache.length() + nbytes);
109             cache.write(buf, 0, nbytes);
110             len -= nbytes;
111             length += nbytes;
112         }
113
114         return pos;
115     }
116
117     /**
118      * Returns <code>true</code> since all
119      * <code>FileCacheSeekableStream</code> instances support seeking
120      * backwards.
121      */

122     public boolean canSeekBackwards() {
123         return true;
124     }
125
126     /**
127      * Returns the current offset in this file.
128      *
129      * @return the offset from the beginning of the file, in bytes,
130      * at which the next read occurs.
131      */

132     public long getFilePointer() {
133         return pointer;
134     }
135
136     /**
137      * Sets the file-pointer offset, measured from the beginning of this
138      * file, at which the next read occurs.
139      *
140      * @param pos the offset position, measured in bytes from the
141      * beginning of the file, at which to set the file
142      * pointer.
143      * @exception IOException if <code>pos</code> is less than
144      * <code>0</code> or if an I/O error occurs.
145      */

146     public void seek(long pos) throws IOException JavaDoc {
147         if (pos < 0) {
148             throw new IOException JavaDoc(PropertyUtil.getString("FileCacheSeekableStream0"));
149         }
150         pointer = pos;
151     }
152
153     /**
154      * Reads the next byte of data from the input stream. The value byte is
155      * returned as an <code>int</code> in the range <code>0</code> to
156      * <code>255</code>. If no byte is available because the end of the stream
157      * has been reached, the value <code>-1</code> is returned. This method
158      * blocks until input data is available, the end of the stream is detected,
159      * or an exception is thrown.
160      *
161      * @return the next byte of data, or <code>-1</code> if the end of the
162      * stream is reached.
163      * @exception IOException if an I/O error occurs.
164      */

165     public int read() throws IOException JavaDoc {
166         long next = pointer + 1;
167         long pos = readUntil(next);
168         if (pos >= next) {
169             cache.seek(pointer++);
170             return cache.read();
171         } else {
172             return -1;
173         }
174     }
175
176     /**
177      * Reads up to <code>len</code> bytes of data from the input stream into
178      * an array of bytes. An attempt is made to read as many as
179      * <code>len</code> bytes, but a smaller number may be read, possibly
180      * zero. The number of bytes actually read is returned as an integer.
181      *
182      * <p> This method blocks until input data is available, end of file is
183      * detected, or an exception is thrown.
184      *
185      * <p> If <code>b</code> is <code>null</code>, a
186      * <code>NullPointerException</code> is thrown.
187      *
188      * <p> If <code>off</code> is negative, or <code>len</code> is negative, or
189      * <code>off+len</code> is greater than the length of the array
190      * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
191      * thrown.
192      *
193      * <p> If <code>len</code> is zero, then no bytes are read and
194      * <code>0</code> is returned; otherwise, there is an attempt to read at
195      * least one byte. If no byte is available because the stream is at end of
196      * file, the value <code>-1</code> is returned; otherwise, at least one
197      * byte is read and stored into <code>b</code>.
198      *
199      * <p> The first byte read is stored into element <code>b[off]</code>, the
200      * next one into <code>b[off+1]</code>, and so on. The number of bytes read
201      * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
202      * bytes actually read; these bytes will be stored in elements
203      * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
204      * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
205      * <code>b[off+len-1]</code> unaffected.
206      *
207      * <p> In every case, elements <code>b[0]</code> through
208      * <code>b[off]</code> and elements <code>b[off+len]</code> through
209      * <code>b[b.length-1]</code> are unaffected.
210      *
211      * <p> If the first byte cannot be read for any reason other than end of
212      * file, then an <code>IOException</code> is thrown. In particular, an
213      * <code>IOException</code> is thrown if the input stream has been closed.
214      *
215      * @param b the buffer into which the data is read.
216      * @param off the start offset in array <code>b</code>
217      * at which the data is written.
218      * @param len the maximum number of bytes to read.
219      * @return the total number of bytes read into the buffer, or
220      * <code>-1</code> if there is no more data because the end of
221      * the stream has been reached.
222      * @exception IOException if an I/O error occurs.
223      */

224     public int read(byte[] b, int off, int len) throws IOException JavaDoc {
225         if (b == null) {
226             throw new NullPointerException JavaDoc();
227         }
228         if ((off < 0) || (len < 0) || (off + len > b.length)) {
229             throw new IndexOutOfBoundsException JavaDoc();
230         }
231         if (len == 0) {
232             return 0;
233         }
234
235         long pos = readUntil(pointer + len);
236
237         // len will always fit into an int so this is safe
238
len = (int)Math.min(len, pos - pointer);
239         if (len > 0) {
240             cache.seek(pointer);
241             cache.readFully(b, off, len);
242             pointer += len;
243             return len;
244         } else {
245             return -1;
246         }
247     }
248     
249     /**
250      * Closes this stream and releases any system resources
251      * associated with the stream.
252      *
253      * @throws IOException if an I/O error occurs.
254      */

255     public void close() throws IOException JavaDoc {
256         super.close();
257         cache.close();
258         cacheFile.delete();
259     }
260 }
261
Popular Tags