package org.zwobble.mammoth.styles.parsing; import org.parboiled.Action; import org.parboiled.BaseParser; import org.parboiled.Context; import org.parboiled.Rule; import org.parboiled.support.Var; import org.zwobble.mammoth.documents.NumberingLevel; import org.zwobble.mammoth.styles.*; import java.math.BigInteger; import java.util.Optional; import java.util.function.BiFunction; import static org.zwobble.mammoth.util.MammothMaps.map; public class StyleMappingParser extends BaseParser { Rule StyleMapping(Var styleMap) { Var> documentElementMatcher = new Var<>(); Var path = new Var<>(); return Sequence( DocumentElementMatcher(documentElementMatcher), Whitespace(), "=>", Whitespace(), HtmlPath(path), EOI, styleMap.set(documentElementMatcher.get().apply(styleMap.get(), path.get()))); } Rule DocumentElementMatcher(Var> updateBuilder) { Var paragraphMatcher = new Var<>(); Var runMatcher = new Var<>(); return FirstOf( Sequence( ParagraphMatcher(paragraphMatcher), new Action() { @Override public boolean run(Context context) { updateBuilder.set((styleMap, htmlPath) -> styleMap.mapParagraph(paragraphMatcher.get(), htmlPath)); return true; } }), Sequence( RunMatcher(runMatcher), new Action() { @Override public boolean run(Context context) { updateBuilder.set((styleMap, htmlPath) -> styleMap.mapRun(runMatcher.get(), htmlPath)); return true; } }), Sequence( "u", new Action() { @Override public boolean run(Context context) { updateBuilder.set(StyleMapBuilder::underline); return true; } }), Sequence( "s", new Action() { @Override public boolean run(Context context) { updateBuilder.set(StyleMapBuilder::strikethrough); return true; } })); } public Rule ParagraphMatcher(Var matcher) { Var> styleId = new Var<>(); Var> styleName = new Var<>(); Var> numbering = new Var<>(); return Sequence( "p", StyleId(styleId), StyleName(styleName), Numbering(numbering), matcher.set(new ParagraphMatcher(styleId.get(), styleName.get(), numbering.get()))); } public Rule RunMatcher(Var matcher) { Var> styleId = new Var<>(); Var> styleName = new Var<>(); return Sequence( "r", StyleId(styleId), StyleName(styleName), matcher.set(new RunMatcher(styleId.get(), styleName.get()))); } Rule StyleId(Var> styleId) { return Sequence(styleId.set(Optional.empty()), Optional(".", Identifier(), styleId.set(Optional.of(match())))); } Rule StyleName(Var> styleName) { Var value = new Var<>(); return Sequence( styleName.set(Optional.empty()), Optional("[style-name=", SingleQuotedString(value), "]", styleName.set(Optional.of(value.get())))); } Rule Numbering(Var> numbering) { Var isOrdered = new Var<>(); Var level = new Var<>(); return Sequence( numbering.set(Optional.empty()), Optional(Sequence( ":", NumberingType(isOrdered), "(", Number(level), ")", numbering.set(Optional.of(new NumberingLevel(level.get().subtract(BigInteger.ONE).toString(), isOrdered.get())))))); } Rule NumberingType(Var isOrdered) { return FirstOf( Sequence("ordered-list", isOrdered.set(true)), Sequence("unordered-list", isOrdered.set(false))); } Rule Number(Var level) { return Sequence( OneOrMore(CharRange('0', '9')), level.set(new BigInteger(match()))); } public Rule HtmlPath(Var htmlPath) { ListVar elements = new ListVar<>(); return Sequence(Optional(HtmlPathElements(elements)), htmlPath.set(new HtmlPath(elements.build()))); } Rule HtmlPathElements(ListVar elements) { Var element = new Var<>(); return Sequence( HtmlPathElement(element), elements.append(element), ZeroOrMore(Sequence(Whitespace(), Ch('>'), Whitespace(), HtmlPathElement(element), elements.append(element)))); } Rule HtmlPathElement(Var element) { ListVar tagNames = new ListVar<>(); ListVar classNames = new ListVar<>(); Var fresh = new Var<>(); return Sequence( TagNames(tagNames), ClassNames(classNames), Fresh(fresh), element.set(new HtmlPathElement( tagNames.build(), classNames.isEmpty() ? map() : map("class", String.join(" ", classNames.build())), !fresh.get()))); } Rule TagNames(ListVar tagNames) { return Sequence( Identifier(), tagNames.append(match()), ZeroOrMore(Sequence("|", Identifier(), tagNames.append(match())))); } Rule ClassNames(ListVar classNames) { Var className = new Var<>(); return ZeroOrMore(Sequence(ClassName(className), classNames.append(className))); } Rule ClassName(Var className) { return Sequence(".", Identifier(), className.set(match())); } Rule Fresh(Var fresh) { return Sequence(fresh.set(false), Optional(":fresh", fresh.set(true))); } Rule SingleQuotedString(Var value) { return Sequence("'", ZeroOrMore(NoneOf("\'")), value.set(match()), "'"); } Rule Identifier() { return Sequence( FirstOf(CharRange('a', 'z'), CharRange('A', 'Z')), ZeroOrMore(FirstOf(CharRange('a', 'z'), CharRange('A', 'Z'), CharRange('0', '9'), Ch('-'), Ch('_')))); } Rule Whitespace() { return ZeroOrMore(AnyOf(" \t")); } }