All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.refcodes.serial.ByteArraySequence Maven / Gradle / Ivy

Go to download

Artifact providing generic (byte) serialization functionality including a TTY-/COM-Port implementation of the serial framework as well as a (local) loopback port.

The newest version!
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany, distributed
// on an "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, and licen-
// sed under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// -----------------------------------------------------------------------------
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// -----------------------------------------------------------------------------
// Apache License, v2.0 ("http://www.apache.org/licenses/TEXT-2.0")
// -----------------------------------------------------------------------------
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.serial;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.refcodes.mixin.ConcatenateMode;
import org.refcodes.mixin.TruncateMode;
import org.refcodes.numerical.CrcAlgorithm;
import org.refcodes.numerical.Endianess;

/**
 * A {@link ByteArraySequence} implements the {@link Sequence} interface by
 * using chunks of byte arrays, therewith reducing array copy operations when
 * working with byte fields. A {@link ByteArraySequence} is a one dimensional
 * representation of a byte array of byte arrays (a two dimensional byte array).
 * Each byte array inside the byte array (of byte arrays) is called a chunk.
 * Using this technique we easily can create our {@link Sequence} instances
 * consisting of many chunks while reducing the copying and concatenating
 * efforts (and buffer allocations), providing for a low memory footprint.
 */
public class ByteArraySequence implements Sequence {

	// /////////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////////

	private byte[][] _chunks;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Constructs an empty {@link ByteArraySequence}.
	 */
	public ByteArraySequence() {
		_chunks = new byte[0][];
	}

	/**
	 * Constructs a {@link ByteArraySequence} from the provided byte.
	 * 
	 * @param aByte The only byte out of which the {@link ByteArraySequence}
	 *        consists.
	 */
	public ByteArraySequence( byte aByte ) {
		_chunks = new byte[1][];
		_chunks[0] = new byte[] { aByte };
	}

	/**
	 * Constructs a {@link ByteArraySequence} from the provided byte chunk.
	 * 
	 * @param aBytes The only chunk out of which the {@link ByteArraySequence}
	 *        consists.
	 */
	public ByteArraySequence( byte... aBytes ) {
		_chunks = new byte[1][];
		_chunks[0] = aBytes;
	}

	/**
	 * Constructs a {@link ByteArraySequence} from the provided byte chunks.
	 * 
	 * @param aBytes The byte chunks (a two dimensional byte array) out of which
	 *        the {@link ByteArraySequence} consists.
	 */
	public ByteArraySequence( byte[]... aBytes ) {
		_chunks = aBytes;
	}

	/**
	 * Constructs a {@link ByteArraySequence} from the provided byte chunk.
	 * 
	 * @param aBytes The only chunk out of which the {@link ByteArraySequence}
	 *        consists.
	 * @param aOffset The offset from where to take the chunk data.
	 * @param aLength The number of bytes to take, beginning at the provided
	 *        offset.
	 */
	public ByteArraySequence( byte[] aBytes, int aOffset, int aLength ) {
		_chunks = new byte[1][];
		_chunks[0] = Arrays.copyOfRange( aBytes, aOffset, aOffset + aLength );
	}

	/**
	 * Constructs a {@link ByteArraySequence} from the provided byte chunks.
	 * 
	 * @param aBytes The byte chunks (many two dimensional byte arrays) out of
	 *        which the {@link ByteArraySequence} consists.
	 */
	public ByteArraySequence( byte[][]... aBytes ) {
		int theChunkCount = 0;
		for ( byte[][] aByte : aBytes ) {
			theChunkCount += aByte.length;
		}
		_chunks = new byte[theChunkCount][];
		byte[][] eChunks;
		int index = 0;
		for ( byte[][] aByte : aBytes ) {
			eChunks = aByte;
			for ( byte[] eChunk : eChunks ) {
				_chunks[index] = eChunk;
				index++;
			}
		}
	}

	/**
	 * Constructs the {@link ByteArraySequence} from the content of the provided
	 * {@link InputStream}.
	 * 
	 * @param aInputStream The {@link InputStream} from which's data to
	 *        construct this instance.
	 * 
	 * @throws IOException thrown in case reading from the {@link InputStream}
	 *         caused problems.
	 */
	public ByteArraySequence( InputStream aInputStream ) throws IOException {
		//		ByteArrayOutputStream theByteOutputStream = new ByteArrayOutputStream();
		//		int eRead;
		//		byte[] theBuffer = new byte[BUFFER_SIZE];
		//		while ( (eRead = aInputStream.read( theBuffer, 0, theBuffer.length )) != -1 ) {
		//			theByteOutputStream.write( theBuffer, 0, eRead );
		//		}
		//		_chunks = new byte[0][];
		//		_chunks[0] = theByteOutputStream.toByteArray();

		final ByteArrayOutputStream theByteOutputStream = new ByteArrayOutputStream();
		int eByte;
		while ( ( eByte = aInputStream.read() ) != -1 ) {
			theByteOutputStream.write( eByte );
		}
		_chunks = new byte[1][];
		_chunks[0] = theByteOutputStream.toByteArray();

	}

	/**
	 * Constructs a {@link ByteArraySequence} of the given initial length.
	 * 
	 * @param aLength The initial length of the {@link Sequence}.
	 */
	public ByteArraySequence( int aLength ) {
		_chunks = new byte[1][];
		_chunks[0] = new byte[aLength];
	}

	/**
	 * Creates a new {@link Sequence} with the bytes from given
	 * {@link Sequence}.
	 * 
	 * @param aSequence The {@link Sequence} from which to take the data.
	 */
	public ByteArraySequence( Sequence aSequence ) {
		this( aSequence.getLength() );
		for ( int i = 0; i < aSequence.getLength(); i++ ) {
			setByteAt( i, aSequence.getByteAt( i ) );
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void append( byte[] aBytes, int aOffset, int aLength ) {

		if ( aOffset != 0 || aLength != aBytes.length ) {
			final byte[] theBytes = new byte[aLength];
			for ( int i = 0; i < theBytes.length; i++ ) {
				theBytes[i] = aBytes[aOffset + i];
			}
			aBytes = theBytes;
		}

		final byte[][] theChunks = new byte[_chunks.length + 1][];
		for ( int i = 0; i < _chunks.length; i++ ) {
			theChunks[i] = _chunks[i];
		}
		theChunks[theChunks.length - 1] = aBytes;
		_chunks = theChunks;
		synchronized ( this ) {
			notifyAll();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void append( Sequence aSequence ) {
		if ( aSequence != null ) {
			_chunks = toChunks( _chunks, toChunks( aSequence ) );
			synchronized ( this ) {
				notifyAll();
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void empty() {
		_chunks = new byte[0][];
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals( Object obj ) {
		if ( this == obj ) {
			return true;
		}
		if ( obj == null ) {
			return false;
		}
		if ( getClass() != obj.getClass() ) {
			return false;
		}
		final ByteArraySequence other = (ByteArraySequence) obj;
		if ( !Arrays.deepEquals( _chunks, other._chunks ) ) {
			return false;
		}
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public byte getByteAt( int aIndex ) {
		int offset = 0;
		byte[] eChunk;
		for ( byte[] _chunk : _chunks ) {
			eChunk = _chunk;
			if ( eChunk.length + offset > aIndex ) {
				return eChunk[aIndex - offset];
			}
			offset += eChunk.length;
		}
		throw new IndexOutOfBoundsException( aIndex );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getLength() {
		if ( _chunks == null ) {
			return -1;
		}
		int size = 0;
		for ( byte[] _chunk : _chunks ) {
			// if ( _chunks[i] != null ) {
			size += _chunk.length;
			// }
		}
		return size;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.deepHashCode( _chunks );
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Iterator iterator() {
		return new SequenceIterator();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void prepend( byte[] aBytes, int aOffset, int aLength ) {

		if ( aOffset != 0 && aLength != aBytes.length ) {
			final byte[] theBytes = new byte[aLength];
			for ( int i = 0; i < theBytes.length; i++ ) {
				theBytes[i] = aBytes[aOffset + i];
			}
			aBytes = theBytes;
		}

		final byte[][] theChunks = new byte[_chunks.length + 1][];
		for ( int i = 0; i < _chunks.length; i++ ) {
			theChunks[i + 1] = _chunks[i];
		}
		theChunks[0] = aBytes;
		_chunks = theChunks;
		synchronized ( this ) {
			notifyAll();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void prepend( Sequence aSequence ) {
		if ( aSequence != null ) {
			_chunks = toChunks( toChunks( aSequence ), _chunks );
			synchronized ( this ) {
				notifyAll();
			}
		}
	}

	/**
	 * Replaces the content of this {@link Sequence} with the content of the
	 * given byte array.
	 * 
	 * @param aBytes The byte array with which to replace the content.
	 */
	@Override
	public void replace( byte[] aBytes ) {
		_chunks = new byte[1][];
		_chunks[0] = aBytes;
	}

	/**
	 * Replaces the content of this {@link Sequence} with the content of the
	 * given {@link Sequence}.
	 * 
	 * @param aSequence The {@link Sequence} with which to replace the content.
	 */
	@Override
	public void replace( Sequence aSequence ) {
		if ( aSequence instanceof ByteArraySequence theSequence ) {
			_chunks = theSequence._chunks;
		}
		else {
			replace( aSequence.toBytes() );
		}
	}

	/**
	 * {@inheritDoc} CAUTION: If possible, this method avoids array copying, so
	 * if the offset is 0 and the length the same as the byte array's length,
	 * then the given byte array is used internally!
	 */
	@Override
	public void replace( byte[] aBytes, int aOffset, int aLength ) {
		_chunks = new byte[1][];

		if ( aOffset == 0 && aLength == aBytes.length ) {
			_chunks[0] = aBytes;
		}
		else {
			_chunks[0] = Arrays.copyOfRange( aBytes, aOffset, aOffset + aLength );
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void replace( Sequence aSequence, int aOffset, int aLength ) {

		if ( aOffset == 0 && aLength == aSequence.getLength() ) {
			replace( aSequence );
		}
		else {
			_chunks = new byte[1][];
			_chunks[0] = aSequence.toBytes( aOffset, aLength );
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setByteAt( int aIndex, byte aByte ) {
		int offset = 0;
		byte[] eChunk;
		for ( byte[] _chunk : _chunks ) {
			eChunk = _chunk;
			if ( eChunk.length + offset > aIndex ) {
				eChunk[aIndex - offset] = aByte;
				return;
			}
			offset += eChunk.length;
		}
		throw new IndexOutOfBoundsException( aIndex );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toAppend( byte... aBytes ) {
		return toAppend( aBytes, 0, aBytes.length );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toAppend( byte[] aBytes, int aOffset, int aLength ) {

		final byte[] theBytes = new byte[aLength];
		for ( int i = 0; i < theBytes.length; i++ ) {
			theBytes[i] = aBytes[aOffset + i];
		}

		final byte[][] theChunks = new byte[_chunks.length + 1][];
		for ( int i = 0; i < _chunks.length; i++ ) {
			theChunks[i] = _chunks[i].clone();
		}
		theChunks[theChunks.length - 1] = theBytes;
		return new ByteArraySequence( theChunks );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toAppend( Sequence aSequence ) {
		return toSequence( this, aSequence );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toClone() {
		final byte[][] theClone = _chunks.clone();
		for ( int i = 0; i < theClone.length; i++ ) {
			theClone[i] = theClone[i].clone();
		}
		return new ByteArraySequence( theClone );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toConcatenate( ConcatenateMode aConcatenateMode, byte... aBytes ) {
		return switch ( aConcatenateMode ) {
		case PREPEND -> toPrepend( aBytes );
		case APPEND -> toAppend( aBytes );
		};
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toConcatenate( Sequence aSequence, ConcatenateMode aConcatenateMode ) {
		return switch ( aConcatenateMode ) {
		case PREPEND -> toPrepend( aSequence );
		case APPEND -> toAppend( aSequence );
		};
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public byte[] toCrcBytes( CrcAlgorithm aCrcAlgorithm, Endianess aEndianess ) {
		long theChecksum = 0;
		for ( int i = 0; i < getChunkCount(); i++ ) {
			theChecksum = aCrcAlgorithm.toCrcChecksum( theChecksum, getChunkAt( i ) );
		}
		return aEndianess.toBytes( theChecksum, aCrcAlgorithm.getCrcWidth() );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public byte[] toCrcBytes( long aCrcChecksum, CrcAlgorithm aCrcAlgorithm, Endianess aEndianess ) {
		for ( int i = 0; i < getChunkCount(); i++ ) {
			aCrcChecksum = aCrcAlgorithm.toCrcChecksum( aCrcChecksum, getChunkAt( i ) );
		}
		return aEndianess.toBytes( aCrcChecksum, aCrcAlgorithm.getCrcWidth() );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public long toCrcChecksum( CrcAlgorithm aCrcAlgorithm ) {
		long theChecksum = 0;
		for ( int i = 0; i < getChunkCount(); i++ ) {
			theChecksum = aCrcAlgorithm.toCrcChecksum( theChecksum, getChunkAt( i ) );
		}
		return theChecksum;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public long toCrcChecksum( long aCrcChecksum, CrcAlgorithm aCrcAlgorithm ) {
		for ( int i = 0; i < getChunkCount(); i++ ) {
			aCrcChecksum = aCrcAlgorithm.toCrcChecksum( aCrcChecksum, getChunkAt( i ) );
		}
		return aCrcChecksum;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toPrepend( byte... aBytes ) {
		return toAppend( aBytes, 0, aBytes.length );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toPrepend( byte[] aBytes, int aOffset, int aLength ) {

		final byte[] theBytes = new byte[aLength];
		for ( int i = 0; i < theBytes.length; i++ ) {
			theBytes[i] = aBytes[aOffset + i];
		}

		final byte[][] theChunks = new byte[_chunks.length + 1][];
		for ( int i = 0; i < _chunks.length; i++ ) {
			theChunks[i + 1] = _chunks[i].clone();
		}
		theChunks[0] = theBytes;
		return new ByteArraySequence( theChunks );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toPrepend( Sequence aSequence ) {
		return toSequence( aSequence, this );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toSequence( int aOffset, int aLength ) {
		return new ByteArraySequence( toBytes( aOffset, aLength ) );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return getClass().getSimpleName() + " [length=" + getLength() + ", chunks=" + Arrays.toString( _chunks ) + "]";
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toTruncate( int aLength, TruncateMode aTruncateMode ) {
		final ByteArraySequence theSequence = toClone().withTruncate( aLength, aTruncateMode );
		return theSequence;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toTruncateHead( int aLength ) {
		final ByteArraySequence theSequence = toClone().withTruncateHead( aLength );
		return theSequence;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toTruncateTail( int aLength ) {
		final ByteArraySequence theSequence = toClone().withTruncateTail( aLength );
		return theSequence;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toOverwrite( byte[] aBytes ) {
		final ByteArraySequence theSequence = toClone().withOverwrite( aBytes );
		return theSequence;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toOverwrite( int aOffset, Sequence aSeqquence, int aBytesOffset, int aLength ) {
		final ByteArraySequence theSequence = toClone().withOverwrite( aOffset, aSeqquence, aBytesOffset, aLength );
		return theSequence;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toOverwrite( int aOffset, byte[] aBytes, int aBytesOffset, int aLength ) {
		final ByteArraySequence theSequence = toClone().withOverwrite( aOffset, aBytes, aBytesOffset, aLength );
		return theSequence;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toOverwrite( int aOffset, Sequence aSequence ) {
		final ByteArraySequence theSequence = toClone().withOverwrite( aOffset, aSequence );
		return theSequence;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence toOverwrite( Sequence aSequence ) {
		final ByteArraySequence theSequence = toClone().withOverwrite( aSequence );
		return theSequence;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void truncateHead( int aLength ) {
		if ( aLength >= getLength() ) {
			_chunks = new byte[0][];
			synchronized ( this ) {
				notifyAll();
			}
		}
		else if ( aLength > 0 ) {
			final int theTruncatedLength = getLength() - aLength;
			final List theChunks = new ArrayList<>();
			int theLength = 0;
			for ( int i = _chunks.length - 1; i > 0; i-- ) {
				if ( theLength + _chunks[i].length <= theTruncatedLength ) {
					theChunks.add( 0, _chunks[i] );
					theLength += _chunks[i].length;
				}
				else {
					break;
				}
			}
			if ( theLength < theTruncatedLength ) {
				final byte[] theLastChunk = toBytes( aLength, theTruncatedLength - theLength );
				theChunks.add( 0, theLastChunk );
			}
			_chunks = theChunks.toArray( new byte[theChunks.size()][] );
			synchronized ( this ) {
				notifyAll();
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void truncateTail( int aLength ) {
		if ( aLength >= getLength() ) {
			_chunks = new byte[0][];
			synchronized ( this ) {
				notifyAll();
			}
		}
		else if ( aLength > 0 ) {
			final int theTruncatedLength = getLength() - aLength;
			final List theChunks = new ArrayList<>();
			int theLength = 0;
			for ( byte[] _chunk : _chunks ) {
				if ( theLength + _chunk.length <= theTruncatedLength ) {
					theChunks.add( _chunk );
					theLength += _chunk.length;
				}
				else {
					break;
				}
			}
			if ( theLength < theTruncatedLength ) {
				final byte[] theLastChunk = toBytes( theLength, theTruncatedLength - theLength );
				theChunks.add( theLastChunk );
			}
			_chunks = theChunks.toArray( new byte[theChunks.size()][] );
			synchronized ( this ) {
				notifyAll();
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withAppend( byte... aBytes ) {
		append( aBytes );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withAppend( byte[] aBytes, int aOffset, int aLength ) {
		append( aBytes, aOffset, aLength );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withAppend( Sequence aSequence ) {
		append( aSequence );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withConcatenate( ConcatenateMode aConcatenateMode, byte... aBytes ) {
		concatenate( aConcatenateMode, aBytes );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withConcatenate( Sequence aSequence, ConcatenateMode aConcatenateMode ) {
		prepend( aSequence );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withPrepend( byte... aBytes ) {
		prepend( aBytes );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withPrepend( byte[] aBytes, int aOffset, int aLength ) {
		prepend( aBytes, aOffset, aLength );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withPrepend( Sequence aSequence ) {
		prepend( aSequence );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withReplace( byte[] aBytes ) {
		replace( aBytes );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withReplace( byte[] aBytes, int aOffset, int aLength ) {
		replace( aBytes, aOffset, aLength );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withReplace( Sequence aSequence ) {
		replace( aSequence );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withReplace( Sequence aSequence, int aOffset, int aLength ) {
		replace( aSequence, aOffset, aLength );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withTruncate( int aLength, TruncateMode aTruncateMode ) {
		truncate( aLength, aTruncateMode );
		return this;
	}

	// /////////////////////////////////////////////////////////////////////////
	// HELPER:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withTruncateHead( int aLength ) {
		truncateHead( aLength );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withTruncateTail( int aLength ) {
		truncateTail( aLength );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withOverwrite( byte[] aBytes ) {
		overwrite( aBytes );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withOverwrite( int aOffset, byte[] aBytes ) {
		overwrite( aOffset, aBytes );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withOverwrite( int aOffset, byte[] aBytes, int aBytesOffset, int aLength ) {
		overwrite( aOffset, aBytes, aBytesOffset, aLength );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withOverwrite( int aOffset, Sequence aSequence ) {
		overwrite( aOffset, aSequence );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withOverwrite( int aOffset, Sequence aSequence, int aSequenceOffset, int aLength ) {
		overwrite( aOffset, aSequence, aSequenceOffset, aLength );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ByteArraySequence withOverwrite( Sequence aSequence ) {
		overwrite( aSequence );
		return this;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void writeTo( OutputStream aOutputStream ) throws IOException {
		for ( byte[] aChunk : _chunks ) {
			aOutputStream.write( aChunk );
		}
	}

	/**
	 * Retrieves the chunk at the given index out of which the
	 * {@link ByteArraySequence} consists. The number of chunks can be
	 * determined with the {@link #getChunkCount()} method.
	 * 
	 * @param aIndex The index of the chunk to retrieve.
	 * 
	 * @return The chunks at the given index out of which this
	 *         {@link ByteArraySequence} consists.
	 * 
	 * @throws IndexOutOfBoundsException thrown in case the given index is out
	 *         of bounds.
	 */
	byte[] getChunkAt( int aIndex ) {
		return _chunks[aIndex];
	}

	/**
	 * Retrieves the number of chunks out of which this
	 * {@link ByteArraySequence} consists.
	 * 
	 * @return The number of chunks.
	 */
	int getChunkCount() {
		return _chunks.length;
	}

	/**
	 * Retrieves the chunks out of which the {@link ByteArraySequence} consists.
	 *
	 * @return The two dimensional byte array being the chunks out of which this
	 *         {@link ByteArraySequence} consists.
	 */
	byte[][] getChunks() {
		return _chunks;
	}

	/**
	 * Concatenates the provided chunks (two dimensional byte arrays) into one
	 * chunk
	 * 
	 * @param aBytes The chunks to be concatenated.
	 * 
	 * @return The concatenated chunks
	 */
	private static byte[][] toChunks( byte[][]... aBytes ) {
		int length = 0;
		for ( byte[][] aByte : aBytes ) {
			length += aByte.length;
		}
		final byte[][] theChunks = new byte[length][];
		byte[][] eChunks;
		int index = 0;
		for ( byte[][] aByte : aBytes ) {
			eChunks = aByte;
			for ( byte[] eChunk : eChunks ) {
				theChunks[index] = eChunk;
				index++;
			}
		}
		return theChunks;
	}

	/**
	 * Converts the given {@link Sequence} to chunks. In case we already have a
	 * {@link ByteArraySequence}, then simply {@link #getChunks()} is returned.
	 * 
	 * @param aSequence The {@link Sequence} to be converted.
	 * 
	 * @return The according chunks.
	 */
	private static byte[][] toChunks( Sequence aSequence ) {
		if ( aSequence instanceof ByteArraySequence ) {
			return ( (ByteArraySequence) aSequence ).getChunks();
		}
		return new byte[][] { aSequence.toBytes() };
	}

	/**
	 * Removes any null values from the array.
	 * 
	 * @param aSequences The array from which to remove null values.
	 * 
	 * @return The normalized array.
	 */
	private static Sequence[] toNormalized( Sequence[] aSequences ) {
		final List theSequences = new ArrayList<>();
		for ( Sequence aSequence : aSequences ) {
			if ( aSequence != null ) {
				theSequences.add( aSequence );
			}
		}
		return theSequences.toArray( new ByteArraySequence[theSequences.size()] );
	}

	/**
	 * Concatenates the provided {@link ByteArraySequence} elements into one
	 * {@link ByteArraySequence} element.
	 * 
	 * @param aSequences The {@link ByteArraySequence} elements to be
	 *        concatenated.
	 * 
	 * @return The concatenated {@link ByteArraySequence}.
	 */
	private static ByteArraySequence toSequence( Sequence... aSequences ) {
		aSequences = toNormalized( aSequences );
		int length = 0;
		ByteArraySequence eChunkSequence;
		for ( Sequence aSequence : aSequences ) {
			if ( aSequence instanceof ByteArraySequence ) {
				eChunkSequence = (ByteArraySequence) aSequence;
				length += eChunkSequence._chunks.length;
			}
			else {
				length += 1;
			}
		}
		final byte[][] theChunks = new byte[length][];
		int index = 0;
		for ( Sequence aSequence : aSequences ) {
			if ( aSequence instanceof ByteArraySequence ) {
				eChunkSequence = (ByteArraySequence) aSequence;
				for ( int j = 0; j < eChunkSequence.getChunkCount(); j++ ) {
					theChunks[index] = eChunkSequence._chunks[j].clone();
					index++;
				}
			}
			else {
				theChunks[index] = aSequence.toBytes();
				index++;
			}
		}
		return new ByteArraySequence( theChunks );
	}

	// /////////////////////////////////////////////////////////////////////////
	// INNER CLASSES:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Implements the {@link Iterator} interface being appied to an instancoe of
	 * the {@link ByteArraySequence} type.
	 */
	public class SequenceIterator implements Iterator {

		private int index = 0;

		/**
		 * {@inheritDoc}
		 */
		@Override
		public boolean hasNext() {
			return index < getLength();
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Byte next() {
			return getByteAt( index++ );
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy