InstrumentedDocumentProcessor.java
package org.cyclopsgroup.caff.dp;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
public class InstrumentedDocumentProcessor implements DocumentProcessor {
private final List<Instrument> instruments;
public InstrumentedDocumentProcessor(List<Instrument> instruments) {
this.instruments = Collections.unmodifiableList(new ArrayList<Instrument>(instruments));
}
private void doProcess(LineNumberReader input, PrintWriter output) throws IOException {
Deque<Instrument> stack = new ArrayDeque<Instrument>();
for (String line = input.readLine(); line != null; line = input.readLine()) {
try {
processSegment(Instrument.LINE_START + line, output, stack);
} catch (RuntimeException e) {
throw new IOException("Can't parse line " + input.getLineNumber() + ":" + line, e);
}
}
}
@Override
public void process(Reader input, Writer output) throws IOException {
LineNumberReader in;
if (input instanceof LineNumberReader) {
in = (LineNumberReader) input;
} else {
in = new LineNumberReader(input);
}
PrintWriter out;
if (output instanceof PrintWriter) {
out = (PrintWriter) output;
} else {
out = new PrintWriter(output);
}
doProcess(in, out);
}
private void processSegment(String segment, PrintWriter out, Deque<Instrument> stack)
throws IOException {
int position = segment.length() + 1;
Instrument selected = null;
boolean toClose = false;
for (Instrument i : instruments) {
int p = i.searchToOpen(segment, stack.peek());
if (p == -1) {
continue;
}
if (p < position) {
selected = i;
position = p;
}
}
if (stack.peek() != null) {
int p = stack.peek().searchToClose(segment);
if (p != -1 && p < position) {
selected = stack.peek();
toClose = true;
position = p;
}
}
if (selected == null) {
writeText(segment, stack, out);
return;
}
if (position > 0) {
writeText(segment.substring(0, position), stack, out);
}
int consumed;
if (toClose) {
consumed = stack.pop().close(segment.substring(position), out);
} else {
consumed = selected.open(segment.substring(position), out);
stack.push(selected);
}
if (position + consumed < segment.length()) {
processSegment(segment.substring(position + consumed), out, stack);
}
}
private void writeText(String text, Deque<Instrument> stack, PrintWriter output) {
if (stack.peek() == null) {
output.print(StringUtils.removeStart(text, Instrument.LINE_START));
} else {
stack.peek().printText(text, output);
}
}
}