From 3e2786a004eddf968a5fa7d5a92dff5b1b5f7734 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Wed, 24 Feb 2016 08:16:16 +0000 Subject: Create internal package --- .../mammoth/internal/xml/NullXmlElement.java | 25 ++++++ .../zwobble/mammoth/internal/xml/XmlElement.java | 89 ++++++++++++++++++++++ .../mammoth/internal/xml/XmlElementLike.java | 9 +++ .../mammoth/internal/xml/XmlElementList.java | 28 +++++++ .../org/zwobble/mammoth/internal/xml/XmlNode.java | 6 ++ .../mammoth/internal/xml/XmlNodeVisitor.java | 6 ++ .../org/zwobble/mammoth/internal/xml/XmlNodes.java | 29 +++++++ .../zwobble/mammoth/internal/xml/XmlTextNode.java | 28 +++++++ .../mammoth/internal/xml/parsing/ElementName.java | 19 +++++ .../mammoth/internal/xml/parsing/SimpleSax.java | 64 ++++++++++++++++ .../internal/xml/parsing/SimpleSaxHandler.java | 9 +++ .../internal/xml/parsing/XmlElementBuilder.java | 28 +++++++ .../mammoth/internal/xml/parsing/XmlParser.java | 81 ++++++++++++++++++++ 13 files changed, 421 insertions(+) create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/NullXmlElement.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/XmlElement.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/XmlElementLike.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/XmlElementList.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/XmlNode.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/XmlNodeVisitor.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/XmlNodes.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/XmlTextNode.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/parsing/ElementName.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSax.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/parsing/SimpleSaxHandler.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlElementBuilder.java create mode 100644 src/main/java/org/zwobble/mammoth/internal/xml/parsing/XmlParser.java (limited to 'src/main/java/org/zwobble/mammoth/internal/xml') 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 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 attributes; + private final List children; + + public XmlElement(String name, Map attributes, List children) { + this.name = name; + this.attributes = attributes; + this.children = children; + } + + public String getName() { + return name; + } + + public Map getAttributes() { + return attributes; + } + + public String getAttribute(String name) { + // TODO: throw more informative error message + return getAttributeOrNone(name).get(); + } + + @Override + public Optional getAttributeOrNone(String name) { + return Optional.ofNullable(attributes.get(name)); + } + + public List getChildren() { + return children; + } + + @Override + public String innerText() { + return Joiner.on("").join(transform(children, XmlNode::innerText)); + } + + @Override + public T accept(XmlNodeVisitor visitor) { + return visitor.visit(this); + } + + public Iterable children() { + return children; + } + + @Override + public String toString() { + return "XmlElement(name=" + name + ", attributes=" + attributes + + ", children=" + children + ")"; + } + + public XmlElementList findChildren(String name) { + Iterable 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 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 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 { + private final List elements; + + public XmlElementList(List elements) { + this.elements = elements; + } + + @Override + public Iterator iterator() { + return elements.iterator(); + } + + public XmlElementList findChildren(String name) { + Iterable 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 accept(XmlNodeVisitor 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 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 attributes) { + return element(name, attributes, list()); + } + + public static XmlElement element(String name, List children) { + return element(name, map(), children); + } + + public static XmlElement element(String name, Map attributes, List 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 accept(XmlNodeVisitor 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 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 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 attributes; + private final List children; + + XmlElementBuilder(String name, Map 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 namespaces; + + public XmlParser(BiMap 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 elementStack; + + public NodeGenerator() { + elementStack = new ArrayDeque<>(); + } + + public XmlElement getRoot() { + return elementStack.getFirst().build(); + } + + @Override + public void startElement(ElementName name, Map attributes) { + Map 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)); + } + } +} -- cgit v1.2.3