package fitnesse.wikitext.parser; import fitnesse.util.StringUtils; import static fitnesse.util.StringUtils.isBlank; public class FrontMatter extends SymbolType implements Rule, Translation { public static final FrontMatter symbolType = new FrontMatter(); public static final SymbolType keyValueSymbolType = new SymbolType("KeyValue"); private static final String FRONT_MATTER_DELIMITER = "---\n"; private static SymbolProvider SYMBOL_PROVIDER = new SymbolProvider(new SymbolType[] { CloseFrontMatter.symbolType, SymbolType.Colon, SymbolType.Whitespace, SymbolType.Newline, SymbolType.Text }); FrontMatter() { super("FrontMatter"); wikiMatcher(new Matcher().startLine().string(FRONT_MATTER_DELIMITER)); wikiRule(this); htmlTranslation(this); } @Override public Maybe<Symbol> parse(Symbol current, Parser parser) { if (current.getStartOffset() != 0) return Symbol.nothing; final Symbol frontMatter = parser.parseToWithSymbols(CloseFrontMatter.symbolType, SYMBOL_PROVIDER, 0); if (!parser.getCurrent().isType(CloseFrontMatter.symbolType)) return Symbol.nothing; Maybe<Symbol> yaml = processYaml(current, frontMatter); if (yaml.isNothing()) { return Symbol.nothing; } return new Maybe<>(current); } private Maybe<Symbol> processYaml(Symbol yaml, Symbol symbolList) { boolean addToPrevious = false; String key = null, value = ""; for (Symbol symbol : symbolList.getChildren()) { if (symbol.isType(SymbolType.Whitespace) && key == null) { addToPrevious = true; } else if (symbol.isType(SymbolType.Text) && key == null) { key = symbol.getContent(); } else if (symbol.isType(SymbolType.Text) || symbol.isType(SymbolType.Whitespace) || (symbol.isType(SymbolType.Colon) && !isBlank(value))) { value += symbol.getContent(); } else if (symbol.isType(SymbolType.Colon)) { // Now start filling value if (key == null) return Symbol.nothing; } else if (symbol.isType(SymbolType.Newline)) { if (key != null) { if (addToPrevious) yaml.getChildren().get(yaml.getChildren().size() - 1).add(yamlLine(key, value.trim())); else yaml.add(yamlLine(key, value.trim())); } key = null; value = ""; addToPrevious = false; } else { // All possible alternatives should be covered now. return Symbol.nothing; } } return new Maybe<>(yaml); } private Symbol yamlLine(final String key, final String value) { return new Symbol(keyValueSymbolType).add(key).add(value); } @Override public String toTarget(Translator translator, Symbol symbol) { return ""; } private static class CloseFrontMatter extends SymbolType { private static final CloseFrontMatter symbolType = new CloseFrontMatter(); private CloseFrontMatter() { super("EndOfFrontMatter"); wikiMatcher(new Matcher().startLine().string(FRONT_MATTER_DELIMITER)); } } }