001 /*
002 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.imageio.stream;
027
028 import java.io.InputStream;
029 import java.io.IOException;
030 import java.io.RandomAccessFile;
031 import java.util.ArrayList;
032 import com.sun.imageio.stream.StreamFinalizer;
033 import sun.java2d.Disposer;
034 import sun.java2d.DisposerRecord;
035
036 /**
037 * An implementation of <code>ImageInputStream</code> that gets its
038 * input from a regular <code>InputStream</code>. A memory buffer is
039 * used to cache at least the data between the discard position and
040 * the current read position.
041 *
042 * <p> In general, it is preferable to use a
043 * <code>FileCacheImageInputStream</code> when reading from a regular
044 * <code>InputStream</code>. This class is provided for cases where
045 * it is not possible to create a writable temporary file.
046 *
047 * @version 0.5
048 */
049 public class MemoryCacheImageInputStream extends ImageInputStreamImpl {
050
051 private InputStream stream;
052
053 private MemoryCache cache = new MemoryCache();
054
055 /** The referent to be registered with the Disposer. */
056 private final Object disposerReferent;
057
058 /** The DisposerRecord that resets the underlying MemoryCache. */
059 private final DisposerRecord disposerRecord;
060
061 /**
062 * Constructs a <code>MemoryCacheImageInputStream</code> that will read
063 * from a given <code>InputStream</code>.
064 *
065 * @param stream an <code>InputStream</code> to read from.
066 *
067 * @exception IllegalArgumentException if <code>stream</code> is
068 * <code>null</code>.
069 */
070 public MemoryCacheImageInputStream(InputStream stream) {
071 if (stream == null) {
072 throw new IllegalArgumentException("stream == null!");
073 }
074 this .stream = stream;
075
076 disposerRecord = new StreamDisposerRecord(cache);
077 if (getClass() == MemoryCacheImageInputStream.class) {
078 disposerReferent = new Object();
079 Disposer.addRecord(disposerReferent, disposerRecord);
080 } else {
081 disposerReferent = new StreamFinalizer(this );
082 }
083 }
084
085 public int read() throws IOException {
086 checkClosed();
087 bitOffset = 0;
088 long pos = cache.loadFromStream(stream, streamPos + 1);
089 if (pos >= streamPos + 1) {
090 return cache.read(streamPos++);
091 } else {
092 return -1;
093 }
094 }
095
096 public int read(byte[] b, int off, int len) throws IOException {
097 checkClosed();
098
099 if (b == null) {
100 throw new NullPointerException("b == null!");
101 }
102 if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
103 throw new IndexOutOfBoundsException(
104 "off < 0 || len < 0 || off+len > b.length || off+len < 0!");
105 }
106
107 bitOffset = 0;
108
109 if (len == 0) {
110 return 0;
111 }
112
113 long pos = cache.loadFromStream(stream, streamPos + len);
114
115 len = (int) (pos - streamPos); // In case stream ended early
116
117 if (len > 0) {
118 cache.read(b, off, len, streamPos);
119 streamPos += len;
120 return len;
121 } else {
122 return -1;
123 }
124 }
125
126 public void flushBefore(long pos) throws IOException {
127 super .flushBefore(pos); // this will call checkClosed() for us
128 cache.disposeBefore(pos);
129 }
130
131 /**
132 * Returns <code>true</code> since this
133 * <code>ImageInputStream</code> caches data in order to allow
134 * seeking backwards.
135 *
136 * @return <code>true</code>.
137 *
138 * @see #isCachedMemory
139 * @see #isCachedFile
140 */
141 public boolean isCached() {
142 return true;
143 }
144
145 /**
146 * Returns <code>false</code> since this
147 * <code>ImageInputStream</code> does not maintain a file cache.
148 *
149 * @return <code>false</code>.
150 *
151 * @see #isCached
152 * @see #isCachedMemory
153 */
154 public boolean isCachedFile() {
155 return false;
156 }
157
158 /**
159 * Returns <code>true</code> since this
160 * <code>ImageInputStream</code> maintains a main memory cache.
161 *
162 * @return <code>true</code>.
163 *
164 * @see #isCached
165 * @see #isCachedFile
166 */
167 public boolean isCachedMemory() {
168 return true;
169 }
170
171 /**
172 * Closes this <code>MemoryCacheImageInputStream</code>, freeing
173 * the cache. The source <code>InputStream</code> is not closed.
174 */
175 public void close() throws IOException {
176 super .close();
177 disposerRecord.dispose(); // this resets the MemoryCache
178 stream = null;
179 cache = null;
180 }
181
182 /**
183 * {@inheritDoc}
184 */
185 protected void finalize() throws Throwable {
186 // Empty finalizer: for performance reasons we instead use the
187 // Disposer mechanism for ensuring that the underlying
188 // MemoryCache is reset prior to garbage collection
189 }
190
191 private static class StreamDisposerRecord implements DisposerRecord {
192 private MemoryCache cache;
193
194 public StreamDisposerRecord(MemoryCache cache) {
195 this .cache = cache;
196 }
197
198 public synchronized void dispose() {
199 if (cache != null) {
200 cache.reset();
201 cache = null;
202 }
203 }
204 }
205 }
|