package org.pentaho.reporting.engine.classic.core.layout.text;
import org.pentaho.reporting.engine.classic.core.ReportAttributeMap;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableComplexText;
import org.pentaho.reporting.engine.classic.core.metadata.ElementType;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.util.InstanceID;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ComplexTextFactory implements RenderableTextFactory {
public ComplexTextFactory() {
}
public RenderNode[] createText( final int[] text, final int offset, final int length, final StyleSheet layoutContext,
final ElementType elementType, final InstanceID instanceId, final ReportAttributeMap<Object> attributeMap ) {
List<String> strings = processText( text, offset, length );
ArrayList<RenderableComplexText> result = new ArrayList<RenderableComplexText>();
Iterator<String> lines = strings.iterator();
while ( lines.hasNext() ) {
String next = lines.next();
if ( next.isEmpty() && lines.hasNext() == false ) {
// ignore the last line, it acted as indicator for a forced linebreak
continue;
}
RenderableComplexText rct =
new RenderableComplexText( layoutContext, instanceId, elementType, attributeMap, next );
rct.setForceLinebreak( lines.hasNext() );
result.add( rct );
}
return result.toArray( new RenderNode[result.size()] );
}
public RenderNode[] finishText() {
return new RenderNode[0];
}
public void startText() {
}
private enum State {
None, LF, CR
}
/**
* This method breaks text into lines in a strict manor. It accepts CR+LF (windows), CR (old Mac) and LF (Unix) as
* line endings and correctly handles empty lines formed by multiple line-breaks.
*
* @param text
* @param offset
* @param length
* @return
*/
public static List<String> processText( final int[] text, final int offset, final int length ) {
final ArrayList<String> result = new ArrayList<String>();
final int end = offset + length;
int start = offset;
State state = State.None;
for ( int i = offset; i < end; i += 1 ) {
State oldState = state;
int cp = text[i];
switch ( cp ) {
case '\n': {
state = State.LF;
switch ( oldState ) {
case CR: {
// LF+CR causes linebreaks
String txt = new String( text, start, i - start - 1 );
result.add( txt );
start = i + 1;
break;
}
case LF: {
result.add( "" );
start = i + 1;
break;
}
default: {
String txt = new String( text, start, i - start );
result.add( txt );
start = i + 1;
break;
}
}
break;
}
case '\r': {
state = State.CR;
if ( oldState == State.CR ) {
// double CR causes a new line
String txt = new String( text, start, i - start - 1 );
result.add( txt );
start = i + 1;
}
break;
}
default: {
state = State.None;
if ( oldState == State.CR ) {
// A CR causes a delayed new line
String txt = new String( text, start, i - start );
result.add( txt );
start = i;
}
break;
}
}
}
switch ( state ) {
case None:
case LF: {
result.add( new String( text, start, offset + length - start ) );
break;
}
case CR: {
result.add( new String( text, start, offset + length - start - 1 ) );
result.add( "" );
break;
}
}
return result;
}
}