package de.axone.web.encoding;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import de.axone.data.tupple.Tripple;
import de.axone.exception.Assert;
import de.axone.tools.S;
public class TranslatingEncoder implements Encoder {
private final String [] tt;
private final char min, max;
public TranslatingEncoder( char [] from, String [] to ) {
Tripple<String[], Character, Character> t = buildTranslationTable( from, to );
this.tt = t.getA();
this.min = t.getB();
this.max = t.getC();
}
protected Tripple<String[], Character, Character> buildTranslationTable( char[] from, String[] to ) {
Map<Character,String> map = new HashMap<>();
char min = Character.MAX_VALUE,
max = Character.MIN_VALUE;
for( int i=0; i<from.length; i++ ){
char c = from[ i ];
if( c < min ) min = c;
if( c > max ) max = c;
map.put( c, to[ i ] );
}
int size = max-min+1;
if( size > 256 ) //Some arbitrary size. This is to prevent major memory leak.
throw new IllegalArgumentException( "Build to big translation table" );
String [] result = new String[ size ];
for( Map.Entry<Character,String> entry : map.entrySet() ){
result[ entry.getKey() - min ] = entry.getValue();
}
return new Tripple<>( result, min, max );
}
@Override
public String encode( CharSequence csq ) {
if( csq == null ) return null;
StringBuilder result = new StringBuilder();
try {
append( result, csq, 0, csq.length() );
} catch( IOException e ) {
throw new RuntimeException( "IO on StringBuffer should not happend",e );
}
return result.toString();
}
private void append( Appendable ap, CharSequence csq, int start, int end ) throws IOException{
int subStart=0;
// iterate over string until there is something to encode
int i;
for( i=start; i<end; i++ ){
char c = csq.charAt( i );
if( c >= min && c <= max ){
String value = tt[ c-min ];
if( value != null ){
// Append chunk
if( i-subStart > 0 )
ap.append( csq.subSequence( subStart, i ) );
// append encoded
ap.append( value );
// remember new start
subStart = i+1;
}
}
}
// Nothing encoded. Append complete string.
if( subStart == 0 ) {
ap.append( csq );
// Append rest of string
} else if( i-subStart > 0 ) {
ap.append( csq.subSequence( subStart, i ) );
}
}
private void append( Appendable ap, char c ) throws IOException{
if( c >= min && c <= max ){
String value = tt[ c-min ];
if( value != null ){
ap.append( value );
return;
}
}
ap.append( c );
}
@Override
public Appendable filter( Appendable sub ) {
return new EncodingAppender( sub );
}
private class EncodingAppender implements Appendable {
private final Appendable sub;
protected EncodingAppender( Appendable out ) {
this.sub = out;
}
@Override
public Appendable append( CharSequence csq ) throws IOException {
if( csq == null ) csq = S._NULL_;
return append( csq, 0, csq.length() );
}
@Override
public Appendable append( CharSequence csq, int start, int end )
throws IOException {
Assert.notNull( csq, "csq" );
Assert.gte( start, "start", 0 );
Assert.lte( start, "start", csq.length() );
Assert.gte( end, "end", start );
Assert.lte( end, "end", csq.length() );
TranslatingEncoder.this.append( sub, csq, start, end );
return this;
}
@Override
public Appendable append( char c ) throws IOException {
TranslatingEncoder.this.append( sub, c );
return this;
}
}
}