0N/A/*
2362N/A * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage javax.imageio.stream;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.io.OutputStream;
0N/A
0N/A/**
0N/A * An implementation of <code>ImageOutputStream</code> that writes its
0N/A * output to a regular <code>OutputStream</code>. A memory buffer is
0N/A * used to cache at least the data between the discard position and
0N/A * the current write position. The only constructor takes an
0N/A * <code>OutputStream</code>, so this class may not be used for
0N/A * read/modify/write operations. Reading can occur only on parts of
0N/A * the stream that have already been written to the cache and not
0N/A * yet flushed.
0N/A *
0N/A */
0N/Apublic class MemoryCacheImageOutputStream extends ImageOutputStreamImpl {
0N/A
0N/A private OutputStream stream;
0N/A
0N/A private MemoryCache cache = new MemoryCache();
0N/A
0N/A /**
0N/A * Constructs a <code>MemoryCacheImageOutputStream</code> that will write
0N/A * to a given <code>OutputStream</code>.
0N/A *
0N/A * @param stream an <code>OutputStream</code> to write to.
0N/A *
0N/A * @exception IllegalArgumentException if <code>stream</code> is
0N/A * <code>null</code>.
0N/A */
0N/A public MemoryCacheImageOutputStream(OutputStream stream) {
0N/A if (stream == null) {
0N/A throw new IllegalArgumentException("stream == null!");
0N/A }
0N/A this.stream = stream;
0N/A }
0N/A
0N/A public int read() throws IOException {
0N/A checkClosed();
0N/A
0N/A bitOffset = 0;
0N/A
0N/A int val = cache.read(streamPos);
0N/A if (val != -1) {
0N/A ++streamPos;
0N/A }
0N/A return val;
0N/A }
0N/A
0N/A public int read(byte[] b, int off, int len) throws IOException {
0N/A checkClosed();
0N/A
0N/A if (b == null) {
0N/A throw new NullPointerException("b == null!");
0N/A }
0N/A // Fix 4467608: read([B,I,I) works incorrectly if len<=0
0N/A if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
0N/A throw new IndexOutOfBoundsException
0N/A ("off < 0 || len < 0 || off+len > b.length || off+len < 0!");
0N/A }
0N/A
0N/A bitOffset = 0;
0N/A
0N/A if (len == 0) {
0N/A return 0;
0N/A }
0N/A
0N/A // check if we're already at/past EOF i.e.
0N/A // no more bytes left to read from cache
0N/A long bytesLeftInCache = cache.getLength() - streamPos;
0N/A if (bytesLeftInCache <= 0) {
0N/A return -1; // EOF
0N/A }
0N/A
0N/A // guaranteed by now that bytesLeftInCache > 0 && len > 0
0N/A // and so the rest of the error checking is done by cache.read()
0N/A // NOTE that alot of error checking is duplicated
0N/A len = (int)Math.min(bytesLeftInCache, (long)len);
0N/A cache.read(b, off, len, streamPos);
0N/A streamPos += len;
0N/A return len;
0N/A }
0N/A
0N/A public void write(int b) throws IOException {
0N/A flushBits(); // this will call checkClosed() for us
0N/A cache.write(b, streamPos);
0N/A ++streamPos;
0N/A }
0N/A
0N/A public void write(byte[] b, int off, int len) throws IOException {
0N/A flushBits(); // this will call checkClosed() for us
0N/A cache.write(b, off, len, streamPos);
0N/A streamPos += len;
0N/A }
0N/A
0N/A public long length() {
0N/A try {
0N/A checkClosed();
0N/A return cache.getLength();
0N/A } catch (IOException e) {
0N/A return -1L;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns <code>true</code> since this
0N/A * <code>ImageOutputStream</code> caches data in order to allow
0N/A * seeking backwards.
0N/A *
0N/A * @return <code>true</code>.
0N/A *
0N/A * @see #isCachedMemory
0N/A * @see #isCachedFile
0N/A */
0N/A public boolean isCached() {
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Returns <code>false</code> since this
0N/A * <code>ImageOutputStream</code> does not maintain a file cache.
0N/A *
0N/A * @return <code>false</code>.
0N/A *
0N/A * @see #isCached
0N/A * @see #isCachedMemory
0N/A */
0N/A public boolean isCachedFile() {
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Returns <code>true</code> since this
0N/A * <code>ImageOutputStream</code> maintains a main memory cache.
0N/A *
0N/A * @return <code>true</code>.
0N/A *
0N/A * @see #isCached
0N/A * @see #isCachedFile
0N/A */
0N/A public boolean isCachedMemory() {
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Closes this <code>MemoryCacheImageOutputStream</code>. All
0N/A * pending data is flushed to the output, and the cache
0N/A * is released. The destination <code>OutputStream</code>
0N/A * is not closed.
0N/A */
0N/A public void close() throws IOException {
0N/A long length = cache.getLength();
0N/A seek(length);
0N/A flushBefore(length);
0N/A super.close();
0N/A cache.reset();
0N/A cache = null;
0N/A stream = null;
0N/A }
0N/A
0N/A public void flushBefore(long pos) throws IOException {
0N/A long oFlushedPos = flushedPos;
0N/A super.flushBefore(pos); // this will call checkClosed() for us
0N/A
0N/A long flushBytes = flushedPos - oFlushedPos;
0N/A cache.writeToStream(stream, oFlushedPos, flushBytes);
0N/A cache.disposeBefore(flushedPos);
0N/A stream.flush();
0N/A }
0N/A}