2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.sal.rest.impl;
10 import static com.google.common.base.Preconditions.checkArgument;
12 import java.io.InputStream;
14 import java.util.Stack;
16 import javax.xml.stream.XMLEventReader;
17 import javax.xml.stream.XMLInputFactory;
18 import javax.xml.stream.XMLStreamException;
19 import javax.xml.stream.events.Characters;
20 import javax.xml.stream.events.StartElement;
21 import javax.xml.stream.events.XMLEvent;
23 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
24 import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
25 import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
26 import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
27 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
28 import org.opendaylight.yangtools.yang.data.api.Node;
30 public class XmlReader {
32 private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
33 private XMLEventReader eventReader;
35 public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException {
36 eventReader = xmlInputFactory.createXMLEventReader(entityStream);
38 if (eventReader.hasNext()) {
39 XMLEvent element = eventReader.peek();
40 if (element.isStartDocument()) {
41 eventReader.nextEvent();
45 if (eventReader.hasNext() && !isCompositeNodeEvent(eventReader.peek())) {
46 throw new UnsupportedFormatException("Root element of XML has to be composite element.");
49 final Stack<NodeWrapper<?>> processingQueue = new Stack<>();
50 CompositeNodeWrapper root = null;
51 NodeWrapper<?> element = null;
52 while (eventReader.hasNext()) {
53 final XMLEvent event = eventReader.nextEvent();
55 if (event.isStartElement()) {
56 final StartElement startElement = event.asStartElement();
57 CompositeNodeWrapper compParentNode = null;
58 if (!processingQueue.isEmpty() && processingQueue.peek() instanceof CompositeNodeWrapper) {
59 compParentNode = (CompositeNodeWrapper) processingQueue.peek();
61 NodeWrapper<?> newNode = null;
62 if (isCompositeNodeEvent(event)) {
64 root = resolveCompositeNodeFromStartElement(startElement);
67 newNode = resolveCompositeNodeFromStartElement(startElement);
69 } else if (isSimpleNodeEvent(event)) {
71 throw new UnsupportedFormatException("Root element of XML has to be composite element.");
73 newNode = resolveSimpleNodeFromStartElement(startElement);
76 if (newNode != null) {
77 processingQueue.push(newNode);
78 if (compParentNode != null) {
79 compParentNode.addValue(newNode);
82 } else if (event.isEndElement()) {
83 element = processingQueue.pop();
87 if (!root.getLocalName().equals(element.getLocalName())) {
88 throw new UnsupportedFormatException("XML should contain only one root element");
94 private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
95 checkArgument(event != null, "XML Event cannot be NULL!");
96 if (event.isStartElement()) {
97 if (eventReader.hasNext()) {
98 final XMLEvent innerEvent;
99 innerEvent = eventReader.peek();
100 if (innerEvent.isCharacters()) {
101 final Characters chars = innerEvent.asCharacters();
102 if (!chars.isWhiteSpace()) {
105 } else if (innerEvent.isEndElement()) {
113 private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException {
114 checkArgument(event != null, "XML Event cannot be NULL!");
115 if (event.isStartElement()) {
116 if (eventReader.hasNext()) {
118 innerEvent = eventReader.peek();
119 if (innerEvent.isCharacters()) {
120 Characters chars = innerEvent.asCharacters();
121 if (chars.isWhiteSpace()) {
122 eventReader.nextEvent();
123 innerEvent = eventReader.peek();
126 if (innerEvent.isStartElement()) {
134 private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final StartElement startElement) {
135 checkArgument(startElement != null, "Start Element cannot be NULL!");
136 return new CompositeNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
139 private NodeWrapper<? extends Node<?>> resolveSimpleNodeFromStartElement(final StartElement startElement)
140 throws XMLStreamException {
141 checkArgument(startElement != null, "Start Element cannot be NULL!");
142 String data = getValueOf(startElement);
144 return new EmptyNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
146 return new SimpleNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement),
147 resolveValueOfElement(data, startElement));
150 private String getValueOf(StartElement startElement) throws XMLStreamException {
152 if (eventReader.hasNext()) {
153 final XMLEvent innerEvent = eventReader.peek();
154 if (innerEvent.isCharacters()) {
155 final Characters chars = innerEvent.asCharacters();
156 if (!chars.isWhiteSpace()) {
157 data = innerEvent.asCharacters().getData();
158 data = data + getAdditionalData(eventReader.nextEvent());
160 } else if (innerEvent.isEndElement()) {
161 if (startElement.getLocation().getCharacterOffset() == innerEvent.getLocation().getCharacterOffset()) {
171 private String getAdditionalData(XMLEvent event) throws XMLStreamException {
173 if (eventReader.hasNext()) {
174 final XMLEvent innerEvent = eventReader.peek();
175 if (innerEvent.isCharacters() && !innerEvent.isEndElement()) {
176 final Characters chars = innerEvent.asCharacters();
177 if (!chars.isWhiteSpace()) {
178 data = innerEvent.asCharacters().getData();
179 data = data + getAdditionalData(eventReader.nextEvent());
186 private String getLocalNameFor(StartElement startElement) {
187 return startElement.getName().getLocalPart();
190 private URI getNamespaceFor(StartElement startElement) {
191 String namespaceURI = startElement.getName().getNamespaceURI();
192 return namespaceURI.isEmpty() ? null : URI.create(namespaceURI);
195 private Object resolveValueOfElement(String value, StartElement startElement) {
196 // it could be instance-identifier Built-In Type
197 if (value.startsWith("/")) {
198 IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml(startElement));
199 if (iiValue != null) {
203 // it could be identityref Built-In Type
204 String[] namespaceAndValue = value.split(":");
205 if (namespaceAndValue.length == 2) {
206 String namespace = startElement.getNamespaceContext().getNamespaceURI(namespaceAndValue[0]);
207 if (namespace != null && !namespace.isEmpty()) {
208 return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0]);
211 // it is not "prefix:value" but just "value"