aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pom.xml6
-rw-r--r--src/main/java/org/zwobble/mammoth/styles/HtmlPath.java6
-rw-r--r--src/main/java/org/zwobble/mammoth/styles/HtmlPathElement.java6
-rw-r--r--src/main/java/org/zwobble/mammoth/styles/StyleMapParser.java105
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/styles/HtmlPathParsingTests.java57
-rw-r--r--src/test/java/org/zwobble/mammoth/tests/styles/StyleMapParserTests.java4
6 files changed, 182 insertions, 2 deletions
diff --git a/pom.xml b/pom.xml
index 085c8a2..f6fbce0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,5 +54,11 @@
<version>18.0</version>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.parboiled</groupId>
+ <artifactId>parboiled-java</artifactId>
+ <version>1.1.7</version>
+ <scope>compile</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/src/main/java/org/zwobble/mammoth/styles/HtmlPath.java b/src/main/java/org/zwobble/mammoth/styles/HtmlPath.java
index eb15dc5..ee78237 100644
--- a/src/main/java/org/zwobble/mammoth/styles/HtmlPath.java
+++ b/src/main/java/org/zwobble/mammoth/styles/HtmlPath.java
@@ -21,7 +21,11 @@ public class HtmlPath {
}
public static HtmlPath collapsibleElement(String tagName) {
- return new HtmlPath(list(new HtmlPathElement(list(tagName), map(), true)));
+ return collapsibleElement(tagName, map());
+ }
+
+ public static HtmlPath collapsibleElement(String tagName, Map<String, String> attributes) {
+ return new HtmlPath(list(new HtmlPathElement(list(tagName), attributes, true)));
}
private final List<HtmlPathElement> elements;
diff --git a/src/main/java/org/zwobble/mammoth/styles/HtmlPathElement.java b/src/main/java/org/zwobble/mammoth/styles/HtmlPathElement.java
index 541c6e7..e503766 100644
--- a/src/main/java/org/zwobble/mammoth/styles/HtmlPathElement.java
+++ b/src/main/java/org/zwobble/mammoth/styles/HtmlPathElement.java
@@ -15,7 +15,11 @@ public class HtmlPathElement {
}
public static HtmlPathElement collapsible(String tagName) {
- return new HtmlPathElement(list(tagName), map(), true);
+ 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;
diff --git a/src/main/java/org/zwobble/mammoth/styles/StyleMapParser.java b/src/main/java/org/zwobble/mammoth/styles/StyleMapParser.java
new file mode 100644
index 0000000..4e18948
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/styles/StyleMapParser.java
@@ -0,0 +1,105 @@
+package org.zwobble.mammoth.styles;
+
+import com.google.common.collect.ImmutableList;
+import org.parboiled.BaseParser;
+import org.parboiled.Parboiled;
+import org.parboiled.Rule;
+import org.parboiled.parserunners.ReportingParseRunner;
+import org.parboiled.support.ParsingResult;
+import org.parboiled.support.Var;
+
+import java.util.List;
+
+import static org.zwobble.mammoth.util.MammothLists.list;
+import static org.zwobble.mammoth.util.MammothMaps.map;
+
+public class StyleMapParser extends BaseParser<Object> {
+ public static HtmlPath parseHtmlPath(String value) {
+ StyleMapParser parser = Parboiled.createParser(StyleMapParser.class);
+ ReportingParseRunner runner = new ReportingParseRunner(parser.HtmlPath());
+ ParsingResult<?> result = runner.run(value);
+ if (result.hasErrors()) {
+ // TODO: wrap in result
+ throw new RuntimeException("Parse error");
+ } else {
+ return (HtmlPath) result.valueStack.peek();
+ }
+ }
+
+ Rule HtmlPath() {
+ ListVar<HtmlPathElement> elements = new ListVar<>();
+ return Sequence(HtmlPathElements(elements), EOI, push(new HtmlPath(elements.build())));
+ }
+
+ Rule HtmlPathElements(ListVar<HtmlPathElement> elements) {
+ Var<HtmlPathElement> element = new Var<>();
+ return FirstOf(
+ Sequence(
+ HtmlPathElement(element),
+ elements.append(element),
+ ZeroOrMore(Sequence(Whitespace(), Ch('>'), Whitespace(), HtmlPathElement(element), elements.append(element)))),
+ toRule(push(ImmutableList.builder())));
+ }
+
+ Rule HtmlPathElement(Var<HtmlPathElement> element) {
+ Var<String> tagName = new Var<>();
+ ListVar<String> classNames = new ListVar<>();
+ Var<Boolean> fresh = new Var<>();
+ return Sequence(
+ TagNames(tagName),
+ ClassNames(classNames),
+ Fresh(fresh),
+ element.set(new HtmlPathElement(
+ list(tagName.get()),
+ classNames.isEmpty() ? map() : map("class", String.join(" ", classNames.build())),
+ !fresh.get())));
+ }
+
+ Rule TagNames(Var<String> tagName) {
+ return Sequence(Identifier(), tagName.set(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 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"));
+ }
+
+ class ListVar<T> extends Var<ImmutableList.Builder<T>> {
+ boolean append(Var<T> element) {
+ if (get() == null) {
+ set(ImmutableList.builder());
+ }
+ return set(get().add(element.get()));
+ }
+
+ public List<T> build() {
+ if (get() == null) {
+ return list();
+ } else {
+ return get().build();
+ }
+ }
+
+ public boolean isEmpty() {
+ return get() == null;
+ }
+ }
+}
diff --git a/src/test/java/org/zwobble/mammoth/tests/styles/HtmlPathParsingTests.java b/src/test/java/org/zwobble/mammoth/tests/styles/HtmlPathParsingTests.java
new file mode 100644
index 0000000..6b75f0f
--- /dev/null
+++ b/src/test/java/org/zwobble/mammoth/tests/styles/HtmlPathParsingTests.java
@@ -0,0 +1,57 @@
+package org.zwobble.mammoth.tests.styles;
+
+import org.junit.Test;
+import org.zwobble.mammoth.styles.HtmlPath;
+import org.zwobble.mammoth.styles.HtmlPathElement;
+import org.zwobble.mammoth.styles.StyleMapParser;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.zwobble.mammoth.tests.DeepReflectionMatcher.deepEquals;
+import static org.zwobble.mammoth.util.MammothLists.list;
+import static org.zwobble.mammoth.util.MammothMaps.map;
+
+public class HtmlPathParsingTests {
+ @Test
+ public void canParseEmptyPath() {
+ assertThat(
+ StyleMapParser.parseHtmlPath(""),
+ deepEquals(HtmlPath.EMPTY));
+ }
+
+ @Test
+ public void canReadSingleElement() {
+ assertThat(
+ StyleMapParser.parseHtmlPath("p"),
+ deepEquals(HtmlPath.collapsibleElement("p")));
+ }
+
+ // TODO: choice
+
+ @Test
+ public void canReadNestedElements() {
+ assertThat(
+ StyleMapParser.parseHtmlPath("ul > li"),
+ deepEquals(new HtmlPath(list(HtmlPathElement.collapsible("ul"), HtmlPathElement.collapsible("li")))));
+ }
+
+ @Test
+ public void canReadClassOnElement() {
+ assertThat(
+ StyleMapParser.parseHtmlPath("p.tip"),
+ deepEquals(HtmlPath.collapsibleElement("p", map("class", "tip"))));
+ }
+
+ @Test
+ public void canReadMultipleClassesOnElement() {
+ assertThat(
+ StyleMapParser.parseHtmlPath("p.tip.help"),
+ deepEquals(HtmlPath.collapsibleElement("p", map("class", "tip help"))));
+ }
+
+ @Test
+ public void canReadWhenElementMustBeFresh() {
+ assertThat(
+ StyleMapParser.parseHtmlPath("p:fresh"),
+ deepEquals(HtmlPath.element("p")));
+ }
+}
diff --git a/src/test/java/org/zwobble/mammoth/tests/styles/StyleMapParserTests.java b/src/test/java/org/zwobble/mammoth/tests/styles/StyleMapParserTests.java
new file mode 100644
index 0000000..09f0806
--- /dev/null
+++ b/src/test/java/org/zwobble/mammoth/tests/styles/StyleMapParserTests.java
@@ -0,0 +1,4 @@
+package org.zwobble.mammoth.tests.styles;
+
+public class StyleMapParserTests {
+}