Fixed for bug 1168 : Issue while update subnet
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / rest / impl / XmlToCompositeNodeReader.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.sal.rest.impl;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import java.io.BufferedInputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.net.URI;
16 import java.util.Stack;
17 import javax.xml.stream.XMLEventReader;
18 import javax.xml.stream.XMLInputFactory;
19 import javax.xml.stream.XMLStreamConstants;
20 import javax.xml.stream.XMLStreamException;
21 import javax.xml.stream.events.Characters;
22 import javax.xml.stream.events.StartElement;
23 import javax.xml.stream.events.XMLEvent;
24 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
25 import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
26 import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
27 import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
28 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
29 import org.opendaylight.yangtools.yang.data.api.Node;
30
31 public class XmlToCompositeNodeReader {
32
33     private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
34     private XMLEventReader eventReader;
35
36     public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException,
37             IOException {
38         // Get an XML stream which can be marked, and reset, so we can check and
39         // see if there is any content being provided.
40         entityStream = getMarkableStream(entityStream);
41
42         if (isInputStreamEmpty(entityStream)) {
43             return null;
44         }
45
46         eventReader = xmlInputFactory.createXMLEventReader(entityStream);
47
48         if (eventReader.hasNext()) {
49             XMLEvent element = eventReader.peek();
50             if (element.isStartDocument()) {
51                 eventReader.nextEvent();
52             }
53         }
54
55         if (eventReader.hasNext() && !isCompositeNodeEvent(eventReader.peek())) {
56             throw new UnsupportedFormatException("Root element of XML has to be composite element.");
57         }
58
59         final Stack<NodeWrapper<?>> processingQueue = new Stack<>();
60         CompositeNodeWrapper root = null;
61         NodeWrapper<?> element = null;
62         while (eventReader.hasNext()) {
63             final XMLEvent event = eventReader.nextEvent();
64
65             if (event.isStartElement()) {
66                 final StartElement startElement = event.asStartElement();
67                 CompositeNodeWrapper compParentNode = null;
68                 if (!processingQueue.isEmpty() && processingQueue.peek() instanceof CompositeNodeWrapper) {
69                     compParentNode = (CompositeNodeWrapper) processingQueue.peek();
70                 }
71                 NodeWrapper<?> newNode = null;
72                 if (isCompositeNodeEvent(event)) {
73                     if (root == null) {
74                         root = resolveCompositeNodeFromStartElement(startElement);
75                         newNode = root;
76                     } else {
77                         newNode = resolveCompositeNodeFromStartElement(startElement);
78                     }
79                 } else if (isSimpleNodeEvent(event)) {
80                     if (root == null) {
81                         throw new UnsupportedFormatException("Root element of XML has to be composite element.");
82                     }
83                     newNode = resolveSimpleNodeFromStartElement(startElement);
84                 }
85
86                 if (newNode != null) {
87                     processingQueue.push(newNode);
88                     if (compParentNode != null) {
89                         compParentNode.addValue(newNode);
90                     }
91                 }
92             } else if (event.isEndElement()) {
93                 element = processingQueue.pop();
94             }
95         }
96
97         if (!root.getLocalName().equals(element.getLocalName())) {
98             throw new UnsupportedFormatException("XML should contain only one root element");
99         }
100
101         return root;
102     }
103
104     /**
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.
107      *
108      * @param entityStream
109      * @return
110      */
111     private InputStream getMarkableStream(InputStream entityStream) {
112         if (!entityStream.markSupported()) {
113             entityStream = new BufferedInputStream(entityStream);
114         }
115         return entityStream;
116     }
117
118     private boolean isInputStreamEmpty(InputStream entityStream) throws IOException {
119         boolean isEmpty = false;
120         entityStream.mark(1);
121         if (entityStream.read() == -1) {
122             isEmpty = true;
123         }
124         entityStream.reset();
125         return isEmpty;
126     }
127
128     private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
129         checkArgument(event != null, "XML Event cannot be NULL!");
130         if (event.isStartElement()) {
131             XMLEvent innerEvent = skipCommentsAndWhitespace();
132             if (innerEvent != null && (innerEvent.isCharacters() || innerEvent.isEndElement())) {
133                 return true;
134             }
135         }
136         return false;
137     }
138
139     private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException {
140         checkArgument(event != null, "XML Event cannot be NULL!");
141         if (event.isStartElement()) {
142             XMLEvent innerEvent = skipCommentsAndWhitespace();
143             if (innerEvent != null) {
144                 if (innerEvent.isStartElement()) {
145                     return true;
146                 }
147             }
148         }
149         return false;
150     }
151
152     private XMLEvent skipCommentsAndWhitespace() throws XMLStreamException {
153         while (eventReader.hasNext()) {
154             XMLEvent event = eventReader.peek();
155             if (event.getEventType() == XMLStreamConstants.COMMENT) {
156                 eventReader.nextEvent();
157                 continue;
158             }
159
160             if (event.isCharacters()) {
161                 Characters chars = event.asCharacters();
162                 if (chars.isWhiteSpace()) {
163                     eventReader.nextEvent();
164                     continue;
165                 }
166             }
167             return event;
168         }
169         return null;
170     }
171
172     private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final StartElement startElement) {
173         checkArgument(startElement != null, "Start Element cannot be NULL!");
174         return new CompositeNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
175     }
176
177     private NodeWrapper<? extends Node<?>> resolveSimpleNodeFromStartElement(final StartElement startElement)
178             throws XMLStreamException {
179         checkArgument(startElement != null, "Start Element cannot be NULL!");
180         String data = getValueOf(startElement);
181         if (data == null) {
182             return new EmptyNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
183         }
184         return new SimpleNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement),
185                 resolveValueOfElement(data, startElement));
186     }
187
188     private String getValueOf(StartElement startElement) throws XMLStreamException {
189         String data = null;
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());
197                 }
198             } else if (innerEvent.isEndElement()) {
199                 if (startElement.getLocation().getCharacterOffset() == innerEvent.getLocation().getCharacterOffset()) {
200                     data = null;
201                 } else {
202                     data = "";
203                 }
204             }
205         }
206         return data == null ? null : data.trim();
207     }
208
209     private String getAdditionalData(XMLEvent event) throws XMLStreamException {
210         String data = "";
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());
218                 }
219             }
220         }
221         return data;
222     }
223
224     private String getLocalNameFor(StartElement startElement) {
225         return startElement.getName().getLocalPart();
226     }
227
228     private URI getNamespaceFor(StartElement startElement) {
229         String namespaceURI = startElement.getName().getNamespaceURI();
230         return namespaceURI.isEmpty() ? null : URI.create(namespaceURI);
231     }
232
233     private Object resolveValueOfElement(String value, StartElement startElement) {
234         // it could be instance-identifier Built-In Type
235         if (value.startsWith("/")) {
236             IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml(
237                     startElement));
238             if (iiValue != null) {
239                 return iiValue;
240             }
241         }
242         // it could be identityref Built-In Type
243         String[] namespaceAndValue = value.split(":");
244         if (namespaceAndValue.length == 2) {
245             String namespace = startElement.getNamespaceContext().getNamespaceURI(namespaceAndValue[0]);
246             if (namespace != null && !namespace.isEmpty()) {
247                 return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0], value);
248             }
249         }
250         // it is not "prefix:value" but just "value"
251         return value;
252     }
253
254 }