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

org.refcodes.codec.BaseBuilderImpl Maven / Gradle / Ivy

Go to download

Artifact with encoding and decoding (not in terms of encryption/decryption) implementations (codecs) such as BASE64 encoding / decoding.

There is a newer version: 3.3.8
Show newest version
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany and licensed
// 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.codec;

import java.util.ArrayList;
import java.util.List;

import org.refcodes.component.CloseException;
import org.refcodes.component.OpenException;
import org.refcodes.data.Binary;
import org.refcodes.data.BitMask;
import org.refcodes.exception.HiddenException;
import org.refcodes.io.ByteArrayConsumer;
import org.refcodes.io.ByteArrayConsumerImpl;
import org.refcodes.io.ByteArrayReceiver;
import org.refcodes.io.ByteArrayReceiverImpl;
import org.refcodes.numerical.NumericalUtility;

/**
 * Vanilla plain implementation of the {@link BaseBuilder} interface.
 */
public class BaseBuilderImpl implements BaseBuilder {

	// /////////////////////////////////////////////////////////////////////////
	// CONSTANTS:
	// /////////////////////////////////////////////////////////////////////////

	private static final boolean IS_USE_SINGLE_CODE_BASE = false;

	// /////////////////////////////////////////////////////////////////////////
	// STATICS:
	// /////////////////////////////////////////////////////////////////////////

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

	private BaseMetrics _baseCodecMetrics = null;

	private String _encodedText = null;

	private byte[] _decodedData = null;

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

	// /////////////////////////////////////////////////////////////////////////
	// INJECTION:
	// /////////////////////////////////////////////////////////////////////////

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public BaseMetrics getBaseMetrics() {
		return _baseCodecMetrics;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setBaseMetrics( BaseMetrics aBaseMetrics ) {
		_baseCodecMetrics = aBaseMetrics;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getEncodedText() {
		if ( IS_USE_SINGLE_CODE_BASE ) {
			_encodedText = toEncodedText( _decodedData );
		}
		else {
			_encodedText = toEncodedText( _decodedData, _baseCodecMetrics );
		}
		_decodedData = null;
		return _encodedText;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setEncodedText( String aEncodedText ) {
		_encodedText = aEncodedText;
		_decodedData = null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public byte[] getDecodedData() {
		if ( IS_USE_SINGLE_CODE_BASE ) {
			_decodedData = toDecodedData( _encodedText );
		}
		else {
			_decodedData = toDecodedData( _encodedText, _baseCodecMetrics );
		}
		_encodedText = null;
		return _decodedData;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setDecodedData( byte[] aDecodedData ) {
		_decodedData = aDecodedData;
		_encodedText = null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setDecodedData( long aDecodedData ) {
		setDecodedData( NumericalUtility.toBytes( aDecodedData ) );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toEncodedText( byte[] aDecodedData ) {
		if ( IS_USE_SINGLE_CODE_BASE ) {
			ByteArrayConsumer theConsumer = new ByteArrayConsumerImpl();
			BaseEncoder theEncoder = new BaseEncoderImpl( theConsumer ).withBaseMetrics( _baseCodecMetrics );
			try {
				theEncoder.writeDatagrams( aDecodedData );
				theEncoder.close();
			}
			catch ( OpenException | CloseException e ) {
				throw new HiddenException( e );
			}
			return new String( theConsumer.getBytes() );
		}
		else {
			return toEncodedText( aDecodedData, _baseCodecMetrics );
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public byte[] toDecodedData( String aEncodedText ) {
		if ( IS_USE_SINGLE_CODE_BASE ) {
			ByteArrayReceiver theProvider = new ByteArrayReceiverImpl( aEncodedText.getBytes() );
			try {
				BaseDecoder theDecoder = new BaseDecoderImpl( theProvider ).withBaseMetrics( _baseCodecMetrics );
				List theDecodedBytes = new ArrayList<>();
				byte[] eDecodedBytes;
				while ( theDecoder.hasDatagram() ) {
					eDecodedBytes = theDecoder.readDatagrams();
					for ( Byte eByte : eDecodedBytes ) {
						theDecodedBytes.add( eByte );
					}
				}
				theDecoder.close();
				return toPrimitiveType( theDecodedBytes.toArray( new Byte[theDecodedBytes.size()] ) );
			}
			catch ( OpenException | CloseException | InterruptedException e ) {
				throw new HiddenException( e );
			}
		}
		else {
			return toDecodedData( aEncodedText, _baseCodecMetrics );
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toEncodedText( long aDecodedData ) {
		return toEncodedText( NumericalUtility.toBytes( aDecodedData ) );
	}

	// /////////////////////////////////////////////////////////////////////////
	// HOOKS:
	// /////////////////////////////////////////////////////////////////////////

	// -------------------------------------------------------------------------
	// ENCODING:
	// -------------------------------------------------------------------------

	/**
	 * To encoded text.
	 *
	 * @param aDecodedData the decoded data
	 * @param aBaseMetrics the base metrics
	 * @return the string
	 */
	protected static String toEncodedText( byte[] aDecodedData, BaseMetrics aBaseMetrics ) {
		int theMod = aDecodedData.length % aBaseMetrics.getBytesPerInt();
		int theTrailingBytes = 0;
		int theEncodedSize = toEncodedSize( aDecodedData, aBaseMetrics );
		char[] theEncodedText = new char[theEncodedSize];
		int theIndex = 0;
		int eTrailingUnused;
		for ( int theOffset = 0; theOffset < aDecodedData.length; theOffset += aBaseMetrics.getBytesPerInt() ) {
			int eWord = toWord( aDecodedData, theOffset, aBaseMetrics );
			if ( theOffset + aBaseMetrics.getBytesPerInt() >= aDecodedData.length ) {
				theTrailingBytes = (theMod == 0) ? 0 : aBaseMetrics.getBytesPerInt() - theMod;
			}
			eTrailingUnused = Binary.BITS_PER_BYTE.getValue() * (Binary.BYTES_PER_INT.getValue() - aBaseMetrics.getBytesPerInt());
			eWord <<= eTrailingUnused;
			for ( int i = 0; i < aBaseMetrics.getDigitsPerInt() - theTrailingBytes; i++ ) {
				int eByte = (eWord >> (Binary.BITS_PER_INT.getValue() - aBaseMetrics.getBitsPerDigit())) & aBaseMetrics.getDigitMask();
				theEncodedText[theIndex++] = aBaseMetrics.toChar( eByte );
				eWord <<= aBaseMetrics.getBitsPerDigit();
			}
			for ( int i = 0; i < theTrailingBytes; i++ ) {
				theEncodedText[theIndex++] = aBaseMetrics.getPaddingChar();
			}
		}
		String theResult = new String( theEncodedText );
		return theResult;
	}

	/**
	 * To encoded size.
	 *
	 * @param aDecodedData the decoded data
	 * @param aBaseMetrics the base metrics
	 * @return the int
	 */
	private static int toEncodedSize( byte[] aDecodedData, BaseMetrics aBaseMetrics ) {
		int theMod = aDecodedData.length % aBaseMetrics.getBytesPerInt();
		int theTrailingBytes = (theMod == 0) ? 0 : aBaseMetrics.getBytesPerInt() - theMod;
		double d = ((double) aBaseMetrics.getDigitsPerInt()) / ((double) aBaseMetrics.getBytesPerInt());
		int theAdd = (int) Math.ceil( d * theTrailingBytes );
		return aDecodedData.length * aBaseMetrics.getDigitsPerInt() / aBaseMetrics.getBytesPerInt() + theAdd;
	}

	/**
	 * To word.
	 *
	 * @param aDecodedData the decoded data
	 * @param aOffset the offset
	 * @param aBaseMetrics the base metrics
	 * @return the int
	 */
	private static int toWord( byte[] aDecodedData, int aOffset, BaseMetrics aBaseMetrics ) {
		int eWord = 0;
		for ( int i = 0; i < aBaseMetrics.getBytesPerInt(); i++ ) {
			eWord <<= Binary.BITS_PER_BYTE.getValue();
			if ( aOffset + i < aDecodedData.length ) {
				eWord |= aDecodedData[aOffset + i] & BitMask.MASK_8.getValue();
			}
		}
		return eWord;
	}

	// -------------------------------------------------------------------------
	// DECODING:
	// -------------------------------------------------------------------------

	/**
	 * To decoded data.
	 *
	 * @param aEncodedText the encoded text
	 * @param aBaseMetrics the base metrics
	 * @return the byte[]
	 */
	protected static byte[] toDecodedData( String aEncodedText, BaseMetrics aBaseMetrics ) {
		if ( aEncodedText.length() % aBaseMetrics.getDigitsPerInt() != 0 ) throw new IllegalArgumentException( "The length of <" + aEncodedText.length() + "> of the encoded text cannot be divided (modulo = 0) by <" + aBaseMetrics.getDigitsPerByte() + "> which is required by the codec <" + aBaseMetrics + ">." );
		int thePaddingIndex = aEncodedText.indexOf( aBaseMetrics.getPaddingChar() );
		int theTrailingBytes = 0;
		int theDecodedSize = toDecodedSize( aEncodedText, aBaseMetrics );
		byte[] theDecodedData = new byte[theDecodedSize];
		int theIndex = 0;
		for ( int theOffset = 0; theOffset < aEncodedText.length(); theOffset += aBaseMetrics.getDigitsPerInt() ) {
			if ( theOffset + aBaseMetrics.getDigitsPerInt() >= aEncodedText.length() ) {
				theTrailingBytes = thePaddingIndex > 0 ? (aEncodedText.length() - thePaddingIndex) : 0;
			}
			int eWord = 0;
			for ( int i = 0; i < aBaseMetrics.getDigitsPerInt(); i++ ) {
				eWord <<= aBaseMetrics.getBitsPerDigit();
				eWord |= aBaseMetrics.toValue( aEncodedText.charAt( theOffset + i ) ) & BitMask.MASK_8.getValue();
			}
			theIndex = toBytes( theDecodedData, theIndex, eWord, theTrailingBytes, aBaseMetrics );
		}
		return theDecodedData;
	}

	/**
	 * To decoded size.
	 *
	 * @param aEncodedText the encoded text
	 * @param aBaseMetrics the base metrics
	 * @return the int
	 */
	protected static int toDecodedSize( String aEncodedText, BaseMetrics aBaseMetrics ) {
		int thePaddingIndex = aEncodedText.indexOf( aBaseMetrics.getPaddingChar() );
		int theTrailingDigits = (thePaddingIndex == -1) ? 0 : aEncodedText.length() - thePaddingIndex;
		return aEncodedText.length() * aBaseMetrics.getBytesPerInt() / aBaseMetrics.getDigitsPerInt() - theTrailingDigits;
	}

	/**
	 * To bytes.
	 *
	 * @param aDecodedBytes the decoded bytes
	 * @param aOffset the offset
	 * @param aWord the word
	 * @param aTrailingBytes the trailing bytes
	 * @param aBaseMetrics the base metrics
	 * @return the int
	 */
	protected static int toBytes( byte[] aDecodedBytes, int aOffset, int aWord, int aTrailingBytes, BaseMetrics aBaseMetrics ) {
		for ( int i = 0; i < aBaseMetrics.getBytesPerInt(); i++ ) {
			if ( i >= aTrailingBytes ) {
				int theIndex = aOffset + aBaseMetrics.getBytesPerInt() - i - 1;
				aDecodedBytes[theIndex] = (byte) aWord;
			}
			aWord >>= Binary.BITS_PER_BYTE.getValue();
		}
		return aOffset + aBaseMetrics.getBytesPerInt();
	}

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

	private static byte[] toPrimitiveType( Byte[] aBytes ) {
		if ( aBytes == null ) return null;
		byte[] thePrimitives = new byte[aBytes.length];
		for ( int i = 0; i < aBytes.length; i++ ) {
			thePrimitives[i] = aBytes[i].byteValue();
		}
		return thePrimitives;
	}

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

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy