package org.jbehave.eclipse.parser;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.jbehave.eclipse.parser.RegexUtils.TokenizerCallback;
public class ContentWithIgnorableEmitter {
public interface Callback<T> {
void emit(T arg, int offset, int length);
void emitIgnorable(int offset, int length);
}
private List<Fragment> fragments;
private int accumulatedDelta = 0;
private int lastFragmentUsed = 0;
private final String content;
public ContentWithIgnorableEmitter(Pattern ignorablePattern, String content) {
this.content = content;
this.fragments = generateFragments(ignorablePattern, content);
}
public List<Fragment> getFragments() {
return fragments;
}
public String contentWithoutIgnorables() {
StringBuilder builder = new StringBuilder();
for (Fragment f : fragments) {
if (!f.isIgnorable) {
builder.append(content.substring(f.startOffset, f.endOffset));
}
}
return builder.toString();
}
public <T> void emitNext(int offsetInIgnoredSpace, int length,
Callback<T> sink, T arg) {
int offset = offsetInIgnoredSpace;
int contentToEmit = length;
for (int i = lastFragmentUsed; i < fragments.size(); i++) {
Fragment f = fragments.get(i);
if (f.isIgnorable) {
sink.emitIgnorable(accumulatedDelta + (offset), f.length());
accumulatedDelta += f.length();
lastFragmentUsed++;
continue;
}
int consumed = f.consume(contentToEmit);
if (consumed > 0) {
sink.emit(arg, accumulatedDelta + (offset), consumed);
offset += consumed;
contentToEmit -= consumed;
}
if (contentToEmit == 0 && f.hasRemaining())
return;
lastFragmentUsed++;
}
// remaining?
if (contentToEmit > 0)
sink.emit(arg, accumulatedDelta + (offsetInIgnoredSpace),
contentToEmit);
}
public static List<Fragment> generateFragments(Pattern ignorablePattern,
String content) {
final List<Fragment> fragments = new ArrayList<Fragment>();
RegexUtils.tokenize(ignorablePattern, content, new TokenizerCallback() {
public void token(int startOffset, int endOffset, String token,
boolean isDelimiter) {
fragments
.add(new Fragment(startOffset, endOffset, isDelimiter));
}
});
return fragments;
}
public static class Fragment {
public final int startOffset;
public final int endOffset;
public final boolean isIgnorable;
public int remaining;
public Fragment(int startOffset, int endOffset, boolean isIgnorable) {
this.startOffset = startOffset;
this.endOffset = endOffset;
this.remaining = isIgnorable ? 0 : length();
this.isIgnorable = isIgnorable;
}
public int length() {
return endOffset - startOffset;
}
public boolean contains(int offset) {
return startOffset <= offset && offset < endOffset;
}
public boolean hasRemaining() {
return remaining > 0;
}
public int consume(int length) {
if (isIgnorable)
return 0;
if (length > remaining) {
int consumed = remaining;
remaining = 0;
return consumed;
}
remaining -= length;
return length;
}
@Override
public String toString() {
return "Fragment [startOffset=" + startOffset + ", endOffset="
+ endOffset + ", isIgnorable=" + isIgnorable
+ ", remaining=" + remaining + "]";
}
}
}