/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.libraries.pixie.wmf;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
/**
* A block of raw mmeory. This is used to store various metafile objects as they are read in from file.
*/
public class Buffer {
/**
* The memory itself.
*/
private byte[] bytes;
/**
* The current length of the memory.
*/
private int length;
/**
* Default Constructor. Defines a buffer without an initial size.
*/
protected Buffer() {
}
/**
* Defines a new buffer with the given initial size in bytes.
*
* @param length the length of the buffer in bytes.
*/
protected Buffer( final int length ) {
setCapacity( length );
}
/**
* The size of the stored data in the memory.
*/
public final int getLength() {
return length;
}
/**
* Extends the length to the given new size.
*
* @param len the new length.
* @throws IllegalArgumentException if the length is shorter than the used storage in memory.
*/
protected void setLength( final int len ) {
if ( len > bytes.length ) {
throw new IllegalArgumentException();
}
this.length = len;
}
/**
* Ensures that the buffer has enough space for the given number of bytes.
*
* @param capacity the new capacity that should be ensured.
* @throws IllegalArgumentException if the capacity is smaller than the buffers length.
*/
protected void setCapacity( final int capacity ) {
if ( capacity < getLength() ) {
throw new IllegalArgumentException();
}
if ( bytes == null || bytes.length == 0 ) {
bytes = new byte[ capacity ];
} else if ( capacity != bytes.length ) {
final byte[] old = bytes;
bytes = new byte[ capacity ];
System.arraycopy( old, 0, bytes, 0, Math.min( old.length, capacity ) );
}
}
/**
* Read <code>len</code> bytes into the memory from a stream and stores the read bytes at the given offset.
*
* @param in the input stream that should be used
* @param offset the offset
* @param len the number bytes that should be read.
*/
public void read( final InputStream in, int offset, int len )
throws IOException {
// make sure, that all bytes can be read and create the buffer if needed.
if ( bytes == null || offset + len > bytes.length ) {
setCapacity( offset + len );
}
//in.readFully( bytes, offset, len );
while ( len > 0 ) {
final int blockSize = in.read( bytes, offset, len );
if ( blockSize <= 0 ) {
throw new EOFException();
}
offset += blockSize;
len -= blockSize;
setLength( offset );
}
}
/**
* Moves the buffer contents from the source offset to the target offset, the areas should not overlap.
*
* @param sourceoffset
* @param length
* @param targetoffset
*/
protected void move( final int sourceoffset, final int length, final int targetoffset ) {
System.arraycopy( bytes, sourceoffset, bytes, targetoffset, length );
}
/**
* Set the int value as big-endian.
*
* @param offset the offset where to set the int value.
* @param value the integer value that should be set.
*/
public void setInt( final int offset, final int value ) {
if ( offset > ( getLength() - 4 ) ) {
throw new IndexOutOfBoundsException();
}
setShort( offset, value & 0x0ffff );
setShort( offset + 2, value >> 16 );
}
/**
* Return the 32-bit int at the given byte offset.
*
* @param offset the offset where the integer value is stored in the memory
* @return the integer.
*/
public int getInt( final int offset ) {
if ( offset > ( getLength() - 4 ) ) {
throw new IndexOutOfBoundsException();
}
return ( getShort( offset ) & 0x0ffff ) | ( getShort( offset + 2 ) << 16 );
}
/**
* Stores the given short as BigEndian value.
*
* @param offset the offset.
* @param shortval the shortvalue.
*/
public void setShort( final int offset, final int shortval ) {
if ( offset > ( getLength() - 2 ) ) {
throw new IndexOutOfBoundsException();
}
bytes[ offset ] = (byte) ( shortval & 0x0ff );
bytes[ offset + 1 ] = (byte) ( shortval >> 8 );
}
/**
* Return the 16-bit int at the given byte offset.
*
* @param offset the offset from where to read the short.
* @return the short.
*/
public int getShort( final int offset ) {
if ( offset > ( getLength() - 2 ) ) {
throw new IndexOutOfBoundsException
( "Offset " + offset + " is out of limit. " +
"Max length is " + ( getLength() - 2 ) );
}
return ( bytes[ offset ] & 0x0ff ) | ( bytes[ offset + 1 ] << 8 );
}
/**
* Sets the byte at the given offset.
*
* @param offset the offset.
* @param value the byte that should be set.
*/
public void setByte( final int offset, final int value ) {
if ( offset > ( getLength() - 1 ) ) {
throw new IndexOutOfBoundsException();
}
bytes[ offset ] = (byte) ( value & 0x0ff );
}
/**
* Return the 8-bit int at the given byte offset.
*
* @param offset the offset from where to read the byte
* @return the byte read.
*/
public int getByte( final int offset ) {
if ( offset > ( getLength() - 1 ) ) {
throw new IndexOutOfBoundsException();
}
return bytes[ offset ] & 0x0ff;
}
/**
* Writes the given string as byte stream using the plattforms default encoding.
*
* @param offset the offset, where to store the string.
* @param str the string that should be stored in the Wmf.
*/
public void setString( final int offset, final String str ) {
if ( ( offset + str.length() ) > ( getLength() - 1 ) ) {
throw new IndexOutOfBoundsException();
}
final byte[] b = str.getBytes();
final int len = getLength() - offset;
for ( int i = 0; i < len; i++ ) {
bytes[ offset + i ] = b[ offset ];
}
if ( ( offset + len ) < getLength() ) {
bytes[ offset + len ] = 0;
}
}
/**
* Return the null-terminated string at the given byte offset with the given maximum length.
*
* @param offset the offset where the string starts
* @param len the maximum length of the string
* @return the null-terminated string read.
*/
public String getString( final int offset, final int len ) {
int i;
for ( i = 0; i < len; i++ ) {
if ( bytes[ offset + i ] == 0 ) {
break;
}
}
return new String( bytes, offset, i );
}
/**
* Gets an input stream to read from the memory buffer.
*
* @param offset the offse, from where to read.
* @return the InputStream.
*/
public InputStream getInputStream( final int offset ) {
return new ByteArrayInputStream( bytes, offset, bytes.length - offset );
}
}