aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/zwobble/mammoth/internal/styles
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2016-02-24 08:16:16 +0000
committerMichael Williamson <mike@zwobble.org>2016-02-24 08:16:16 +0000
commit3e2786a004eddf968a5fa7d5a92dff5b1b5f7734 (patch)
tree7fa1bcc1e484cd0ef782f64baf504ee83333d397 /src/main/java/org/zwobble/mammoth/internal/styles
parent0dc3a12affc2e46c1a0df2899cf51a1ae3fdbd61 (diff)
Create internal package
Diffstat (limited to 'src/main/java/org/zwobble/mammoth/internal/styles')
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/DocumentElementMatcher.java5
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/DocumentElementMatching.java26
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/HtmlPath.java51
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/HtmlPathElement.java38
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/ParagraphMatcher.java50
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/RunMatcher.java30
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/StyleMap.java57
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/StyleMapBuilder.java41
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/StyleMapping.java19
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/parsing/ListVar.java34
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/parsing/Parsing.java21
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/parsing/StyleMapParser.java34
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/styles/parsing/StyleMappingParser.java178
13 files changed, 584 insertions, 0 deletions
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/DocumentElementMatcher.java b/src/main/java/org/zwobble/mammoth/internal/styles/DocumentElementMatcher.java
new file mode 100644
index 0000000..697926c
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/DocumentElementMatcher.java
@@ -0,0 +1,5 @@
+package org.zwobble.mammoth.internal.styles;
+
+public interface DocumentElementMatcher<T> {
+ boolean matches(T element);
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/DocumentElementMatching.java b/src/main/java/org/zwobble/mammoth/internal/styles/DocumentElementMatching.java
new file mode 100644
index 0000000..a0c5aec
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/DocumentElementMatching.java
@@ -0,0 +1,26 @@
+package org.zwobble.mammoth.internal.styles;
+
+import org.zwobble.mammoth.internal.documents.Style;
+
+import java.util.Optional;
+import java.util.function.BiPredicate;
+
+class DocumentElementMatching {
+ static boolean matchesStyle(Optional<String> styleId, Optional<String> styleName, Optional<Style> style) {
+ return matchesStyleId(styleId, style) && matchesStyleName(styleName, style);
+ }
+
+ private static boolean matchesStyleId(Optional<String> styleId, Optional<Style> style) {
+ return matches(styleId, style.map(Style::getStyleId), Object::equals);
+ }
+
+ private static boolean matchesStyleName(Optional<String> styleName, Optional<Style> style) {
+ return matches(styleName, style.flatMap(Style::getName), String::equalsIgnoreCase);
+ }
+
+ static <T> boolean matches(Optional<T> required, Optional<T> actual, BiPredicate<T, T> areEqual) {
+ return required
+ .map(requiredValue -> actual.map(actualValue -> areEqual.test(requiredValue, actualValue)).orElse(false))
+ .orElse(true);
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/HtmlPath.java b/src/main/java/org/zwobble/mammoth/internal/styles/HtmlPath.java
new file mode 100644
index 0000000..a20043a
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/HtmlPath.java
@@ -0,0 +1,51 @@
+package org.zwobble.mammoth.internal.styles;
+
+import com.google.common.collect.Lists;
+import org.zwobble.mammoth.internal.html.HtmlNode;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.zwobble.mammoth.internal.util.MammothLists.list;
+import static org.zwobble.mammoth.internal.util.MammothMaps.map;
+
+public class HtmlPath {
+ public static final HtmlPath EMPTY = new HtmlPath(list());
+
+ public static HtmlPath element(String tagName) {
+ return element(tagName, map());
+ }
+
+ public static HtmlPath element(String tagName, Map<String, String> attributes) {
+ return new HtmlPath(list(new HtmlPathElement(list(tagName), attributes, false)));
+ }
+
+ public static HtmlPath collapsibleElement(String tagName) {
+ return collapsibleElement(tagName, map());
+ }
+
+ public static HtmlPath collapsibleElement(List<String> tagNames) {
+ return collapsibleElement(tagNames, map());
+ }
+
+ public static HtmlPath collapsibleElement(String tagName, Map<String, String> attributes) {
+ return collapsibleElement(list(tagName), attributes);
+ }
+
+ public static HtmlPath collapsibleElement(List<String> tagNames, Map<String, String> attributes) {
+ return new HtmlPath(list(new HtmlPathElement(tagNames, attributes, true)));
+ }
+
+ private final List<HtmlPathElement> elements;
+
+ public HtmlPath(List<HtmlPathElement> elements) {
+ this.elements = elements;
+ }
+
+ public List<HtmlNode> wrap(List<HtmlNode> nodes) {
+ for (HtmlPathElement element : Lists.reverse(elements)) {
+ nodes = element.wrap(nodes);
+ }
+ return nodes;
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/HtmlPathElement.java b/src/main/java/org/zwobble/mammoth/internal/styles/HtmlPathElement.java
new file mode 100644
index 0000000..7f110a4
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/HtmlPathElement.java
@@ -0,0 +1,38 @@
+package org.zwobble.mammoth.internal.styles;
+
+import org.zwobble.mammoth.internal.html.HtmlElement;
+import org.zwobble.mammoth.internal.html.HtmlNode;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.zwobble.mammoth.internal.util.MammothLists.list;
+import static org.zwobble.mammoth.internal.util.MammothMaps.map;
+
+public class HtmlPathElement {
+ public static HtmlPathElement fresh(String tagName) {
+ return new HtmlPathElement(list(tagName), map(), false);
+ }
+
+ public static HtmlPathElement collapsible(String tagName) {
+ return collapsible(tagName, map());
+ }
+
+ public static HtmlPathElement collapsible(String tagName, Map<String, String> attributes) {
+ return new HtmlPathElement(list(tagName), attributes, true);
+ }
+
+ private final List<String> tagNames;
+ private final Map<String, String> attributes;
+ private final boolean isCollapsible;
+
+ public HtmlPathElement(List<String> tagNames, Map<String, String> attributes, boolean isCollapsible) {
+ this.tagNames = tagNames;
+ this.attributes = attributes;
+ this.isCollapsible = isCollapsible;
+ }
+
+ public List<HtmlNode> wrap(List<HtmlNode> nodes) {
+ return list(new HtmlElement(tagNames, attributes, nodes, isCollapsible));
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/ParagraphMatcher.java b/src/main/java/org/zwobble/mammoth/internal/styles/ParagraphMatcher.java
new file mode 100644
index 0000000..50d5df9
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/ParagraphMatcher.java
@@ -0,0 +1,50 @@
+package org.zwobble.mammoth.internal.styles;
+
+import org.zwobble.mammoth.internal.documents.NumberingLevel;
+import org.zwobble.mammoth.internal.documents.Paragraph;
+
+import java.util.Optional;
+
+public class ParagraphMatcher implements DocumentElementMatcher<Paragraph> {
+ public static final ParagraphMatcher ANY = new ParagraphMatcher(Optional.empty(), Optional.empty(), Optional.empty());
+
+ public static ParagraphMatcher styleId(String styleId) {
+ return new ParagraphMatcher(Optional.of(styleId), Optional.empty(), Optional.empty());
+ }
+
+ public static ParagraphMatcher styleName(String styleName) {
+ return new ParagraphMatcher(Optional.empty(), Optional.of(styleName), Optional.empty());
+ }
+
+ public static ParagraphMatcher orderedList(String level) {
+ return new ParagraphMatcher(Optional.empty(), Optional.empty(), Optional.of(NumberingLevel.ordered(level)));
+ }
+
+ public static ParagraphMatcher unorderedList(String level) {
+ return new ParagraphMatcher(Optional.empty(), Optional.empty(), Optional.of(NumberingLevel.unordered(level)));
+ }
+
+ private final Optional<String> styleId;
+ private final Optional<String> styleName;
+ private final Optional<NumberingLevel> numbering;
+
+ public ParagraphMatcher(Optional<String> styleId, Optional<String> styleName, Optional<NumberingLevel> numbering) {
+ this.styleId = styleId;
+ this.styleName = styleName;
+ this.numbering = numbering;
+ }
+
+ @Override
+ public boolean matches(Paragraph paragraph) {
+ return matchesStyle(paragraph) && matchesNumbering(paragraph);
+ }
+
+ private boolean matchesStyle(Paragraph paragraph) {
+ return DocumentElementMatching.matchesStyle(styleId, styleName, paragraph.getStyle());
+ }
+
+ private boolean matchesNumbering(Paragraph paragraph) {
+ return DocumentElementMatching.matches(numbering, paragraph.getNumbering(), (first, second) ->
+ first.isOrdered() == second.isOrdered() && first.getLevelIndex().equalsIgnoreCase(second.getLevelIndex()));
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/RunMatcher.java b/src/main/java/org/zwobble/mammoth/internal/styles/RunMatcher.java
new file mode 100644
index 0000000..93b50dd
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/RunMatcher.java
@@ -0,0 +1,30 @@
+package org.zwobble.mammoth.internal.styles;
+
+import org.zwobble.mammoth.internal.documents.Run;
+
+import java.util.Optional;
+
+public class RunMatcher implements DocumentElementMatcher<Run> {
+ public static final RunMatcher ANY = new RunMatcher(Optional.empty(), Optional.empty());
+
+ public static RunMatcher styleId(String styleId) {
+ return new RunMatcher(Optional.of(styleId), Optional.empty());
+ }
+
+ public static RunMatcher styleName(String styleName) {
+ return new RunMatcher(Optional.empty(), Optional.of(styleName));
+ }
+
+ private final Optional<String> styleId;
+ private final Optional<String> styleName;
+
+ public RunMatcher(Optional<String> styleId, Optional<String> styleName) {
+ this.styleId = styleId;
+ this.styleName = styleName;
+ }
+
+ @Override
+ public boolean matches(Run run) {
+ return DocumentElementMatching.matchesStyle(styleId, styleName, run.getStyle());
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/StyleMap.java b/src/main/java/org/zwobble/mammoth/internal/styles/StyleMap.java
new file mode 100644
index 0000000..7d28bf1
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/StyleMap.java
@@ -0,0 +1,57 @@
+package org.zwobble.mammoth.internal.styles;
+
+import com.google.common.collect.Iterables;
+import org.zwobble.mammoth.internal.documents.Paragraph;
+import org.zwobble.mammoth.internal.documents.Run;
+
+import java.util.List;
+import java.util.Optional;
+
+public class StyleMap {
+ public static StyleMapBuilder builder() {
+ return new StyleMapBuilder();
+ }
+
+ public static final StyleMap EMPTY = new StyleMapBuilder().build();
+
+ private final HtmlPath underline;
+ private final HtmlPath strikethrough;
+ private final List<StyleMapping<Paragraph>> paragraphStyles;
+ private final List<StyleMapping<Run>> runStyles;
+
+ public StyleMap(
+ HtmlPath underline,
+ HtmlPath strikethrough,
+ List<StyleMapping<Paragraph>> paragraphStyles,
+ List<StyleMapping<Run>> runStyles)
+ {
+ this.underline = underline;
+ this.strikethrough = strikethrough;
+ this.paragraphStyles = paragraphStyles;
+ this.runStyles = runStyles;
+ }
+
+ public HtmlPath getUnderline() {
+ return underline;
+ }
+
+ public HtmlPath getStrikethrough() {
+ return strikethrough;
+ }
+
+ public Optional<HtmlPath> getParagraphHtmlPath(Paragraph paragraph) {
+ com.google.common.base.Optional<StyleMapping<Paragraph>> mapping = Iterables.tryFind(
+ paragraphStyles,
+ styleMapping -> styleMapping.matches(paragraph));
+ return Optional.ofNullable(mapping.orNull())
+ .map(StyleMapping::getHtmlPath);
+ }
+
+ public Optional<HtmlPath> getRunHtmlPath(Run run) {
+ com.google.common.base.Optional<StyleMapping<Run>> mapping = Iterables.tryFind(
+ runStyles,
+ styleMapping -> styleMapping.matches(run));
+ return Optional.ofNullable(mapping.orNull())
+ .map(StyleMapping::getHtmlPath);
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/StyleMapBuilder.java b/src/main/java/org/zwobble/mammoth/internal/styles/StyleMapBuilder.java
new file mode 100644
index 0000000..6300d29
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/StyleMapBuilder.java
@@ -0,0 +1,41 @@
+package org.zwobble.mammoth.internal.styles;
+
+import com.google.common.collect.ImmutableList;
+import org.zwobble.mammoth.internal.documents.Paragraph;
+import org.zwobble.mammoth.internal.documents.Run;
+
+public class StyleMapBuilder {
+ private HtmlPath underline;
+ private HtmlPath strikethrough;
+ private final ImmutableList.Builder<StyleMapping<Paragraph>> paragraphStyles = ImmutableList.builder();
+ private final ImmutableList.Builder<StyleMapping<Run>> runStyles = ImmutableList.builder();
+
+ public StyleMapBuilder() {
+ this.underline = HtmlPath.EMPTY;
+ this.strikethrough = HtmlPath.collapsibleElement("s");
+ }
+
+ public StyleMapBuilder underline(HtmlPath path) {
+ this.underline = path;
+ return this;
+ }
+
+ public StyleMapBuilder strikethrough(HtmlPath path) {
+ this.strikethrough = path;
+ return this;
+ }
+
+ public StyleMapBuilder mapParagraph(ParagraphMatcher matcher, HtmlPath path) {
+ paragraphStyles.add(new StyleMapping<Paragraph>(matcher, path));
+ return this;
+ }
+
+ public StyleMapBuilder mapRun(RunMatcher matcher, HtmlPath path) {
+ runStyles.add(new StyleMapping<Run>(matcher, path));
+ return this;
+ }
+
+ public StyleMap build() {
+ return new StyleMap(underline, strikethrough, paragraphStyles.build(), runStyles.build());
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/StyleMapping.java b/src/main/java/org/zwobble/mammoth/internal/styles/StyleMapping.java
new file mode 100644
index 0000000..5512c22
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/StyleMapping.java
@@ -0,0 +1,19 @@
+package org.zwobble.mammoth.internal.styles;
+
+public class StyleMapping<T> {
+ private final DocumentElementMatcher<T> matcher;
+ private final HtmlPath htmlPath;
+
+ public StyleMapping(DocumentElementMatcher<T> matcher, HtmlPath htmlPath) {
+ this.matcher = matcher;
+ this.htmlPath = htmlPath;
+ }
+
+ public boolean matches(T element) {
+ return matcher.matches(element);
+ }
+
+ public HtmlPath getHtmlPath() {
+ return htmlPath;
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/parsing/ListVar.java b/src/main/java/org/zwobble/mammoth/internal/styles/parsing/ListVar.java
new file mode 100644
index 0000000..74b3dbc
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/parsing/ListVar.java
@@ -0,0 +1,34 @@
+package org.zwobble.mammoth.internal.styles.parsing;
+
+
+import com.google.common.collect.ImmutableList;
+import org.parboiled.support.Var;
+
+import java.util.List;
+
+import static org.zwobble.mammoth.internal.util.MammothLists.list;
+
+class ListVar<T> extends Var<ImmutableList.Builder<T>> {
+ boolean append(Var<T> element) {
+ return append(element.get());
+ }
+
+ boolean append(T element) {
+ if (get() == null) {
+ set(ImmutableList.builder());
+ }
+ return set(get().add(element));
+ }
+
+ public List<T> build() {
+ if (get() == null) {
+ return list();
+ } else {
+ return get().build();
+ }
+ }
+
+ public boolean isEmpty() {
+ return get() == null;
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/parsing/Parsing.java b/src/main/java/org/zwobble/mammoth/internal/styles/parsing/Parsing.java
new file mode 100644
index 0000000..bef3c0b
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/parsing/Parsing.java
@@ -0,0 +1,21 @@
+package org.zwobble.mammoth.internal.styles.parsing;
+
+import org.parboiled.BaseParser;
+import org.parboiled.Parboiled;
+import org.parboiled.Rule;
+import org.parboiled.parserunners.ReportingParseRunner;
+import org.parboiled.support.ParsingResult;
+
+import java.util.function.Function;
+
+public class Parsing {
+ public static <T, U extends BaseParser<T>, V> void parse(Class<U> parserType, Function<U, Rule> rule, String value) {
+ U parser = Parboiled.createParser(parserType);
+ ReportingParseRunner<T> runner = new ReportingParseRunner<>(rule.apply(parser));
+ ParsingResult<T> result = runner.run(value);
+ if (result.hasErrors()) {
+ // TODO: wrap in result
+ throw new RuntimeException("Parse error");
+ }
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/parsing/StyleMapParser.java b/src/main/java/org/zwobble/mammoth/internal/styles/parsing/StyleMapParser.java
new file mode 100644
index 0000000..b43e300
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/parsing/StyleMapParser.java
@@ -0,0 +1,34 @@
+package org.zwobble.mammoth.internal.styles.parsing;
+
+import org.parboiled.support.Var;
+import org.zwobble.mammoth.internal.styles.StyleMap;
+import org.zwobble.mammoth.internal.styles.StyleMapBuilder;
+
+import java.util.List;
+
+import static java.util.Arrays.asList;
+
+public class StyleMapParser {
+ public static StyleMap parse(String input) {
+ return parse(asList(input.split("\\r?\\n")));
+ }
+
+ public static StyleMap parse(List<String> lines) {
+ Var<StyleMapBuilder> styleMap = new Var<>(StyleMap.builder());
+ for (String line : lines) {
+ handleLine(styleMap, line);
+ }
+ return styleMap.get().build();
+ }
+
+ private static void handleLine(Var<StyleMapBuilder> styleMap, String line) {
+ if (line.startsWith("#")) {
+ return;
+ }
+ line = line.trim();
+ if (line.isEmpty()) {
+ return;
+ }
+ Parsing.parse(StyleMappingParser.class, parser -> parser.StyleMapping(styleMap), line);
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/styles/parsing/StyleMappingParser.java b/src/main/java/org/zwobble/mammoth/internal/styles/parsing/StyleMappingParser.java
new file mode 100644
index 0000000..5c1500c
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/styles/parsing/StyleMappingParser.java
@@ -0,0 +1,178 @@
+package org.zwobble.mammoth.internal.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.internal.documents.NumberingLevel;
+import org.zwobble.mammoth.internal.styles.*;
+
+import java.math.BigInteger;
+import java.util.Optional;
+import java.util.function.BiFunction;
+
+import static org.zwobble.mammoth.internal.util.MammothMaps.map;
+
+public class StyleMappingParser extends BaseParser<StyleMapBuilder> {
+ Rule StyleMapping(Var<StyleMapBuilder> styleMap) {
+ Var<BiFunction<StyleMapBuilder, HtmlPath, StyleMapBuilder>> documentElementMatcher = new Var<>();
+ Var<HtmlPath> path = new Var<>();
+ return Sequence(
+ DocumentElementMatcher(documentElementMatcher), Whitespace(), "=>", Whitespace(), HtmlPath(path), EOI,
+ styleMap.set(documentElementMatcher.get().apply(styleMap.get(), path.get())));
+ }
+
+ Rule DocumentElementMatcher(Var<BiFunction<StyleMapBuilder, HtmlPath, StyleMapBuilder>> updateBuilder) {
+ Var<ParagraphMatcher> paragraphMatcher = new Var<>();
+ Var<RunMatcher> 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<ParagraphMatcher> matcher) {
+ Var<Optional<String>> styleId = new Var<>();
+ Var<Optional<String>> styleName = new Var<>();
+ Var<Optional<NumberingLevel>> 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<RunMatcher> matcher) {
+ Var<Optional<String>> styleId = new Var<>();
+ Var<Optional<String>> styleName = new Var<>();
+ return Sequence(
+ "r", StyleId(styleId), StyleName(styleName),
+ matcher.set(new RunMatcher(styleId.get(), styleName.get())));
+ }
+
+ Rule StyleId(Var<Optional<String>> styleId) {
+ return Sequence(styleId.set(Optional.empty()), Optional(".", Identifier(), styleId.set(Optional.of(match()))));
+ }
+
+ Rule StyleName(Var<Optional<String>> styleName) {
+ Var<String> value = new Var<>();
+ return Sequence(
+ styleName.set(Optional.empty()),
+ Optional("[style-name=", SingleQuotedString(value), "]", styleName.set(Optional.of(value.get()))));
+ }
+
+ Rule Numbering(Var<Optional<NumberingLevel>> numbering) {
+ Var<Boolean> isOrdered = new Var<>();
+ Var<BigInteger> 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<Boolean> isOrdered) {
+ return FirstOf(
+ Sequence("ordered-list", isOrdered.set(true)),
+ Sequence("unordered-list", isOrdered.set(false)));
+ }
+
+ Rule Number(Var<BigInteger> level) {
+ return Sequence(
+ OneOrMore(CharRange('0', '9')),
+ level.set(new BigInteger(match())));
+ }
+
+ public Rule HtmlPath(Var<HtmlPath> htmlPath) {
+ ListVar<HtmlPathElement> elements = new ListVar<>();
+ return Sequence(Optional(HtmlPathElements(elements)), htmlPath.set(new HtmlPath(elements.build())));
+ }
+
+ Rule HtmlPathElements(ListVar<HtmlPathElement> elements) {
+ Var<HtmlPathElement> element = new Var<>();
+ return Sequence(
+ HtmlPathElement(element),
+ elements.append(element),
+ ZeroOrMore(Sequence(Whitespace(), Ch('>'), Whitespace(), HtmlPathElement(element), elements.append(element))));
+ }
+
+ Rule HtmlPathElement(Var<HtmlPathElement> element) {
+ ListVar<String> tagNames = new ListVar<>();
+ ListVar<String> classNames = new ListVar<>();
+ Var<Boolean> 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<String> tagNames) {
+ return Sequence(
+ Identifier(),
+ tagNames.append(match()),
+ ZeroOrMore(Sequence("|", Identifier(), tagNames.append(match()))));
+ }
+
+ Rule ClassNames(ListVar<String> classNames) {
+ Var<String> className = new Var<>();
+ return ZeroOrMore(Sequence(ClassName(className), classNames.append(className)));
+ }
+
+ Rule ClassName(Var<String> className) {
+ return Sequence(".", Identifier(), className.set(match()));
+ }
+
+ Rule Fresh(Var<Boolean> fresh) {
+ return Sequence(fresh.set(false), Optional(":fresh", fresh.set(true)));
+ }
+
+ Rule SingleQuotedString(Var<String> 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"));
+ }
+}