org.odftoolkit.odfdom.converter.core.utils.ByteArrayOutputStream Maven / Gradle / Ivy
/**
* Copyright (C) 2011-2012 The XDocReport Team
*
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.odftoolkit.odfdom.converter.core.utils;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* This class implements an output stream in which the data is written into a byte array. The buffer automatically grows
* as data is written to it.
*
* The data can be retrieved using toByteArray()
and toString()
.
*
* Closing a ByteArrayOutputStream has no effect. The methods in this class can be called after the stream has
* been closed without generating an IOException.
*
* This is an alternative implementation of the java.io.ByteArrayOutputStream class. The original implementation only
* allocates 32 bytes at the beginning. As this class is designed for heavy duty it starts at 1024 bytes. In contrast to
* the original it doesn't reallocate the whole memory block but allocates additional buffers. This way no buffers need
* to be garbage collected and the contents don't have to be copied to the new buffer. This class is designed to behave
* exactly like the original. The only exception is the deprecated toString(int) method that has been ignored.
*
* @author Jeremias Maerki
* @author Holger Hoffstatte
* @version $Id: ByteArrayOutputStream.java 610010 2008-01-08 14:50:59Z niallp $
*/
public class ByteArrayOutputStream
extends OutputStream
{
/** A singleton empty byte array. */
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/** The list of buffers, which grows and never reduces. */
private List buffers = new ArrayList();
/** The index of the current buffer. */
private int currentBufferIndex;
/** The total count of bytes in all the filled buffers. */
private int filledBufferSum;
/** The current buffer. */
private byte[] currentBuffer;
/** The total count of bytes written. */
private int count;
/**
* Creates a new byte array output stream. The buffer capacity is initially 1024 bytes, though its size increases if
* necessary.
*/
public ByteArrayOutputStream()
{
this( 1024 );
}
/**
* Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes.
*
* @param size the initial size
* @throws IllegalArgumentException if size is negative
*/
public ByteArrayOutputStream( int size )
{
if ( size < 0 )
{
throw new IllegalArgumentException( "Negative initial size: " + size );
}
needNewBuffer( size );
}
/**
* Return the appropriate byte[]
buffer specified by index.
*
* @param index the index of the buffer required
* @return the buffer
*/
private byte[] getBuffer( int index )
{
return (byte[]) buffers.get( index );
}
/**
* Makes a new buffer available either by allocating a new one or re-cycling an existing one.
*
* @param newcount the size of the buffer if one is created
*/
private void needNewBuffer( int newcount )
{
if ( currentBufferIndex < buffers.size() - 1 )
{
// Recycling old buffer
filledBufferSum += currentBuffer.length;
currentBufferIndex++;
currentBuffer = getBuffer( currentBufferIndex );
}
else
{
// Creating new buffer
int newBufferSize;
if ( currentBuffer == null )
{
newBufferSize = newcount;
filledBufferSum = 0;
}
else
{
newBufferSize = Math.max( currentBuffer.length << 1, newcount - filledBufferSum );
filledBufferSum += currentBuffer.length;
}
currentBufferIndex++;
currentBuffer = new byte[newBufferSize];
buffers.add( currentBuffer );
}
}
/**
* Write the bytes to byte array.
*
* @param b the bytes to write
* @param off The start offset
* @param len The number of bytes to write
*/
public void write( byte[] b, int off, int len )
{
if ( ( off < 0 ) || ( off > b.length ) || ( len < 0 ) || ( ( off + len ) > b.length ) || ( ( off + len ) < 0 ) )
{
throw new IndexOutOfBoundsException();
}
else if ( len == 0 )
{
return;
}
synchronized ( this )
{
int newcount = count + len;
int remaining = len;
int inBufferPos = count - filledBufferSum;
while ( remaining > 0 )
{
int part = Math.min( remaining, currentBuffer.length - inBufferPos );
System.arraycopy( b, off + len - remaining, currentBuffer, inBufferPos, part );
remaining -= part;
if ( remaining > 0 )
{
needNewBuffer( newcount );
inBufferPos = 0;
}
}
count = newcount;
}
}
/**
* Write a byte to byte array.
*
* @param b the byte to write
*/
public synchronized void write( int b )
{
int inBufferPos = count - filledBufferSum;
if ( inBufferPos == currentBuffer.length )
{
needNewBuffer( count + 1 );
inBufferPos = 0;
}
currentBuffer[inBufferPos] = (byte) b;
count++;
}
/**
* Writes the entire contents of the specified input stream to this byte stream. Bytes from the input stream are
* read directly into the internal buffers of this streams.
*
* @param in the input stream to read from
* @return total number of bytes read from the input stream (and written to this stream)
* @throws IOException if an I/O error occurs while reading the input stream
* @since Commons IO 1.4
*/
public synchronized int write( InputStream in )
throws IOException
{
int readCount = 0;
int inBufferPos = count - filledBufferSum;
int n = in.read( currentBuffer, inBufferPos, currentBuffer.length - inBufferPos );
while ( n != -1 )
{
readCount += n;
inBufferPos += n;
count += n;
if ( inBufferPos == currentBuffer.length )
{
needNewBuffer( currentBuffer.length );
inBufferPos = 0;
}
n = in.read( currentBuffer, inBufferPos, currentBuffer.length - inBufferPos );
}
return readCount;
}
/**
* Return the current size of the byte array.
*
* @return the current size of the byte array
*/
public synchronized int size()
{
return count;
}
/**
* Closing a ByteArrayOutputStream has no effect. The methods in this class can be called after the stream
* has been closed without generating an IOException.
*
* @throws IOException never (this method should not declare this exception but it has to now due to backwards
* compatability)
*/
public void close()
throws IOException
{
// nop
}
/**
* @see java.io.ByteArrayOutputStream#reset()
*/
public synchronized void reset()
{
count = 0;
filledBufferSum = 0;
currentBufferIndex = 0;
currentBuffer = getBuffer( currentBufferIndex );
}
/**
* Writes the entire contents of this byte stream to the specified output stream.
*
* @param out the output stream to write to
* @throws IOException if an I/O error occurs, such as if the stream is closed
* @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
*/
public synchronized void writeTo( OutputStream out )
throws IOException
{
int remaining = count;
for ( int i = 0; i < buffers.size(); i++ )
{
byte[] buf = getBuffer( i );
int c = Math.min( buf.length, remaining );
out.write( buf, 0, c );
remaining -= c;
if ( remaining == 0 )
{
break;
}
}
}
/**
* Gets the curent contents of this byte stream as a byte array. The result is independent of this stream.
*
* @return the current contents of this output stream, as a byte array
* @see java.io.ByteArrayOutputStream#toByteArray()
*/
public synchronized byte[] toByteArray()
{
int remaining = count;
if ( remaining == 0 )
{
return EMPTY_BYTE_ARRAY;
}
byte newbuf[] = new byte[remaining];
int pos = 0;
for ( int i = 0; i < buffers.size(); i++ )
{
byte[] buf = getBuffer( i );
int c = Math.min( buf.length, remaining );
System.arraycopy( buf, 0, newbuf, pos, c );
pos += c;
remaining -= c;
if ( remaining == 0 )
{
break;
}
}
return newbuf;
}
/**
* Gets the curent contents of this byte stream as a string.
*
* @return the contents of the byte array as a String
* @see java.io.ByteArrayOutputStream#toString()
*/
public String toString()
{
return new String( toByteArray() );
}
/**
* Gets the curent contents of this byte stream as a string using the specified encoding.
*
* @param enc the name of the character encoding
* @return the string converted from the byte array
* @throws UnsupportedEncodingException if the encoding is not supported
* @see java.io.ByteArrayOutputStream#toString(String)
*/
public String toString( String enc )
throws UnsupportedEncodingException
{
return new String( toByteArray(), enc );
}
}