+package org.opendaylight.controller.sal.rest.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Stack;
+
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Characters;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+
+public class XmlReader {
+
+ private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
+ private XMLEventReader eventReader;
+
+ public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException {
+ eventReader = xmlInputFactory.createXMLEventReader(entityStream);
+
+ if (eventReader.hasNext()) {
+ XMLEvent element = eventReader.peek();
+ if (element.isStartDocument()) {
+ eventReader.nextEvent();
+ }
+ }
+
+ if (eventReader.hasNext() && !isCompositeNodeEvent(eventReader.peek())) {
+ throw new UnsupportedFormatException("Root element of XML has to be composite element.");
+ }
+
+ final Stack<NodeWrapper<?>> processingQueue = new Stack<>();
+ CompositeNodeWrapper root = null;
+ NodeWrapper<?> element = null;
+ while (eventReader.hasNext()) {
+ final XMLEvent event = eventReader.nextEvent();
+
+ if (event.isStartElement()) {
+ final StartElement startElement = event.asStartElement();
+ CompositeNodeWrapper compParentNode = null;
+ if (!processingQueue.isEmpty() && processingQueue.peek() instanceof CompositeNodeWrapper) {
+ compParentNode = (CompositeNodeWrapper) processingQueue.peek();
+ }
+ NodeWrapper<?> newNode = null;
+ if (isCompositeNodeEvent(event)) {
+ if (root == null) {
+ root = resolveCompositeNodeFromStartElement(startElement);
+ newNode = root;
+ } else {
+ newNode = resolveCompositeNodeFromStartElement(startElement);
+ }
+ } else if (isSimpleNodeEvent(event)) {
+ if (root == null) {
+ throw new UnsupportedFormatException("Root element of XML has to be composite element.");
+ }
+ newNode = resolveSimpleNodeFromStartElement(startElement);
+ }
+
+ if (newNode != null) {
+ processingQueue.push(newNode);
+ if (compParentNode != null) {
+ compParentNode.addValue(newNode);
+ }
+ }
+ } else if (event.isEndElement()) {
+ element = processingQueue.pop();
+ }
+ }
+
+ if (!root.getLocalName().equals(element.getLocalName())) {
+ throw new UnsupportedFormatException("XML should contain only one root element");
+ }
+
+ return root;
+ }
+
+ private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
+ checkArgument(event != null, "XML Event cannot be NULL!");
+ if (event.isStartElement()) {
+ if (eventReader.hasNext()) {
+ final XMLEvent innerEvent;
+ innerEvent = eventReader.peek();
+ if (innerEvent.isCharacters()) {
+ final Characters chars = innerEvent.asCharacters();
+ if (!chars.isWhiteSpace()) {
+ return true;
+ }
+ } else if (innerEvent.isEndElement()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException {
+ checkArgument(event != null, "XML Event cannot be NULL!");
+ if (event.isStartElement()) {
+ if (eventReader.hasNext()) {
+ XMLEvent innerEvent;
+ innerEvent = eventReader.peek();
+ if (innerEvent.isCharacters()) {
+ Characters chars = innerEvent.asCharacters();
+ if (chars.isWhiteSpace()) {
+ eventReader.nextEvent();
+ innerEvent = eventReader.peek();
+ }
+ }
+ if (innerEvent.isStartElement()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private SimpleNodeWrapper resolveSimpleNodeFromStartElement(final StartElement startElement) throws XMLStreamException {
+ checkArgument(startElement != null, "Start Element cannot be NULL!");
+ String data = null;
+
+ if (eventReader.hasNext()) {
+ final XMLEvent innerEvent = eventReader.peek();
+ if (innerEvent.isCharacters()) {
+ final Characters chars = innerEvent.asCharacters();
+ if (!chars.isWhiteSpace()) {
+ data = innerEvent.asCharacters().getData();
+ }
+ } else if (innerEvent.isEndElement()) {
+ data = "";
+ }
+ }
+
+ return new SimpleNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement), data);
+ }
+
+ private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final StartElement startElement) {
+ checkArgument(startElement != null, "Start Element cannot be NULL!");
+ return new CompositeNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement));
+ }
+
+ private String getLocalNameFrom(StartElement startElement) {
+ return startElement.getName().getLocalPart();
+ }
+
+ private URI getNamespaceFrom(StartElement startElement) {
+ String namespaceURI = startElement.getName().getNamespaceURI();
+ return namespaceURI.isEmpty() ? null : URI.create(namespaceURI);
+ }
+
+}