Merge "Refactor Neutron Pagination Code to support Network, Port and Subnet"
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / rest / impl / XmlReader.java
index d6f8a9874b963a90fa62e595e5bb7e6a9fcad8b0..1d6ca15f3c86986d97044b12de645343166226d6 100644 (file)
@@ -1,18 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
 package org.opendaylight.controller.sal.rest.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import java.io.BufferedInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Stack;
-
 import javax.xml.stream.XMLEventReader;
 import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.events.Characters;
 import javax.xml.stream.events.StartElement;
 import javax.xml.stream.events.XMLEvent;
-
 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
 import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
 import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
@@ -25,7 +33,17 @@ public class XmlReader {
     private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
     private XMLEventReader eventReader;
 
-    public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException {
+    public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException,
+                                                                      UnsupportedFormatException,
+                                                                      IOException {
+        //Get an XML stream which can be marked, and reset, so we can check and see if there is
+        //any content being provided.
+        entityStream = getMarkableStream(entityStream);
+
+        if( isInputStreamEmpty( entityStream ) ) {
+            return null;
+        }
+
         eventReader = xmlInputFactory.createXMLEventReader(entityStream);
 
         if (eventReader.hasNext()) {
@@ -84,20 +102,37 @@ public class XmlReader {
         return root;
     }
 
+    /**
+     * If the input stream is not markable, then it wraps the input stream with a buffered stream,
+     * which is mark able. That way we can check if the stream is empty safely.
+     * @param entityStream
+     * @return
+     */
+    private InputStream getMarkableStream(InputStream entityStream) {
+        if( !entityStream.markSupported() )
+        {
+            entityStream = new BufferedInputStream( entityStream );
+        }
+        return entityStream;
+    }
+
+    private boolean isInputStreamEmpty(InputStream entityStream)
+            throws IOException {
+        boolean isEmpty = false;
+        entityStream.mark( 1 );
+        if( entityStream.read() == -1 ){
+            isEmpty = true;
+        }
+        entityStream.reset();
+        return isEmpty;
+    }
+
     private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
         checkArgument(event != null, "XML Event cannot be NULL!");
         if (event.isStartElement()) {
-            if (eventReader.hasNext()) {
-                final XMLEvent innerEvent;
-                innerEvent = eventReader.peek();
-                if (innerEvent.isCharacters()) {
-                    final Characters chars = innerEvent.asCharacters();
-                    if (!chars.isWhiteSpace()) {
-                        return true;
-                    }
-                } else if (innerEvent.isEndElement()) {
-                    return true;
-                }
+            XMLEvent innerEvent = skipCommentsAndWhitespace();
+            if ( innerEvent != null && (innerEvent.isCharacters() || innerEvent.isEndElement())) {
+                return true;
             }
         }
         return false;
@@ -106,16 +141,8 @@ public class XmlReader {
     private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException {
         checkArgument(event != null, "XML Event cannot be NULL!");
         if (event.isStartElement()) {
-            if (eventReader.hasNext()) {
-                XMLEvent innerEvent;
-                innerEvent = eventReader.peek();
-                if (innerEvent.isCharacters()) {
-                    Characters chars = innerEvent.asCharacters();
-                    if (chars.isWhiteSpace()) {
-                        eventReader.nextEvent();
-                        innerEvent = eventReader.peek();
-                    }
-                }
+            XMLEvent innerEvent = skipCommentsAndWhitespace();
+            if( innerEvent != null ) {
                 if (innerEvent.isStartElement()) {
                     return true;
                 }
@@ -124,6 +151,26 @@ public class XmlReader {
         return false;
     }
 
+    private XMLEvent skipCommentsAndWhitespace() throws XMLStreamException {
+        while( eventReader.hasNext() ) {
+            XMLEvent event = eventReader.peek();
+            if( event.getEventType() == XMLStreamConstants.COMMENT ) {
+                eventReader.nextEvent();
+                continue;
+            }
+
+            if( event.isCharacters() ) {
+                Characters chars = event.asCharacters();
+                if( chars.isWhiteSpace() ) {
+                    eventReader.nextEvent();
+                    continue;
+                }
+            }
+            return event;
+        }
+        return null;
+    }
+
     private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final StartElement startElement) {
         checkArgument(startElement != null, "Start Element cannot be NULL!");
         return new CompositeNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
@@ -158,7 +205,7 @@ public class XmlReader {
                 }
             }
         }
-        return data;
+        return data == null ? null : data.trim();
     }
 
     private String getAdditionalData(XMLEvent event) throws XMLStreamException {
@@ -198,7 +245,7 @@ public class XmlReader {
         if (namespaceAndValue.length == 2) {
             String namespace = startElement.getNamespaceContext().getNamespaceURI(namespaceAndValue[0]);
             if (namespace != null && !namespace.isEmpty()) {
-                return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0]);
+                return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0],value);
             }
         }
         // it is not "prefix:value" but just "value"