History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/imageio/stream/FileCacheImageInputStream.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/imageio/stream/FileCacheImageInputStream.java+290−0
@@ -0,0 +1,290 @@
1/*
2 * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.imageio.stream;
27
28import java.io.File;
29import java.io.InputStream;
30import java.io.IOException;
31import java.io.RandomAccessFile;
32import com.sun.imageio.stream.StreamCloser;
33import com.sun.imageio.stream.StreamFinalizer;
34import sun.java2d.Disposer;
35import sun.java2d.DisposerRecord;
36
37/**
38 * An implementation of <code>ImageInputStream</code> that gets its
39 * input from a regular <code>InputStream</code>. A file is used to
40 * cache previously read data.
41 *
42 */
43public class FileCacheImageInputStream extends ImageInputStreamImpl {
44
45 private InputStream stream;
46
47 private File cacheFile;
48
49 private RandomAccessFile cache;
50
51 private static final int BUFFER_LENGTH = 1024;
52
53 private byte[] buf = new byte[BUFFER_LENGTH];
54
55 private long length = 0L;
56
57 private boolean foundEOF = false;
58
59 /** The referent to be registered with the Disposer. */
60 private final Object disposerReferent;
61
62 /** The DisposerRecord that closes the underlying cache. */
63 private final DisposerRecord disposerRecord;
64
65 /** The CloseAction that closes the stream in
66 * the StreamCloser's shutdown hook */
67 private final StreamCloser.CloseAction closeAction;
68
69 /**
70 * Constructs a <code>FileCacheImageInputStream</code> that will read
71 * from a given <code>InputStream</code>.
72 *
73 * <p> A temporary file is used as a cache. If
74 * <code>cacheDir</code>is non-<code>null</code> and is a
75 * directory, the file will be created there. If it is
76 * <code>null</code>, the system-dependent default temporary-file
77 * directory will be used (see the documentation for
78 * <code>File.createTempFile</code> for details).
79 *
80 * @param stream an <code>InputStream</code> to read from.
81 * @param cacheDir a <code>File</code> indicating where the
82 * cache file should be created, or <code>null</code> to use the
83 * system directory.
84 *
85 * @exception IllegalArgumentException if <code>stream</code> is
86 * <code>null</code>.
87 * @exception IllegalArgumentException if <code>cacheDir</code> is
88 * non-<code>null</code> but is not a directory.
89 * @exception IOException if a cache file cannot be created.
90 */
91 public FileCacheImageInputStream(InputStream stream, File cacheDir)
92 throws IOException {
93 if (stream == null) {
94 throw new IllegalArgumentException("stream == null!");
95 }
96 if ((cacheDir != null) && !(cacheDir.isDirectory())) {
97 throw new IllegalArgumentException("Not a directory!");
98 }
99 this.stream = stream;
100 this.cacheFile =
101 File.createTempFile("imageio", ".tmp", cacheDir);
102 this.cache = new RandomAccessFile(cacheFile, "rw");
103
104 this.closeAction = StreamCloser.createCloseAction(this);
105 StreamCloser.addToQueue(closeAction);
106
107 disposerRecord = new StreamDisposerRecord(cacheFile, cache);
108 if (getClass() == FileCacheImageInputStream.class) {
109 disposerReferent = new Object();
110 Disposer.addRecord(disposerReferent, disposerRecord);
111 } else {
112 disposerReferent = new StreamFinalizer(this);
113 }
114 }
115
116 /**
117 * Ensures that at least <code>pos</code> bytes are cached,
118 * or the end of the source is reached. The return value
119 * is equal to the smaller of <code>pos</code> and the
120 * length of the source file.
121 */
122 private long readUntil(long pos) throws IOException {
123 // We've already got enough data cached
124 if (pos < length) {
125 return pos;
126 }
127 // pos >= length but length isn't getting any bigger, so return it
128 if (foundEOF) {
129 return length;
130 }
131
132 long len = pos - length;
133 cache.seek(length);
134 while (len > 0) {
135 // Copy a buffer's worth of data from the source to the cache
136 // BUFFER_LENGTH will always fit into an int so this is safe
137 int nbytes =
138 stream.read(buf, 0, (int)Math.min(len, (long)BUFFER_LENGTH));
139 if (nbytes == -1) {
140 foundEOF = true;
141 return length;
142 }
143
144 cache.write(buf, 0, nbytes);
145 len -= nbytes;
146 length += nbytes;
147 }
148
149 return pos;
150 }
151
152 public int read() throws IOException {
153 checkClosed();
154 bitOffset = 0;
155 long next = streamPos + 1;
156 long pos = readUntil(next);
157 if (pos >= next) {
158 cache.seek(streamPos++);
159 return cache.read();
160 } else {
161 return -1;
162 }
163 }
164
165 public int read(byte[] b, int off, int len) throws IOException {
166 checkClosed();
167
168 if (b == null) {
169 throw new NullPointerException("b == null!");
170 }
171 // Fix 4430357 - if off + len < 0, overflow occurred
172 if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
173 throw new IndexOutOfBoundsException
174 ("off < 0 || len < 0 || off+len > b.length || off+len < 0!");
175 }
176
177 bitOffset = 0;
178
179 if (len == 0) {
180 return 0;
181 }
182
183 long pos = readUntil(streamPos + len);
184
185 // len will always fit into an int so this is safe
186 len = (int)Math.min((long)len, pos - streamPos);
187 if (len > 0) {
188 cache.seek(streamPos);
189 cache.readFully(b, off, len);
190 streamPos += len;
191 return len;
192 } else {
193 return -1;
194 }
195 }
196
197 /**
198 * Returns <code>true</code> since this
199 * <code>ImageInputStream</code> caches data in order to allow
200 * seeking backwards.
201 *
202 * @return <code>true</code>.
203 *
204 * @see #isCachedMemory
205 * @see #isCachedFile
206 */
207 public boolean isCached() {
208 return true;
209 }
210
211 /**
212 * Returns <code>true</code> since this
213 * <code>ImageInputStream</code> maintains a file cache.
214 *
215 * @return <code>true</code>.
216 *
217 * @see #isCached
218 * @see #isCachedMemory
219 */
220 public boolean isCachedFile() {
221 return true;
222 }
223
224 /**
225 * Returns <code>false</code> since this
226 * <code>ImageInputStream</code> does not maintain a main memory
227 * cache.
228 *
229 * @return <code>false</code>.
230 *
231 * @see #isCached
232 * @see #isCachedFile
233 */
234 public boolean isCachedMemory() {
235 return false;
236 }
237
238 /**
239 * Closes this <code>FileCacheImageInputStream</code>, closing
240 * and removing the cache file. The source <code>InputStream</code>
241 * is not closed.
242 *
243 * @exception IOException if an error occurs.
244 */
245 public void close() throws IOException {
246 super.close();
247 disposerRecord.dispose(); // this will close/delete the cache file
248 stream = null;
249 cache = null;
250 cacheFile = null;
251 StreamCloser.removeFromQueue(closeAction);
252 }
253
254 /**
255 * {@inheritDoc}
256 */
257 protected void finalize() throws Throwable {
258 // Empty finalizer: for performance reasons we instead use the
259 // Disposer mechanism for ensuring that the underlying
260 // RandomAccessFile is closed/deleted prior to garbage collection
261 }
262
263 private static class StreamDisposerRecord implements DisposerRecord {
264 private File cacheFile;
265 private RandomAccessFile cache;
266
267 public StreamDisposerRecord(File cacheFile, RandomAccessFile cache) {
268 this.cacheFile = cacheFile;
269 this.cache = cache;
270 }
271
272 public synchronized void dispose() {
273 if (cache != null) {
274 try {
275 cache.close();
276 } catch (IOException e) {
277 } finally {
278 cache = null;
279 }
280 }
281 if (cacheFile != null) {
282 cacheFile.delete();
283 cacheFile = null;
284 }
285 // Note: Explicit removal of the stream from the StreamCloser
286 // queue is not mandatory in this case, as it will be removed
287 // automatically by GC shortly after this method is called.
288 }
289 }
290}