package org.pentaho.reporting.engine.classic.core.layout.text;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
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;
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;
}
}