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;
11 import java.io.BufferedInputStream;
12 import java.io.IOException;
13 import java.io.InputStream;
15 import java.util.Stack;
16 import javax.xml.stream.XMLEventReader;
17 import javax.xml.stream.XMLInputFactory;
18 import javax.xml.stream.XMLStreamConstants;
19 import javax.xml.stream.XMLStreamException;
20 import javax.xml.stream.events.Characters;
21 import javax.xml.stream.events.StartElement;
22 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;
31 * @deprecated class will be removed in Lithium release
34 public class XmlToCompositeNodeReader {
36 private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
38 xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
40 private XMLEventReader eventReader;
42 public Node<?> read(InputStream entityStream) throws XMLStreamException,
43 UnsupportedFormatException,
45 //Get an XML stream which can be marked, and reset, so we can check and see if there is
46 //any content being provided.
47 entityStream = getMarkableStream(entityStream);
49 if (isInputStreamEmpty(entityStream)) {
53 eventReader = xmlInputFactory.createXMLEventReader(entityStream);
54 if (eventReader.hasNext()) {
55 final XMLEvent element = eventReader.peek();
56 if (element.isStartDocument()) {
57 eventReader.nextEvent();
61 final Stack<NodeWrapper<?>> processingQueue = new Stack<>();
62 NodeWrapper<?> root = null;
63 NodeWrapper<?> element = null;
64 while (eventReader.hasNext()) {
65 final XMLEvent event = eventReader.nextEvent();
67 if (event.isStartElement()) {
68 final StartElement startElement = event.asStartElement();
69 CompositeNodeWrapper compParentNode = null;
70 if (!processingQueue.isEmpty() && processingQueue.peek() instanceof CompositeNodeWrapper) {
71 compParentNode = (CompositeNodeWrapper) processingQueue.peek();
73 NodeWrapper<?> newNode = null;
74 if (isCompositeNodeEvent(event)) {
75 newNode = resolveCompositeNodeFromStartElement(startElement);
79 } else if (isSimpleNodeEvent(event)) {
80 newNode = resolveSimpleNodeFromStartElement(startElement);
86 if (newNode != null) {
87 processingQueue.push(newNode);
88 if (compParentNode != null) {
89 compParentNode.addValue(newNode);
92 } else if (event.isEndElement()) {
93 element = processingQueue.pop();
97 if (!root.getLocalName().equals(element.getLocalName())) {
98 throw new UnsupportedFormatException("XML should contain only one root element");
101 return (Node<?>) root;
105 * If the input stream is not markable, then it wraps the input stream with a buffered stream, which is mark able.
106 * That way we can check if the stream is empty safely.
108 * @param entityStream
111 private InputStream getMarkableStream(InputStream entityStream) {
112 if (!entityStream.markSupported()) {
113 entityStream = new BufferedInputStream(entityStream);
118 private boolean isInputStreamEmpty(final InputStream entityStream) throws IOException {
119 boolean isEmpty = false;
120 entityStream.mark(1);
121 if (entityStream.read() == -1) {
124 entityStream.reset();
128 private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
129 checkArgument(event != null, "XML Event cannot be NULL!");
130 if (event.isStartElement()) {
131 final XMLEvent innerEvent = skipCommentsAndWhitespace();
132 if (innerEvent != null && (innerEvent.isCharacters() || innerEvent.isEndElement())) {
139 private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException {
140 checkArgument(event != null, "XML Event cannot be NULL!");
141 if (event.isStartElement()) {
142 final XMLEvent innerEvent = skipCommentsAndWhitespace();
143 if (innerEvent != null) {
144 if (innerEvent.isStartElement()) {
152 private XMLEvent skipCommentsAndWhitespace() throws XMLStreamException {
153 while (eventReader.hasNext()) {
154 final XMLEvent event = eventReader.peek();
155 if (event.getEventType() == XMLStreamConstants.COMMENT) {
156 eventReader.nextEvent();
160 if (event.isCharacters()) {
161 final Characters chars = event.asCharacters();
162 if (chars.isWhiteSpace()) {
163 eventReader.nextEvent();
172 private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final StartElement startElement) {
173 checkArgument(startElement != null, "Start Element cannot be NULL!");
174 return new CompositeNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
177 private NodeWrapper<? extends Node<?>> resolveSimpleNodeFromStartElement(final StartElement startElement)
178 throws XMLStreamException {
179 checkArgument(startElement != null, "Start Element cannot be NULL!");
180 final String data = getValueOf(startElement);
182 return new EmptyNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
184 return new SimpleNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement),
185 resolveValueOfElement(data, startElement));
188 private String getValueOf(final StartElement startElement) throws XMLStreamException {
190 if (eventReader.hasNext()) {
191 final XMLEvent innerEvent = eventReader.peek();
192 if (innerEvent.isCharacters()) {
193 final Characters chars = innerEvent.asCharacters();
194 if (!chars.isWhiteSpace()) {
195 data = innerEvent.asCharacters().getData();
196 data = data + getAdditionalData(eventReader.nextEvent());
198 } else if (innerEvent.isEndElement()) {
199 if (startElement.getLocation().getCharacterOffset() == innerEvent.getLocation().getCharacterOffset()) {
206 return data == null ? null : data.trim();
209 private String getAdditionalData(final XMLEvent event) throws XMLStreamException {
211 if (eventReader.hasNext()) {
212 final XMLEvent innerEvent = eventReader.peek();
213 if (innerEvent.isCharacters() && !innerEvent.isEndElement()) {
214 final Characters chars = innerEvent.asCharacters();
215 if (!chars.isWhiteSpace()) {
216 data = innerEvent.asCharacters().getData();
217 data = data + getAdditionalData(eventReader.nextEvent());
224 private String getLocalNameFor(final StartElement startElement) {
225 return startElement.getName().getLocalPart();
228 private URI getNamespaceFor(final StartElement startElement) {
229 final String namespaceURI = startElement.getName().getNamespaceURI();
230 return namespaceURI.isEmpty() ? null : URI.create(namespaceURI);
233 private Object resolveValueOfElement(final String value, final StartElement startElement) {
234 // it could be instance-identifier Built-In Type
235 if (value.startsWith("/")) {
236 final IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml(
238 if (iiValue != null) {
242 // it could be identityref Built-In Type
243 final String[] namespaceAndValue = value.split(":");
244 if (namespaceAndValue.length == 2) {
245 final String namespace = startElement.getNamespaceContext().getNamespaceURI(namespaceAndValue[0]);
246 if (namespace != null && !namespace.isEmpty()) {
247 return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0], value);
250 // it is not "prefix:value" but just "value"