1. /*
  2. * @(#)MemoryCacheImageOutputStream.java 1.17 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.imageio.stream;
  8. import java.io.IOException;
  9. import java.io.OutputStream;
  10. /**
  11. * An implementation of <code>ImageOutputStream</code> that writes its
  12. * output to a regular <code>OutputStream</code>. A memory buffer is
  13. * used to cache at least the data between the discard position and
  14. * the current write position. The only constructor takes an
  15. * <code>OutputStream</code>, so this class may not be used for
  16. * read/modify/write operations. Reading can occur only on parts of
  17. * the stream that have already been written to the cache and not
  18. * yet flushed.
  19. *
  20. * @version 0.5
  21. */
  22. public class MemoryCacheImageOutputStream extends ImageOutputStreamImpl {
  23. private OutputStream stream;
  24. private MemoryCache cache = new MemoryCache();
  25. /**
  26. * Constructs a <code>MemoryCacheImageOutputStream</code> that will write
  27. * to a given <code>OutputStream</code>.
  28. *
  29. * @param stream an <code>OutputStream</code> to write to.
  30. *
  31. * @exception IllegalArgumentException if <code>stream</code> is
  32. * <code>null</code>.
  33. */
  34. public MemoryCacheImageOutputStream(OutputStream stream) {
  35. if (stream == null) {
  36. throw new IllegalArgumentException("stream == null!");
  37. }
  38. this.stream = stream;
  39. }
  40. public int read() throws IOException {
  41. checkClosed();
  42. bitOffset = 0;
  43. int val = cache.read(streamPos);
  44. if (val != -1) {
  45. ++streamPos;
  46. }
  47. return val;
  48. }
  49. public int read(byte[] b, int off, int len) throws IOException {
  50. checkClosed();
  51. // Fix 4467619: read([B,I,I) doesn't throw NPE as specified
  52. // Fix 4467608: read([B,I,I) works incorrectly if len<=0
  53. // Will throw NullPointerException if b == null
  54. // Will throw IIOBE if off, len are bad args
  55. if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
  56. throw new IndexOutOfBoundsException
  57. ("off < 0 || len < 0 || off + len > b.length!");
  58. }
  59. bitOffset = 0;
  60. if (len == 0) {
  61. return 0;
  62. }
  63. // check if we're already at/past EOF i.e.
  64. // no more bytes left to read from cache
  65. long bytesLeftInCache = cache.getLength() - streamPos;
  66. if (bytesLeftInCache <= 0) {
  67. return -1; // EOF
  68. }
  69. // guaranteed by now that bytesLeftInCache > 0 && len > 0
  70. // and so the rest of the error checking is done by cache.read()
  71. // NOTE that alot of error checking is duplicated
  72. len = (int)Math.min(bytesLeftInCache, (long)len);
  73. cache.read(b, off, len, streamPos);
  74. streamPos += len;
  75. return len;
  76. }
  77. public void write(int b) throws IOException {
  78. checkClosed();
  79. flushBits();
  80. cache.write(b, streamPos);
  81. ++streamPos;
  82. }
  83. public void write(byte[] b, int off, int len) throws IOException {
  84. checkClosed();
  85. flushBits();
  86. cache.write(b, off, len, streamPos);
  87. streamPos += len;
  88. }
  89. public long length() {
  90. return cache.getLength();
  91. }
  92. /**
  93. * Returns <code>true</code> since this
  94. * <code>ImageOutputStream</code> caches data in order to allow
  95. * seeking backwards.
  96. *
  97. * @return <code>true</code>.
  98. *
  99. * @see #isCachedMemory
  100. * @see #isCachedFile
  101. */
  102. public boolean isCached() {
  103. return true;
  104. }
  105. /**
  106. * Returns <code>false</code> since this
  107. * <code>ImageOutputStream</code> does not maintain a file cache.
  108. *
  109. * @return <code>false</code>.
  110. *
  111. * @see #isCached
  112. * @see #isCachedMemory
  113. */
  114. public boolean isCachedFile() {
  115. return false;
  116. }
  117. /**
  118. * Returns <code>true</code> since this
  119. * <code>ImageOutputStream</code> maintains a main memory cache.
  120. *
  121. * @return <code>true</code>.
  122. *
  123. * @see #isCached
  124. * @see #isCachedFile
  125. */
  126. public boolean isCachedMemory() {
  127. return true;
  128. }
  129. /**
  130. * Closes this <code>MemoryCacheImageOutputStream</code>. All
  131. * pending data is flushed to the output, and the cache
  132. * is released. The destination <code>OutputStream</code>
  133. * is not closed.
  134. */
  135. public void close() throws IOException {
  136. long length = cache.getLength();
  137. seek(length);
  138. flushBefore(length);
  139. super.close();
  140. cache.reset();
  141. stream = null;
  142. }
  143. public void flushBefore(long pos) throws IOException {
  144. long oFlushedPos = flushedPos;
  145. super.flushBefore(pos);
  146. long flushBytes = flushedPos - oFlushedPos;
  147. cache.writeToStream(stream, oFlushedPos, flushBytes);
  148. cache.disposeBefore(flushedPos);
  149. stream.flush();
  150. }
  151. }