/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.vertical.adminweb; import java.io.IOException; import java.io.Reader; import java.util.Map; public final class TranslationReader extends Reader { private final static int VARIABLE_LENGTH = 128; private final static int BUFFER_SIZE = 16 * 1024; private final static int STATE_LISTEN = 0; private final static int STATE_VARIABLE = 1; private Map translations; private Reader reader; private char[] buffer = new char[BUFFER_SIZE * 2]; private int offset; private int length; private StringBuffer variable = new StringBuffer( VARIABLE_LENGTH ); private int state = STATE_LISTEN; /** * Creates a new translation writer. * * @param translations * @param reader */ public TranslationReader( Map translations, Reader reader ) { this.translations = translations; this.reader = reader; } /** * @see java.io.Reader#read(char[],int,int) */ public int read( char[] cbuf, int off, int len ) throws IOException { if ( this.length < len ) { int translated = translate(); while ( translated > 0 && this.length < len ) { translated = translate(); } } for ( int i = 0; i < len && i < this.length; i++ ) { cbuf[off + i] = this.buffer[this.offset + i]; } int count = Math.min( len, this.length ); this.offset += count; this.length -= count; if ( count <= 0 ) { return -1; } return count; } private int translate() throws IOException { int initialLength = this.length; // reset buffer if ( this.length == 0 ) { this.offset = 0; } else if ( this.offset > 0 ) { System.arraycopy( this.buffer, this.offset, this.buffer, 0, this.length ); this.offset = 0; } // read untranslated text char[] untranslated = new char[BUFFER_SIZE]; int length = this.reader.read( untranslated ); if ( length < 0 ) { return length; } // resize buffer if necessary if ( this.length + length > ( this.buffer.length - this.buffer.length / 5 ) ) { resizeBuffer( this.length + length ); } for ( int i = 0; i < length; i++ ) { // listen if ( state == STATE_LISTEN ) { if ( untranslated[i] == '%' ) { this.state = STATE_VARIABLE; this.variable.append( '%' ); } else { this.buffer[this.length] = untranslated[i]; this.length++; } } // variable else { // end of variable reached if ( untranslated[i] == '%' ) { if ( this.variable.length() > 1 ) { this.variable.append( untranslated[i] ); String key = this.variable.toString(); String value; if ( this.translations.containsKey( key ) ) { value = (String) this.translations.get( key ); } else { value = key; } // resize buffer if necessary if ( this.length + value.length() > this.buffer.length ) { resizeBuffer( value.length() + length ); } for ( int j = 0; j < value.length(); j++ ) { this.buffer[this.length] = value.charAt( j ); this.length++; } } else { this.buffer[this.length] = '%'; this.length++; this.buffer[this.length] = '%'; this.length++; } this.variable.setLength( 0 ); this.state = STATE_LISTEN; } // variable character saved else if ( validVariableCharacter( this.variable.length() - 1, untranslated[i] ) ) { this.variable.append( untranslated[i] ); } // unexpected end of variable else { for ( int j = 0; j < this.variable.length(); j++ ) { this.buffer[this.length] = this.variable.charAt( j ); this.length++; } this.buffer[this.length] = untranslated[i]; this.length++; this.variable.setLength( 0 ); this.state = STATE_LISTEN; } } } return this.length - initialLength; } private void resizeBuffer( int minLength ) { int increase = ( ( ( minLength - this.buffer.length ) / 1024 ) + 1 ) * 1024; increase = Math.max( increase, this.buffer.length / 10 ); char[] newBuffer = new char[this.buffer.length + increase]; System.arraycopy( this.buffer, this.offset, newBuffer, this.offset, this.length ); this.buffer = newBuffer; } private boolean validVariableCharacter( int i, int c ) { boolean letter = ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) ); boolean number = ( c >= '0' && c <= '9' ); boolean underscore = c == '_'; return ( i == 0 && letter ) || ( i > 0 && ( letter || number || underscore) ); } /** * @see java.io.Reader#close() */ public void close() throws IOException { reader.close(); } }