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.IOException;
029 import java.io.OutputStream;
030
031 /**
032 * An implementation of <code>ImageOutputStream</code> that writes its
033 * output to a regular <code>OutputStream</code>. A memory buffer is
034 * used to cache at least the data between the discard position and
035 * the current write position. The only constructor takes an
036 * <code>OutputStream</code>, so this class may not be used for
037 * read/modify/write operations. Reading can occur only on parts of
038 * the stream that have already been written to the cache and not
039 * yet flushed.
040 *
041 * @version 0.5
042 */
043 public class MemoryCacheImageOutputStream extends ImageOutputStreamImpl {
044
045 private OutputStream stream;
046
047 private MemoryCache cache = new MemoryCache();
048
049 /**
050 * Constructs a <code>MemoryCacheImageOutputStream</code> that will write
051 * to a given <code>OutputStream</code>.
052 *
053 * @param stream an <code>OutputStream</code> to write to.
054 *
055 * @exception IllegalArgumentException if <code>stream</code> is
056 * <code>null</code>.
057 */
058 public MemoryCacheImageOutputStream(OutputStream stream) {
059 if (stream == null) {
060 throw new IllegalArgumentException("stream == null!");
061 }
062 this .stream = stream;
063 }
064
065 public int read() throws IOException {
066 checkClosed();
067
068 bitOffset = 0;
069
070 int val = cache.read(streamPos);
071 if (val != -1) {
072 ++streamPos;
073 }
074 return val;
075 }
076
077 public int read(byte[] b, int off, int len) throws IOException {
078 checkClosed();
079
080 if (b == null) {
081 throw new NullPointerException("b == null!");
082 }
083 // Fix 4467608: read([B,I,I) works incorrectly if len<=0
084 if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
085 throw new IndexOutOfBoundsException(
086 "off < 0 || len < 0 || off+len > b.length || off+len < 0!");
087 }
088
089 bitOffset = 0;
090
091 if (len == 0) {
092 return 0;
093 }
094
095 // check if we're already at/past EOF i.e.
096 // no more bytes left to read from cache
097 long bytesLeftInCache = cache.getLength() - streamPos;
098 if (bytesLeftInCache <= 0) {
099 return -1; // EOF
100 }
101
102 // guaranteed by now that bytesLeftInCache > 0 && len > 0
103 // and so the rest of the error checking is done by cache.read()
104 // NOTE that alot of error checking is duplicated
105 len = (int) Math.min(bytesLeftInCache, (long) len);
106 cache.read(b, off, len, streamPos);
107 streamPos += len;
108 return len;
109 }
110
111 public void write(int b) throws IOException {
112 flushBits(); // this will call checkClosed() for us
113 cache.write(b, streamPos);
114 ++streamPos;
115 }
116
117 public void write(byte[] b, int off, int len) throws IOException {
118 flushBits(); // this will call checkClosed() for us
119 cache.write(b, off, len, streamPos);
120 streamPos += len;
121 }
122
123 public long length() {
124 try {
125 checkClosed();
126 return cache.getLength();
127 } catch (IOException e) {
128 return -1L;
129 }
130 }
131
132 /**
133 * Returns <code>true</code> since this
134 * <code>ImageOutputStream</code> caches data in order to allow
135 * seeking backwards.
136 *
137 * @return <code>true</code>.
138 *
139 * @see #isCachedMemory
140 * @see #isCachedFile
141 */
142 public boolean isCached() {
143 return true;
144 }
145
146 /**
147 * Returns <code>false</code> since this
148 * <code>ImageOutputStream</code> does not maintain a file cache.
149 *
150 * @return <code>false</code>.
151 *
152 * @see #isCached
153 * @see #isCachedMemory
154 */
155 public boolean isCachedFile() {
156 return false;
157 }
158
159 /**
160 * Returns <code>true</code> since this
161 * <code>ImageOutputStream</code> maintains a main memory cache.
162 *
163 * @return <code>true</code>.
164 *
165 * @see #isCached
166 * @see #isCachedFile
167 */
168 public boolean isCachedMemory() {
169 return true;
170 }
171
172 /**
173 * Closes this <code>MemoryCacheImageOutputStream</code>. All
174 * pending data is flushed to the output, and the cache
175 * is released. The destination <code>OutputStream</code>
176 * is not closed.
177 */
178 public void close() throws IOException {
179 long length = cache.getLength();
180 seek(length);
181 flushBefore(length);
182 super .close();
183 cache.reset();
184 cache = null;
185 stream = null;
186 }
187
188 public void flushBefore(long pos) throws IOException {
189 long oFlushedPos = flushedPos;
190 super .flushBefore(pos); // this will call checkClosed() for us
191
192 long flushBytes = flushedPos - oFlushedPos;
193 cache.writeToStream(stream, oFlushedPos, flushBytes);
194 cache.disposeBefore(flushedPos);
195 stream.flush();
196 }
197 }
|