KickJava   Java API By Example, From Geeks To Geeks.

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


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.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.util.Vector JavaDoc;
23
24 /**
25  * A subclass of <code>SeekableStream</code> that may be used to wrap
26  * a regular <code>InputStream</code>. Seeking backwards is supported
27  * by means of an in-memory cache. For greater efficiency,
28  * <code>FileCacheSeekableStream</code> should be used in
29  * circumstances that allow the creation of a temporary file.
30  *
31  * <p> The <code>mark()</code> and <code>reset()</code> methods are
32  * supported.
33  *
34  * <p><b> This class is not a committed part of the JAI API. It may
35  * be removed or changed in future releases of JAI.</b>
36  */

37 public final class MemoryCacheSeekableStream extends SeekableStream {
38
39     /** The source input stream. */
40     private InputStream JavaDoc src;
41
42     /** Position of first unread byte. */
43     private long pointer = 0;
44
45     /** Log_2 of the sector size. */
46     private static final int SECTOR_SHIFT = 9;
47
48     /** The sector size. */
49     private static final int SECTOR_SIZE = 1 << SECTOR_SHIFT;
50
51     /** A mask to determine the offset within a sector. */
52     private static final int SECTOR_MASK = SECTOR_SIZE - 1;
53
54     /** A Vector of source sectors. */
55     private Vector JavaDoc data = new Vector JavaDoc();
56
57     /** Number of sectors stored. */
58     int sectors = 0;
59
60     /** Number of bytes read. */
61     int length = 0;
62
63     /** True if we've previously reached the end of the source stream */
64     boolean foundEOS = false;
65
66     /**
67      * Constructs a <code>MemoryCacheSeekableStream</code> that takes
68      * its source data from a regular <code>InputStream</code>.
69      * Seeking backwards is supported by means of an in-memory cache.
70      */

71     public MemoryCacheSeekableStream(InputStream JavaDoc src) {
72         this.src = src;
73     }
74
75     /**
76      * Ensures that at least <code>pos</code> bytes are cached,
77      * or the end of the source is reached. The return value
78      * is equal to the smaller of <code>pos</code> and the
79      * length of the source stream.
80      */

81     private long readUntil(long pos) throws IOException JavaDoc {
82         // We've already got enough data cached
83
if (pos < length) {
84             return pos;
85         }
86         // pos >= length but length isn't getting any bigger, so return it
87
if (foundEOS) {
88             return length;
89         }
90
91         int sector = (int)(pos >> SECTOR_SHIFT);
92
93         // First unread sector
94
int startSector = length >> SECTOR_SHIFT;
95
96         // Read sectors until the desired sector
97
for (int i = startSector; i <= sector; i++) {
98             byte[] buf = new byte[SECTOR_SIZE];
99             data.addElement(buf);
100             
101             // Read up to SECTOR_SIZE bytes
102
int len = SECTOR_SIZE;
103             int off = 0;
104             while (len > 0) {
105                 int nbytes = src.read(buf, off, len);
106                 // Found the end-of-stream
107
if (nbytes == -1) {
108                     foundEOS = true;
109                     return length;
110                 }
111                 off += nbytes;
112                 len -= nbytes;
113                 
114                 // Record new data length
115
length += nbytes;
116             }
117         }
118
119         return length;
120     }
121
122     /**
123      * Returns <code>true</code> since all
124      * <code>MemoryCacheSeekableStream</code> instances support seeking
125      * backwards.
126      */

127     public boolean canSeekBackwards() {
128         return true;
129     }
130
131     /**
132      * Returns the current offset in this file.
133      *
134      * @return the offset from the beginning of the file, in bytes,
135      * at which the next read occurs.
136      */

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

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

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

228     public int read(byte[] b, int off, int len) throws IOException JavaDoc {
229         if (b == null) {
230             throw new NullPointerException JavaDoc();
231         }
232         if ((off < 0) || (len < 0) || (off + len > b.length)) {
233             throw new IndexOutOfBoundsException JavaDoc();
234         }
235         if (len == 0) {
236             return 0;
237         }
238
239         long pos = readUntil(pointer + len);
240         // End-of-stream
241
if (pos <= pointer) {
242             return -1;
243         }
244
245         byte[] buf = (byte[])data.elementAt((int)(pointer >> SECTOR_SHIFT));
246         int nbytes = Math.min(len, SECTOR_SIZE - (int)(pointer & SECTOR_MASK));
247         System.arraycopy(buf, (int)(pointer & SECTOR_MASK),
248                          b, off, nbytes);
249         pointer += nbytes;
250         return nbytes;
251     }
252 }
253
Popular Tags