aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/zwobble/mammoth/internal/xml
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/xml
parent0dc3a12affc2e46c1a0df2899cf51a1ae3fdbd61 (diff)
Create internal package
Diffstat (limited to 'src/main/java/org/zwobble/mammoth/internal/xml')
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/NullXmlElement.java25
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/XmlElement.java89
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/XmlElementLike.java9
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/XmlElementList.java28
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/XmlNode.java6
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/XmlNodeVisitor.java6
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/XmlNodes.java29
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/XmlTextNode.java28
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/parsing/ElementName.java19
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSax.java64
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSaxHandler.java9
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlElementBuilder.java28
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlParser.java81
13 files changed, 421 insertions, 0 deletions
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/NullXmlElement.java b/src/main/java/org/zwobble/mammoth/internal/xml/NullXmlElement.java
new file mode 100644
index 0000000..ecc7971
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/NullXmlElement.java
@@ -0,0 +1,25 @@
+package org.zwobble.mammoth.internal.xml;
+
+import java.util.Optional;
+
+public class NullXmlElement implements XmlElementLike {
+ public static final XmlElementLike INSTANCE = new NullXmlElement();
+
+ private NullXmlElement() {
+ }
+
+ @Override
+ public boolean hasChild(String name) {
+ return false;
+ }
+
+ @Override
+ public XmlElementLike findChildOrEmpty(String name) {
+ return this;
+ }
+
+ @Override
+ public Optional<String> getAttributeOrNone(String name) {
+ return Optional.empty();
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/XmlElement.java b/src/main/java/org/zwobble/mammoth/internal/xml/XmlElement.java
new file mode 100644
index 0000000..157d638
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/XmlElement.java
@@ -0,0 +1,89 @@
+package org.zwobble.mammoth.internal.xml;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.google.common.collect.Iterables.*;
+
+public class XmlElement implements XmlNode, XmlElementLike {
+ private final String name;
+ private final Map<String, String> attributes;
+ private final List<XmlNode> children;
+
+ public XmlElement(String name, Map<String, String> attributes, List<XmlNode> children) {
+ this.name = name;
+ this.attributes = attributes;
+ this.children = children;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public String getAttribute(String name) {
+ // TODO: throw more informative error message
+ return getAttributeOrNone(name).get();
+ }
+
+ @Override
+ public Optional<String> getAttributeOrNone(String name) {
+ return Optional.ofNullable(attributes.get(name));
+ }
+
+ public List<XmlNode> getChildren() {
+ return children;
+ }
+
+ @Override
+ public String innerText() {
+ return Joiner.on("").join(transform(children, XmlNode::innerText));
+ }
+
+ @Override
+ public <T> T accept(XmlNodeVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ public Iterable<XmlNode> children() {
+ return children;
+ }
+
+ @Override
+ public String toString() {
+ return "XmlElement(name=" + name + ", attributes=" + attributes
+ + ", children=" + children + ")";
+ }
+
+ public XmlElementList findChildren(String name) {
+ Iterable<XmlElement> filtered = findChildrenIterable(name);
+ return new XmlElementList(ImmutableList.copyOf(filtered));
+ }
+
+ public XmlElement findChild(String name) {
+ return findChildrenIterable(name).iterator().next();
+ }
+
+ @Override
+ public boolean hasChild(String name) {
+ return findChildrenIterable(name).iterator().hasNext();
+ }
+
+ @Override
+ public XmlElementLike findChildOrEmpty(String name) {
+ return getFirst(findChildrenIterable(name), NullXmlElement.INSTANCE);
+ }
+
+ private Iterable<XmlElement> findChildrenIterable(String name) {
+ return filter(
+ filter(children, XmlElement.class),
+ child -> child.getName().equals(name));
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/XmlElementLike.java b/src/main/java/org/zwobble/mammoth/internal/xml/XmlElementLike.java
new file mode 100644
index 0000000..bdb3cc8
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/XmlElementLike.java
@@ -0,0 +1,9 @@
+package org.zwobble.mammoth.internal.xml;
+
+import java.util.Optional;
+
+public interface XmlElementLike {
+ boolean hasChild(String name);
+ XmlElementLike findChildOrEmpty(String name);
+ Optional<String> getAttributeOrNone(String name);
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/XmlElementList.java b/src/main/java/org/zwobble/mammoth/internal/xml/XmlElementList.java
new file mode 100644
index 0000000..3facde4
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/XmlElementList.java
@@ -0,0 +1,28 @@
+package org.zwobble.mammoth.internal.xml;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class XmlElementList implements Iterable<XmlElement> {
+ private final List<XmlElement> elements;
+
+ public XmlElementList(List<XmlElement> elements) {
+ this.elements = elements;
+ }
+
+ @Override
+ public Iterator<XmlElement> iterator() {
+ return elements.iterator();
+ }
+
+ public XmlElementList findChildren(String name) {
+ Iterable<XmlElement> children = Iterables.concat(
+ Iterables.transform(
+ elements,
+ element -> element.findChildren(name)));
+ return new XmlElementList(ImmutableList.copyOf(children));
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/XmlNode.java b/src/main/java/org/zwobble/mammoth/internal/xml/XmlNode.java
new file mode 100644
index 0000000..6f6e7fa
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/XmlNode.java
@@ -0,0 +1,6 @@
+package org.zwobble.mammoth.internal.xml;
+
+public interface XmlNode {
+ String innerText();
+ <T> T accept(XmlNodeVisitor<T> visitor);
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/XmlNodeVisitor.java b/src/main/java/org/zwobble/mammoth/internal/xml/XmlNodeVisitor.java
new file mode 100644
index 0000000..5bbef54
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/XmlNodeVisitor.java
@@ -0,0 +1,6 @@
+package org.zwobble.mammoth.internal.xml;
+
+public interface XmlNodeVisitor<T> {
+ T visit(XmlElement element);
+ T visit(XmlTextNode textNode);
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/XmlNodes.java b/src/main/java/org/zwobble/mammoth/internal/xml/XmlNodes.java
new file mode 100644
index 0000000..3b4bab7
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/XmlNodes.java
@@ -0,0 +1,29 @@
+package org.zwobble.mammoth.internal.xml;
+
+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 XmlNodes {
+ public static XmlElement element(String name) {
+ return element(name, list());
+ }
+
+ public static XmlElement element(String name, Map<String, String> attributes) {
+ return element(name, attributes, list());
+ }
+
+ public static XmlElement element(String name, List<XmlNode> children) {
+ return element(name, map(), children);
+ }
+
+ public static XmlElement element(String name, Map<String, String> attributes, List<XmlNode> children) {
+ return new XmlElement(name, attributes, children);
+ }
+
+ public static XmlTextNode text(String value) {
+ return new XmlTextNode(value);
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/XmlTextNode.java b/src/main/java/org/zwobble/mammoth/internal/xml/XmlTextNode.java
new file mode 100644
index 0000000..02380ee
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/XmlTextNode.java
@@ -0,0 +1,28 @@
+package org.zwobble.mammoth.internal.xml;
+
+public class XmlTextNode implements XmlNode {
+ private final String value;
+
+ public XmlTextNode(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String innerText() {
+ return value;
+ }
+
+ @Override
+ public <T> T accept(XmlNodeVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ return "XmlTextNode(value=" + value + ")";
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/parsing/ElementName.java b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/ElementName.java
new file mode 100644
index 0000000..a97a683
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/ElementName.java
@@ -0,0 +1,19 @@
+package org.zwobble.mammoth.internal.xml.parsing;
+
+class ElementName {
+ private final String uri;
+ private final String localName;
+
+ ElementName(String uri, String localName) {
+ this.uri = uri;
+ this.localName = localName;
+ }
+
+ String getUri() {
+ return uri;
+ }
+
+ String getLocalName() {
+ return localName;
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSax.java b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSax.java
new file mode 100644
index 0000000..047e02d
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSax.java
@@ -0,0 +1,64 @@
+package org.zwobble.mammoth.internal.xml.parsing;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.Map;
+import java.util.stream.IntStream;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+import static java.util.stream.Collectors.toMap;
+
+class SimpleSax {
+ static void parseStream(InputStream input, SimpleSaxHandler handler) {
+ parseInputSource(new InputSource(input), handler);
+ }
+
+ static void parseString(String value, SimpleSaxHandler handler) {
+ parseInputSource(new InputSource(new StringReader(value)), handler);
+ }
+
+ private static void parseInputSource(InputSource inputSource, SimpleSaxHandler handler) {
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ parserFactory.setNamespaceAware(true);
+ try {
+ SAXParser saxParser = parserFactory.newSAXParser();
+ XMLReader xmlReader = saxParser.getXMLReader();
+ xmlReader.setContentHandler(new DefaultHandler() {
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ ElementName name = new ElementName(uri, localName);
+ Map<ElementName, String> attributesMap = IntStream.range(0, attributes.getLength())
+ .boxed()
+ .collect(toMap(
+ index -> new ElementName(attributes.getURI(index), attributes.getLocalName(index)),
+ attributes::getValue
+ ));
+ handler.startElement(name, attributesMap);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ handler.endElement();
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ handler.characters(new String(ch, start, length));
+ }
+ });
+ xmlReader.parse(inputSource);
+ } catch (ParserConfigurationException | SAXException | IOException exception) {
+ throw new RuntimeException(exception);
+ }
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSaxHandler.java b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSaxHandler.java
new file mode 100644
index 0000000..8358346
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSaxHandler.java
@@ -0,0 +1,9 @@
+package org.zwobble.mammoth.internal.xml.parsing;
+
+import java.util.Map;
+
+interface SimpleSaxHandler {
+ void startElement(ElementName name, Map<ElementName, String> attributes);
+ void endElement();
+ void characters(String string);
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlElementBuilder.java b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlElementBuilder.java
new file mode 100644
index 0000000..231299e
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlElementBuilder.java
@@ -0,0 +1,28 @@
+package org.zwobble.mammoth.internal.xml.parsing;
+
+import org.zwobble.mammoth.internal.xml.XmlElement;
+import org.zwobble.mammoth.internal.xml.XmlNode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+class XmlElementBuilder {
+ private final String name;
+ private final Map<String, String> attributes;
+ private final List<XmlNode> children;
+
+ XmlElementBuilder(String name, Map<String, String> attributes) {
+ this.name = name;
+ this.attributes = attributes;
+ this.children = new ArrayList<>();
+ }
+
+ XmlElement build() {
+ return new XmlElement(name, attributes, children);
+ }
+
+ void addChild(XmlNode node) {
+ children.add(node);
+ }
+}
diff --git a/src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlParser.java b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlParser.java
new file mode 100644
index 0000000..566dbd6
--- /dev/null
+++ b/src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlParser.java
@@ -0,0 +1,81 @@
+package org.zwobble.mammoth.internal.xml.parsing;
+
+import java.io.InputStream;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.BiMap;
+import org.zwobble.mammoth.internal.xml.XmlElement;
+import org.zwobble.mammoth.internal.xml.XmlTextNode;
+
+import static java.util.stream.Collectors.toMap;
+
+public class XmlParser {
+ private final BiMap<String, String> namespaces;
+
+ public XmlParser(BiMap<String, String> namespaces) {
+ this.namespaces = namespaces;
+ }
+
+ public XmlElement parseStream(InputStream inputStream) {
+ NodeGenerator nodeGenerator = new NodeGenerator();
+ SimpleSax.parseStream(inputStream, nodeGenerator);
+ return nodeGenerator.getRoot();
+ }
+
+ public XmlElement parseString(String value) {
+ NodeGenerator nodeGenerator = new NodeGenerator();
+ SimpleSax.parseString(value, nodeGenerator);
+ return nodeGenerator.getRoot();
+ }
+
+ private class NodeGenerator implements SimpleSaxHandler {
+ private final Deque<XmlElementBuilder> elementStack;
+
+ public NodeGenerator() {
+ elementStack = new ArrayDeque<>();
+ }
+
+ public XmlElement getRoot() {
+ return elementStack.getFirst().build();
+ }
+
+ @Override
+ public void startElement(ElementName name, Map<ElementName, String> attributes) {
+ Map<String, String> simpleAttributes = attributes
+ .entrySet()
+ .stream()
+ .collect(toMap(
+ entry -> readName(entry.getKey()),
+ entry -> entry.getValue()));
+ XmlElementBuilder element = new XmlElementBuilder(readName(name), simpleAttributes);
+ elementStack.add(element);
+ }
+
+ private String readName(ElementName name) {
+ if (Strings.isNullOrEmpty(name.getUri())) {
+ return name.getLocalName();
+ } else if (namespaces.containsValue(name.getUri())) {
+ return namespaces.inverse().get(name.getUri()) + ":" + name.getLocalName();
+ } else {
+ return "{" + name.getUri() + "}" + name.getLocalName();
+ }
+
+ }
+
+ @Override
+ public void endElement() {
+ if (elementStack.size() > 1) {
+ XmlElementBuilder element = elementStack.removeLast();
+ elementStack.getLast().addChild(element.build());
+ }
+ }
+
+ @Override
+ public void characters(String string) {
+ elementStack.getLast().addChild(new XmlTextNode(string));
+ }
+ }
+}