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+
26+package javax.imageio.stream;
27+
28+import java.io.File;
29+import java.io.InputStream;
30+import java.io.IOException;
31+import java.io.RandomAccessFile;
32+import com.sun.imageio.stream.StreamCloser;
33+import com.sun.imageio.stream.StreamFinalizer;
34+import sun.java2d.Disposer;
35+import 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+ */
43+public 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+}