View Javadoc
1   /*
2    * Copyright (c) 2000, 2006, 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.IOException;
29  import java.io.OutputStream;
30  
31  /**
32   * An implementation of <code>ImageOutputStream</code> that writes its
33   * output to a regular <code>OutputStream</code>.  A memory buffer is
34   * used to cache at least the data between the discard position and
35   * the current write position.  The only constructor takes an
36   * <code>OutputStream</code>, so this class may not be used for
37   * read/modify/write operations.  Reading can occur only on parts of
38   * the stream that have already been written to the cache and not
39   * yet flushed.
40   *
41   */
42  public class MemoryCacheImageOutputStream extends ImageOutputStreamImpl {
43  
44      private OutputStream stream;
45  
46      private MemoryCache cache = new MemoryCache();
47  
48      /**
49       * Constructs a <code>MemoryCacheImageOutputStream</code> that will write
50       * to a given <code>OutputStream</code>.
51       *
52       * @param stream an <code>OutputStream</code> to write to.
53       *
54       * @exception IllegalArgumentException if <code>stream</code> is
55       * <code>null</code>.
56       */
57      public MemoryCacheImageOutputStream(OutputStream stream) {
58          if (stream == null) {
59              throw new IllegalArgumentException("stream == null!");
60          }
61          this.stream = stream;
62      }
63  
64      public int read() throws IOException {
65          checkClosed();
66  
67          bitOffset = 0;
68  
69          int val = cache.read(streamPos);
70          if (val != -1) {
71              ++streamPos;
72          }
73          return val;
74      }
75  
76      public int read(byte[] b, int off, int len) throws IOException {
77          checkClosed();
78  
79          if (b == null) {
80              throw new NullPointerException("b == null!");
81          }
82          // Fix 4467608: read([B,I,I) works incorrectly if len<=0
83          if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
84              throw new IndexOutOfBoundsException
85                  ("off < 0 || len < 0 || off+len > b.length || off+len < 0!");
86          }
87  
88          bitOffset = 0;
89  
90          if (len == 0) {
91              return 0;
92          }
93  
94          // check if we're already at/past EOF i.e.
95          // no more bytes left to read from cache
96          long bytesLeftInCache = cache.getLength() - streamPos;
97          if (bytesLeftInCache <= 0) {
98              return -1; // EOF
99          }
100 
101         // guaranteed by now that bytesLeftInCache > 0 && len > 0
102         // and so the rest of the error checking is done by cache.read()
103         // NOTE that alot of error checking is duplicated
104         len = (int)Math.min(bytesLeftInCache, (long)len);
105         cache.read(b, off, len, streamPos);
106         streamPos += len;
107         return len;
108     }
109 
110     public void write(int b) throws IOException {
111         flushBits(); // this will call checkClosed() for us
112         cache.write(b, streamPos);
113         ++streamPos;
114     }
115 
116     public void write(byte[] b, int off, int len) throws IOException {
117         flushBits(); // this will call checkClosed() for us
118         cache.write(b, off, len, streamPos);
119         streamPos += len;
120     }
121 
122     public long length() {
123         try {
124             checkClosed();
125             return cache.getLength();
126         } catch (IOException e) {
127             return -1L;
128         }
129     }
130 
131     /**
132      * Returns <code>true</code> since this
133      * <code>ImageOutputStream</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>ImageOutputStream</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>ImageOutputStream</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>MemoryCacheImageOutputStream</code>.  All
173      * pending data is flushed to the output, and the cache
174      * is released.  The destination <code>OutputStream</code>
175      * is not closed.
176      */
177     public void close() throws IOException {
178         long length = cache.getLength();
179         seek(length);
180         flushBefore(length);
181         super.close();
182         cache.reset();
183         cache = null;
184         stream = null;
185     }
186 
187     public void flushBefore(long pos) throws IOException {
188         long oFlushedPos = flushedPos;
189         super.flushBefore(pos); // this will call checkClosed() for us
190 
191         long flushBytes = flushedPos - oFlushedPos;
192         cache.writeToStream(stream, oFlushedPos, flushBytes);
193         cache.disposeBefore(flushedPos);
194         stream.flush();
195     }
196 }