org.refcodes.io.ReplaceInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refcodes-io Show documentation
Show all versions of refcodes-io Show documentation
Artifact with commonly used I/O functionality and for connection related
issues such as receiving or transmitting data in a unified way.
// /////////////////////////////////////////////////////////////////////////////
// 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 );
}
}
}