014e839f2665c7e9aa7c89c5a9f344aaef377b7b
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / rest / impl / XmlReader.java
1 package org.opendaylight.controller.sal.rest.impl;
2
3 import static com.google.common.base.Preconditions.checkArgument;
4
5 import java.io.InputStream;
6 import java.net.URI;
7 import java.util.Stack;
8
9 import javax.xml.stream.XMLEventReader;
10 import javax.xml.stream.XMLInputFactory;
11 import javax.xml.stream.XMLStreamException;
12 import javax.xml.stream.events.Characters;
13 import javax.xml.stream.events.StartElement;
14 import javax.xml.stream.events.XMLEvent;
15
16 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
17 import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
18 import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
19 import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
20 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
21 import org.opendaylight.yangtools.yang.data.api.Node;
22
23 public class XmlReader {
24
25     private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
26     private XMLEventReader eventReader;
27
28     public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException {
29         eventReader = xmlInputFactory.createXMLEventReader(entityStream);
30
31         if (eventReader.hasNext()) {
32             XMLEvent element = eventReader.peek();
33             if (element.isStartDocument()) {
34                 eventReader.nextEvent();
35             }
36         }
37
38         if (eventReader.hasNext() && !isCompositeNodeEvent(eventReader.peek())) {
39             throw new UnsupportedFormatException("Root element of XML has to be composite element.");
40         }
41
42         final Stack<NodeWrapper<?>> processingQueue = new Stack<>();
43         CompositeNodeWrapper root = null;
44         NodeWrapper<?> element = null;
45         while (eventReader.hasNext()) {
46             final XMLEvent event = eventReader.nextEvent();
47
48             if (event.isStartElement()) {
49                 final StartElement startElement = event.asStartElement();
50                 CompositeNodeWrapper compParentNode = null;
51                 if (!processingQueue.isEmpty() && processingQueue.peek() instanceof CompositeNodeWrapper) {
52                     compParentNode = (CompositeNodeWrapper) processingQueue.peek();
53                 }
54                 NodeWrapper<?> newNode = null;
55                 if (isCompositeNodeEvent(event)) {
56                     if (root == null) {
57                         root = resolveCompositeNodeFromStartElement(startElement);
58                         newNode = root;
59                     } else {
60                         newNode = resolveCompositeNodeFromStartElement(startElement);
61                     }
62                 } else if (isSimpleNodeEvent(event)) {
63                     if (root == null) {
64                         throw new UnsupportedFormatException("Root element of XML has to be composite element.");
65                     }
66                     newNode = resolveSimpleNodeFromStartElement(startElement);
67                 }
68
69                 if (newNode != null) {
70                     processingQueue.push(newNode);
71                     if (compParentNode != null) {
72                         compParentNode.addValue(newNode);
73                     }
74                 }
75             } else if (event.isEndElement()) {
76                 element = processingQueue.pop();
77             }
78         }
79
80         if (!root.getLocalName().equals(element.getLocalName())) {
81             throw new UnsupportedFormatException("XML should contain only one root element");
82         }
83
84         return root;
85     }
86
87     private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
88         checkArgument(event != null, "XML Event cannot be NULL!");
89         if (event.isStartElement()) {
90             if (eventReader.hasNext()) {
91                 final XMLEvent innerEvent;
92                 innerEvent = eventReader.peek();
93                 if (innerEvent.isCharacters()) {
94                     final Characters chars = innerEvent.asCharacters();
95                     if (!chars.isWhiteSpace()) {
96                         return true;
97                     }
98                 } else if (innerEvent.isEndElement()) {
99                     return true;
100                 }
101             }
102         }
103         return false;
104     }
105
106     private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException {
107         checkArgument(event != null, "XML Event cannot be NULL!");
108         if (event.isStartElement()) {
109             if (eventReader.hasNext()) {
110                 XMLEvent innerEvent;
111                 innerEvent = eventReader.peek();
112                 if (innerEvent.isCharacters()) {
113                     Characters chars = innerEvent.asCharacters();
114                     if (chars.isWhiteSpace()) {
115                         eventReader.nextEvent();
116                         innerEvent = eventReader.peek();
117                     }
118                 }
119                 if (innerEvent.isStartElement()) {
120                     return true;
121                 }
122             }
123         }
124         return false;
125     }
126
127     private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final StartElement startElement) {
128         checkArgument(startElement != null, "Start Element cannot be NULL!");
129         return new CompositeNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
130     }
131
132     private NodeWrapper<? extends Node<?>> resolveSimpleNodeFromStartElement(final StartElement startElement)
133             throws XMLStreamException {
134         checkArgument(startElement != null, "Start Element cannot be NULL!");
135         String data = getValueOf(startElement);
136         if (data == null) {
137             return new EmptyNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
138         }
139         return new SimpleNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement),
140                 resolveValueOfElement(data, startElement));
141     }
142
143     private String getValueOf(StartElement startElement) throws XMLStreamException {
144         String data = null;
145         if (eventReader.hasNext()) {
146             final XMLEvent innerEvent = eventReader.peek();
147             if (innerEvent.isCharacters()) {
148                 final Characters chars = innerEvent.asCharacters();
149                 if (!chars.isWhiteSpace()) {
150                     data = innerEvent.asCharacters().getData();
151                 }
152             } else if (innerEvent.isEndElement()) {
153                 if (startElement.getLocation().getCharacterOffset() == innerEvent.getLocation().getCharacterOffset()) {
154                     data = null;
155                 } else {
156                     data = "";
157                 }
158             }
159         }
160         return data;
161     }
162
163     private String getLocalNameFor(StartElement startElement) {
164         return startElement.getName().getLocalPart();
165     }
166
167     private URI getNamespaceFor(StartElement startElement) {
168         String namespaceURI = startElement.getName().getNamespaceURI();
169         return namespaceURI.isEmpty() ? null : URI.create(namespaceURI);
170     }
171
172     /**
173      * @param value
174      *            value of startElement
175      * @param startElement
176      *            element containing value
177      * @return if value is "prefix:value" then {@link IdentityValuesDTO} else the same
178      *         string as parameter "value"
179      */
180     private Object resolveValueOfElement(String value, StartElement startElement) {
181         String[] namespaceAndValue = value.split(":");
182         if (namespaceAndValue.length != 2) { // it is not "prefix:value"
183             return value;
184         }
185         String namespace = startElement.getNamespaceContext().getNamespaceURI(namespaceAndValue[0]);
186         if (namespace != null && !namespace.isEmpty()) {
187             return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0]);
188         }
189         return value;
190     }
191
192 }