001 /**
002 * ========================================
003 * JFreeReport : a free Java report library
004 * ========================================
005 *
006 * Project Info: http://reporting.pentaho.org/
007 *
008 * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
009 *
010 * This library is free software; you can redistribute it and/or modify it under the terms
011 * of the GNU Lesser General Public License as published by the Free Software Foundation;
012 * either version 2.1 of the License, or (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016 * See the GNU Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public License along with this
019 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020 * Boston, MA 02111-1307, USA.
021 *
022 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023 * in the United States and other countries.]
024 *
025 * ------------
026 * $Id: MemoryByteArrayOutputStream.java,v 1.1 2007/05/14 08:56:27 taqua Exp $
027 * ------------
028 * (C) Copyright 2000-2005, by Object Refinery Limited.
029 * (C) Copyright 2005-2007, by Pentaho Corporation.
030 */
031
032 package org.jfree.report.util;
033
034 import java.io.IOException;
035 import java.io.OutputStream;
036
037 import org.jfree.util.Log;
038
039 /**
040 * A string writer that is able to write large amounts of data. The original StringWriter contained in Java doubles
041 * its buffersize everytime the buffer overflows. This is nice with small amounts of data, but awfull for huge
042 * buffers.
043 *
044 * @author Thomas Morgner
045 */
046 public class MemoryByteArrayOutputStream extends OutputStream
047 {
048 private int initialBufferSize;
049 private int maximumBufferIncrement;
050 private int cursor;
051 private byte[] buffer;
052 private byte[] singleIntArray;
053
054 /**
055 * Create a new character-stream writer whose critical sections will synchronize on the writer itself.
056 */
057 public MemoryByteArrayOutputStream()
058 {
059 this(4096, 65536);
060 }
061
062 /**
063 * Create a new character-stream writer whose critical sections will synchronize on the writer itself.
064 */
065 public MemoryByteArrayOutputStream(final int bufferSize, final int maximumBufferIncrement)
066 {
067 this.initialBufferSize = bufferSize;
068 this.maximumBufferIncrement = maximumBufferIncrement;
069 this.buffer = new byte[bufferSize];
070 this.singleIntArray = new byte[1];
071 }
072
073
074 /**
075 * Write a portion of an array of characters.
076 *
077 * @param cbuf Array of characters
078 * @param off Offset from which to start writing characters
079 * @param len Number of characters to write
080 * @throws java.io.IOException If an I/O error occurs
081 */
082 public synchronized void write(final byte cbuf[], final int off, final int len) throws IOException
083 {
084 if (len < 0)
085 {
086 throw new IllegalArgumentException();
087 }
088 if (off < 0)
089 {
090 throw new IndexOutOfBoundsException();
091 }
092 if (cbuf == null)
093 {
094 throw new NullPointerException();
095 }
096 if ((len + off) > cbuf.length)
097 {
098 throw new IndexOutOfBoundsException();
099 }
100
101 ensureSize (cursor + len);
102
103 System.arraycopy(cbuf, off, this.buffer, cursor, len);
104 cursor += len;
105 }
106
107 /**
108 * Writes <code>b.length</code> bytes from the specified byte array to this output stream. The general contract for
109 * <code>write(b)</code> is that it should have exactly the same effect as the call <code>write(b, 0,
110 * b.length)</code>.
111 *
112 * @param b the data.
113 * @throws java.io.IOException if an I/O error occurs.
114 * @see java.io.OutputStream#write(byte[], int, int)
115 */
116 public void write(byte b[]) throws IOException
117 {
118 write(b, 0, b.length);
119 }
120
121 /**
122 * Writes the specified byte to this output stream. The general contract for <code>write</code> is that one byte is
123 * written to the output stream. The byte to be written is the eight low-order bits of the argument <code>b</code>.
124 * The 24 high-order bits of <code>b</code> are ignored.
125 * <p/>
126 * Subclasses of <code>OutputStream</code> must provide an implementation for this method.
127 *
128 * @param b the <code>byte</code>.
129 * @throws java.io.IOException if an I/O error occurs. In particular, an <code>IOException</code> may be thrown if the
130 * output stream has been closed.
131 */
132 public synchronized void write(final int b) throws IOException
133 {
134 this.singleIntArray[0] = (byte) (0xFF & b);
135 write(singleIntArray, 0, 1);
136 }
137
138 private void ensureSize(final int size)
139 {
140 if (this.buffer.length >= size)
141 {
142 return;
143 }
144
145 final int computedSize = (int) Math.min ((this.buffer.length + 1) * 1.5, this.buffer.length + maximumBufferIncrement);
146 final int newSize = Math.max (size, computedSize);
147 final byte[] newBuffer = new byte[newSize];
148 System.arraycopy(this.buffer, 0, newBuffer, 0, cursor);
149 this.buffer = newBuffer;
150 }
151
152 /**
153 * Flush the stream. If the stream has saved any characters from the various write() methods in a buffer, write them
154 * immediately to their intended destination. Then, if that destination is another character or byte stream, flush
155 * it. Thus one flush() invocation will flush all the buffers in a chain of Writers and OutputStreams.
156 * <p/>
157 * If the intended destination of this stream is an abstraction provided by the underlying operating system, for
158 * example a file, then flushing the stream guarantees only that bytes previously written to the stream are passed to
159 * the operating system for writing; it does not guarantee that they are actually written to a physical device such as
160 * a disk drive.
161 *
162 * @throws java.io.IOException If an I/O error occurs
163 */
164 public void flush() throws IOException
165 {
166 if ((buffer.length - cursor) > 50000)
167 Log.debug ("WASTED: " + (buffer.length - cursor));
168 }
169
170 /**
171 * Close the stream, flushing it first. Once a stream has been closed, further write() or flush() invocations will
172 * cause an IOException to be thrown. Closing a previously-closed stream, however, has no effect.
173 *
174 * @throws java.io.IOException If an I/O error occurs
175 */
176 public void close() throws IOException
177 {
178 }
179
180 public synchronized byte[] toByteArray()
181 {
182 final byte[] retval = new byte[cursor];
183 System.arraycopy(buffer, 0, retval, 0, cursor);
184 return retval;
185 }
186
187 public int getLength()
188 {
189 return cursor;
190 }
191
192 public byte[] getRaw()
193 {
194 if ((buffer.length - cursor) > 50000)
195 Log.debug ("WASTED: " + (buffer.length - cursor) + " Length: " + buffer.length);
196 return buffer;
197 }
198 }