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

org.refcodes.io.ReplaceInputStream Maven / Gradle / Ivy

Go to download

Artifact with commonly used I/O functionality and for connection related issues such as receiving or transmitting data in a unified way.

There is a newer version: 3.3.8
Show 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/LICENSE-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.io;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;

import org.refcodes.mixin.Clonable;

/**
 * The {@link ReplaceInputStream} replaces a give byte sequence with another
 * given bytes sequence.
 */
public class ReplaceInputStream extends InputStream {

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

	private InputStream _inputStream;
	private byte[] _findBytes;
	private byte[] _replaceBytes;
	private Status _status = new Status();
	private Status _mark = null;

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

	/**
	 * Constructs a {@link ReplaceInputStream} from the given
	 * {@link InputStream} replacing given bytes with replacement bytes.
	 * 
	 * @param aInputStream The {@link InputStream} to be manipulated
	 *        accordingly.
	 * @param aFindBytes The bytes to replace.
	 * @param aReplaceBytes The replacement bytes.
	 */
	public ReplaceInputStream( InputStream aInputStream, byte[] aFindBytes, byte[] aReplaceBytes ) {
		if ( aFindBytes == null || aFindBytes.length == 0 ) {
			throw new IllegalArgumentException( "You must provide at an array of bytes with a length of at least <1> for the bytes to be replaced!" );
		}
		_findBytes = aFindBytes;
		_replaceBytes = aReplaceBytes != null ? aReplaceBytes : new byte[] {};
		_status.readBytes = new int[_findBytes.length];
		_inputStream = aInputStream;
	}

	/**
	 * Constructs a {@link ReplaceInputStream} from the given
	 * {@link InputStream} replacing given bytes with replacement bytes.
	 * 
	 * @param aInputStream The {@link InputStream} to be manipulated
	 *        accordingly.
	 * @param aFindBytes The {@link String} containing the bytes to replace.
	 * @param aReplaceBytes The {@link String} containing the replacement bytes.
	 */
	public ReplaceInputStream( InputStream aInputStream, String aFindBytes, String aReplaceBytes ) {
		this( aInputStream, aFindBytes.getBytes(), aReplaceBytes.getBytes() );
	}

	/**
	 * Constructs a {@link ReplaceInputStream} from the given
	 * {@link InputStream} replacing given bytes with replacement bytes.
	 * 
	 * @param aInputStream The {@link InputStream} to be manipulated
	 *        accordingly.
	 * @param aFindBytes The {@link String} containing the bytes to replace.
	 * @param aReplaceBytes The {@link String} containing the replacement bytes.
	 * @param aCharset The {@link Charset} to be used when converting the
	 *        provided {@link String} texts into bytes.
	 */
	public ReplaceInputStream( InputStream aInputStream, String aFindBytes, String aReplaceBytes, Charset aCharset ) {
		this( aInputStream, aFindBytes.getBytes( aCharset ), aReplaceBytes.getBytes( aCharset ) );
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int read() throws IOException {

		if ( _status.hasReadBuffer() ) {
			final int theRead = _status.readBytes[_status.readIndex];
			_status.readIndex++;
			return theRead;
		}

		if ( _status.hasReplaceBuffer() ) {
			final int theRead = _replaceBytes[_status.replaceIndex];
			_status.replaceIndex++;
			return theRead;
		}

		int eRead;
		for ( int i = 0; i < _findBytes.length; i++ ) {
			eRead = _inputStream.read();
			_status.readBytes[i] = eRead;
			if ( (byte) eRead != _findBytes[i] ) {
				if ( i == 0 ) {
					return eRead;
				}
				_status.readIndex = 0;
				_status.readLength = i + 1;
				return read();
			}
		}
		_status.replaceIndex = 0;
		return read();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int available() throws IOException {
		final int theAvailableBytes = _status.getReadBuffer() + _status.getReplaceBuffer();
		return _inputStream.available() + theAvailableBytes;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void close() throws IOException {
		_inputStream.close();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void mark( int aReadlimit ) {
		_inputStream.mark( aReadlimit );
		if ( markSupported() ) {
			_mark = (Status) _status.clone();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void reset() throws IOException {
		try {
			_inputStream.reset();
			if ( markSupported() && _mark != null ) {
				_status = _mark;
			}
		}
		catch ( IOException e ) {
			_mark = null;
			throw e;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean markSupported() {
		return _inputStream.markSupported();
	}

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

	private class Status implements Clonable {

		Status() {}

		Status( int[] aReadBytes, int aReadIndex, int aReadLength, int aReplaceIndex ) {
			readBytes = aReadBytes;
			readIndex = aReadIndex;
			readLength = aReadLength;
			replaceIndex = aReplaceIndex;
		}

		int replaceIndex = -1;
		int[] readBytes;
		int readIndex = -1;
		int readLength = 0;

		public boolean hasReadBuffer() {
			return readIndex != -1 && readIndex < readLength;
		}

		public int getReadBuffer() {
			return hasReadBuffer() ? readLength - ( readIndex + 1 ) : 0;
		}

		public boolean hasReplaceBuffer() {
			return replaceIndex != -1 && replaceIndex < _replaceBytes.length;
		}

		public int getReplaceBuffer() {
			if ( hasReplaceBuffer() ) {
				return _replaceBytes.length - ( replaceIndex + 1 );
			}
			return 0;
		}

		@Override
		public Object clone() {
			return new Status( readBytes, readIndex, readLength, replaceIndex );
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy