package org.jbehave.eclipse.parser;
import java.util.List;
import org.jbehave.eclipse.Keyword;
import org.jbehave.eclipse.editor.step.LocalizedStepSupport;
import org.jbehave.eclipse.util.CharIterator;
import org.jbehave.eclipse.util.CharIterators;
import org.jbehave.eclipse.util.CharTree;
public class VisitingStoryParser {
private final LocalizedStepSupport localizedStepSupport;
public VisitingStoryParser(LocalizedStepSupport localizedStepSupport) {
this.localizedStepSupport = localizedStepSupport;
}
public List<StoryElement> parse(CharSequence content) {
VisitingCollector collector = new VisitingCollector();
parse(content, collector);
return collector.getElements();
}
public void parse(CharSequence content, StoryVisitor visitor) {
parse(CharIterators.createFrom(content), 0, visitor);
}
public void parse(CharIterator it, StoryVisitor visitor) {
parse(it, 0, visitor);
}
public void parse(CharIterator it, int baseOffset, StoryVisitor visitor) {
CharTree<Keyword> tree = localizedStepSupport.getKeywordTree();
int offset = baseOffset;
Line line = new Line();
Block block = new Block();
block.reset(offset);
line.reset(offset);
while (true) {
int read = it.read();
if (read == CharIterator.EOF) {
break;
}
line.append((char) read);
if (isNewlineCharacter(read)) {
if (line.startsWithBreakingKeyword(tree, block)) {
block.emitTo(visitor);
block.reset(line.offset);
}
line.emitTo(block);
// line is reset without any char, offset must be the next one
line.reset(offset + 1);
}
offset++;
}
// remaining
if (line.startsWithBreakingKeyword(tree, block)) {
block.emitTo(visitor);
block.reset(line.offset);
}
line.emitTo(block);
block.emitTo(visitor);
}
private class Block {
private StringBuilder buffer = new StringBuilder();
private int offset;
private Keyword keyword;
public void reset(int offset) {
this.offset = offset;
this.buffer.setLength(0);
}
public void emitTo(StoryVisitor visitor) {
if (buffer.length() > 0) {
String content = buffer.toString();
StoryElement element = new StoryElement(localizedStepSupport, offset,
content);
Keyword extractedKeyword = element.extractKeyword();
if (extractedKeyword != null && extractedKeyword.isStep()) {
if (extractedKeyword == Keyword.And) {
element.setPreferredKeyword(keyword);
} else {
keyword = extractedKeyword;
}
} else {
keyword = null;
}
visitor.visit(element);
}
}
}
private class Line {
private StringBuilder buffer = new StringBuilder();
private int offset;
public void append(char c) {
buffer.append(c);
}
public void reset(int offset) {
this.offset = offset;
this.buffer.setLength(0);
}
public boolean startsWithBreakingKeyword(CharTree<Keyword> tree,
Block block) {
Keyword keyword = tree.lookup(buffer);
if (keyword == null) {
return false;
}
switch (keyword) {
case Ignorable: // comment should not be considered as breaking, but
// must be ignored...
case ExamplesTableHeaderSeparator:
case ExamplesTableValueSeparator:
case ExamplesTableIgnorableSeparator:
return false;
default:
return true;
}
}
public void emitTo(Block block) {
block.buffer.append(buffer);
}
}
private static boolean isNewlineCharacter(int read) {
return read == '\r' || read == '\n';
}
}