Instance identifier support
[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.EmptyNodeWrapper;
18 import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
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                     data = data + getAdditionalData(eventReader.nextEvent());
152                 }
153             } else if (innerEvent.isEndElement()) {
154                 if (startElement.getLocation().getCharacterOffset() == innerEvent.getLocation().getCharacterOffset()) {
155                     data = null;
156                 } else {
157                     data = "";
158                 }
159             }
160         }
161         return data;
162     }
163
164     private String getAdditionalData(XMLEvent event) throws XMLStreamException {
165         String data = "";
166         if (eventReader.hasNext()) {
167             final XMLEvent innerEvent = eventReader.peek();
168             if (innerEvent.isCharacters() && !innerEvent.isEndElement()) {
169                 final Characters chars = innerEvent.asCharacters();
170                 if (!chars.isWhiteSpace()) {
171                     data = innerEvent.asCharacters().getData();
172                     data = data + getAdditionalData(eventReader.nextEvent());
173                 }
174             }
175         }
176         return data;
177     }
178
179     private String getLocalNameFor(StartElement startElement) {
180         return startElement.getName().getLocalPart();
181     }
182
183     private URI getNamespaceFor(StartElement startElement) {
184         String namespaceURI = startElement.getName().getNamespaceURI();
185         return namespaceURI.isEmpty() ? null : URI.create(namespaceURI);
186     }
187
188     private Object resolveValueOfElement(String value, StartElement startElement) {
189         // it could be instance-identifier Built-In Type
190         if (value.startsWith("/")) {
191             IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml(startElement));
192             if (iiValue != null) {
193                 return iiValue;
194             }
195         }
196         // it could be identityref Built-In Type
197         String[] namespaceAndValue = value.split(":");
198         if (namespaceAndValue.length == 2) {
199             String namespace = startElement.getNamespaceContext().getNamespaceURI(namespaceAndValue[0]);
200             if (namespace != null && !namespace.isEmpty()) {
201                 return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0]);
202             }
203         }
204         // it is not "prefix:value" but just "value"
205         return value;
206     }
207
208 }