org.refcodes.codec.BaseBuilderImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refcodes-codec Show documentation
Show all versions of refcodes-codec Show documentation
Artifact with encoding and decoding (not in terms of encryption/decryption)
implementations (codecs) such as BASE64 encoding / decoding.
// /////////////////////////////////////////////////////////////////////////////
// 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 - 2025 Weber Informatics LLC | Privacy Policy