Merge "BUG 1204 - exception if multiple container node occures"
authorTom Pantelis <tpanteli@brocade.com>
Tue, 8 Jul 2014 18:19:55 +0000 (18:19 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 8 Jul 2014 18:19:55 +0000 (18:19 +0000)
14 files changed:
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/gson/JsonParser.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeReader.java [moved from opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java with 86% similarity]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeReader.java [moved from opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java with 88% similarity]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MultipleEqualNamesForDataNodesTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-container.json [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-container.xml [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.json [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.xml [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/yang/equal-data-node-names.yang [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-cnsn/empty-data1.json
opendaylight/md-sal/sal-rest-connector/src/test/resources/multiple-nodes/multiple-nodes.yang [new file with mode: 0644]

diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/gson/JsonParser.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/gson/JsonParser.java
new file mode 100644 (file)
index 0000000..f3b61de
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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.gson;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.internal.LazilyParsedNumber;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.MalformedJsonException;
+import java.io.EOFException;
+import java.io.IOException;
+
+/**
+ * This class parses JSON elements from a gson JsonReader. It disallows multiple
+ * elements of the same name unlike the default gson JsonParser."
+ */
+public class JsonParser {
+    public JsonElement parse(JsonReader reader) throws JsonIOException, JsonSyntaxException {
+        // code copied from gson's JsonParser and Stream classes
+
+        boolean lenient = reader.isLenient();
+        reader.setLenient(true);
+        boolean isEmpty = true;
+        try {
+            reader.peek();
+            isEmpty = false;
+            return read(reader);
+        } catch (EOFException e) {
+            if (isEmpty) {
+                return JsonNull.INSTANCE;
+            }
+            // The stream ended prematurely so it is likely a syntax error.
+            throw new JsonSyntaxException(e);
+        } catch (MalformedJsonException e) {
+            throw new JsonSyntaxException(e);
+        } catch (IOException e) {
+            throw new JsonIOException(e);
+        } catch (NumberFormatException e) {
+            throw new JsonSyntaxException(e);
+        } catch (StackOverflowError | OutOfMemoryError e) {
+            throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
+        } finally {
+            reader.setLenient(lenient);
+        }
+    }
+
+    public JsonElement read(JsonReader in) throws IOException {
+        switch (in.peek()) {
+        case STRING:
+            return new JsonPrimitive(in.nextString());
+        case NUMBER:
+            String number = in.nextString();
+            return new JsonPrimitive(new LazilyParsedNumber(number));
+        case BOOLEAN:
+            return new JsonPrimitive(in.nextBoolean());
+        case NULL:
+            in.nextNull();
+            return JsonNull.INSTANCE;
+        case BEGIN_ARRAY:
+            JsonArray array = new JsonArray();
+            in.beginArray();
+            while (in.hasNext()) {
+                array.add(read(in));
+            }
+            in.endArray();
+            return array;
+        case BEGIN_OBJECT:
+            JsonObject object = new JsonObject();
+            in.beginObject();
+            while (in.hasNext()) {
+                final String childName = in.nextName();
+                if (object.has(childName)) {
+                    throw new JsonSyntaxException("Duplicate name " + childName + " in JSON input.");
+                }
+                object.add(childName, read(in));
+            }
+            in.endObject();
+            return object;
+        case END_DOCUMENT:
+        case NAME:
+        case END_OBJECT:
+        case END_ARRAY:
+        default:
+            throw new IllegalArgumentException();
+        }
+    }
+}
index 9e6966540534c9c79d2a120f89c35ebf603d956d..211e7e818a90bf2b3af9af67a5c693d86fb32ab7 100644 (file)
@@ -11,14 +11,12 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-
 import javax.ws.rs.Consumes;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.Provider;
-
 import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
@@ -34,25 +32,28 @@ import org.slf4j.LoggerFactory;
 public enum JsonToCompositeNodeProvider implements MessageBodyReader<CompositeNode> {
     INSTANCE;
 
-    private final static Logger LOG = LoggerFactory.getLogger( JsonToCompositeNodeProvider.class );
+    private final static Logger LOG = LoggerFactory.getLogger(JsonToCompositeNodeProvider.class);
 
     @Override
-    public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
+    public boolean isReadable(final Class<?> type, final Type genericType,
+                              final Annotation[] annotations, final MediaType mediaType) {
         return true;
     }
 
     @Override
-    public CompositeNode readFrom(final Class<CompositeNode> type, final Type genericType, final Annotation[] annotations,
-            final MediaType mediaType, final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream)
-                    throws IOException, WebApplicationException {
+    public CompositeNode readFrom(final Class<CompositeNode> type, final Type genericType,
+                                  final Annotation[] annotations, final MediaType mediaType,
+                                  final MultivaluedMap<String, String> httpHeaders,
+                                  final InputStream entityStream)
+                                          throws IOException, WebApplicationException {
         try {
-            return JsonReader.read(entityStream);
+            return JsonToCompositeNodeReader.read(entityStream);
         } catch (Exception e) {
             LOG.debug( "Error parsing json input", e);
+
             throw new RestconfDocumentedException(
                     "Error parsing input: " + e.getMessage(),
                     ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
     }
-
 }
@@ -11,16 +11,17 @@ import com.google.common.base.Splitter;
 import com.google.common.collect.Iterators;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
 import com.google.gson.JsonPrimitive;
+import com.google.gson.stream.JsonReader;
 
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URI;
-import java.util.Iterator;
 import java.util.Map.Entry;
+import java.util.Iterator;
 import java.util.Set;
 
+import org.opendaylight.controller.sal.rest.gson.JsonParser;
 import org.opendaylight.controller.sal.rest.impl.RestUtil.PrefixMapingFromJson;
 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
 import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
@@ -29,20 +30,22 @@ import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-final class JsonReader {
+class JsonToCompositeNodeReader {
     private static final Logger LOG = LoggerFactory.getLogger(JsonReader.class);
     private static final Splitter COLON_SPLITTER = Splitter.on(':');
 
-    private JsonReader() {
+    private JsonToCompositeNodeReader() {
 
     }
 
-    public static CompositeNodeWrapper read(final InputStream entityStream) throws UnsupportedFormatException {
+    public static CompositeNodeWrapper read(final InputStream entityStream)
+                                                      throws UnsupportedFormatException {
         JsonParser parser = new JsonParser();
 
-        JsonElement rootElement = parser.parse(new InputStreamReader(entityStream));
+        JsonElement rootElement = parser.parse(new JsonReader(
+                                                    new InputStreamReader(entityStream)));
         if (rootElement.isJsonNull()) {
-            //no content, so return null to indicate no input
+            // no content, so return null to indicate no input
             return null;
         }
 
@@ -50,7 +53,8 @@ final class JsonReader {
             throw new UnsupportedFormatException("Root element of Json has to be Object");
         }
 
-        Set<Entry<String, JsonElement>> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet();
+        Set<Entry<String, JsonElement>> entrySetsOfRootJsonObject =
+                                                  rootElement.getAsJsonObject().entrySet();
         if (entrySetsOfRootJsonObject.size() != 1) {
             throw new UnsupportedFormatException("Json Object should contain one element");
         }
@@ -77,7 +81,8 @@ final class JsonReader {
                 "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
     }
 
-    private static CompositeNodeWrapper createStructureWithRoot(final String rootObjectName, final JsonObject rootObject) {
+    private static CompositeNodeWrapper createStructureWithRoot(final String rootObjectName,
+                                                                final JsonObject rootObject) {
         CompositeNodeWrapper firstNode = new CompositeNodeWrapper(getNamespaceFor(rootObjectName),
                 getLocalNameFor(rootObjectName));
         for (Entry<String, JsonElement> childOfFirstNode : rootObject.entrySet()) {
@@ -86,9 +91,11 @@ final class JsonReader {
         return firstNode;
     }
 
-    private static void addChildToParent(final String childName, final JsonElement childType, final CompositeNodeWrapper parent) {
+    private static void addChildToParent(final String childName, final JsonElement childType,
+                                         final CompositeNodeWrapper parent) {
         if (childType.isJsonObject()) {
-            CompositeNodeWrapper child = new CompositeNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName));
+            CompositeNodeWrapper child = new CompositeNodeWrapper(getNamespaceFor(childName),
+                    getLocalNameFor(childName));
             parent.addValue(child);
             for (Entry<String, JsonElement> childOfChild : childType.getAsJsonObject().entrySet()) {
                 addChildToParent(childOfChild.getKey(), childOfChild.getValue(), child);
@@ -96,6 +103,7 @@ final class JsonReader {
         } else if (childType.isJsonArray()) {
             if (childType.getAsJsonArray().size() == 1 && childType.getAsJsonArray().get(0).isJsonNull()) {
                 parent.addValue(new EmptyNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName)));
+
             } else {
                 for (JsonElement childOfChildType : childType.getAsJsonArray()) {
                     addChildToParent(childName, childOfChildType, parent);
@@ -145,8 +153,9 @@ final class JsonReader {
         // it could be identityref Built-In Type
         URI namespace = getNamespaceFor(value);
         if (namespace != null) {
-            return new IdentityValuesDTO(namespace.toString(), getLocalNameFor(value), null,value);
+            return new IdentityValuesDTO(namespace.toString(), getLocalNameFor(value), null, value);
         }
+
         // it is not "prefix:value" but just "value"
         return value;
     }
index bc7473864cbb3ffa553c78fbf8a96fb64a7dad6d..31e9c96462541e75205d9ef9c6aac9508abc6e43 100644 (file)
@@ -11,7 +11,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-
 import javax.ws.rs.Consumes;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
@@ -19,7 +18,6 @@ import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.Provider;
 import javax.xml.stream.XMLStreamException;
-
 import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
@@ -35,7 +33,7 @@ import org.slf4j.LoggerFactory;
 public enum XmlToCompositeNodeProvider implements MessageBodyReader<CompositeNode> {
     INSTANCE;
 
-    private final static Logger LOG = LoggerFactory.getLogger( XmlToCompositeNodeProvider.class );
+    private final static Logger LOG = LoggerFactory.getLogger(XmlToCompositeNodeProvider.class);
 
     @Override
     public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
@@ -46,14 +44,13 @@ public enum XmlToCompositeNodeProvider implements MessageBodyReader<CompositeNod
     public CompositeNode readFrom(Class<CompositeNode> type, Type genericType, Annotation[] annotations,
             MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
             throws IOException, WebApplicationException {
-        XmlReader xmlReader = new XmlReader();
+        XmlToCompositeNodeReader xmlReader = new XmlToCompositeNodeReader();
         try {
             return xmlReader.read(entityStream);
         } catch (XMLStreamException | UnsupportedFormatException e) {
-            LOG.debug( "Error parsing json input", e );
-            throw new RestconfDocumentedException(
-                            "Error parsing input: " + e.getMessage(),
-                            ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE );
+            LOG.debug("Error parsing json input", e);
+            throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
+                    ErrorTag.MALFORMED_MESSAGE);
         }
     }
 
@@ -28,19 +28,19 @@ import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
 import org.opendaylight.yangtools.yang.data.api.Node;
 
-public class XmlReader {
+public class XmlToCompositeNodeReader {
 
     private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
     private XMLEventReader eventReader;
 
-    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.
+    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 ) ) {
+        if (isInputStreamEmpty(entityStream)) {
             return null;
         }
 
@@ -103,24 +103,24 @@ public class XmlReader {
     }
 
     /**
-     * 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.
+     * 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 );
+        if (!entityStream.markSupported()) {
+            entityStream = new BufferedInputStream(entityStream);
         }
         return entityStream;
     }
 
-    private boolean isInputStreamEmpty(InputStream entityStream)
-            throws IOException {
+    private boolean isInputStreamEmpty(InputStream entityStream) throws IOException {
         boolean isEmpty = false;
-        entityStream.mark( 1 );
-        if( entityStream.read() == -1 ){
+        entityStream.mark(1);
+        if (entityStream.read() == -1) {
             isEmpty = true;
         }
         entityStream.reset();
@@ -131,7 +131,7 @@ public class XmlReader {
         checkArgument(event != null, "XML Event cannot be NULL!");
         if (event.isStartElement()) {
             XMLEvent innerEvent = skipCommentsAndWhitespace();
-            if ( innerEvent != null && (innerEvent.isCharacters() || innerEvent.isEndElement())) {
+            if (innerEvent != null && (innerEvent.isCharacters() || innerEvent.isEndElement())) {
                 return true;
             }
         }
@@ -142,7 +142,7 @@ public class XmlReader {
         checkArgument(event != null, "XML Event cannot be NULL!");
         if (event.isStartElement()) {
             XMLEvent innerEvent = skipCommentsAndWhitespace();
-            if( innerEvent != null ) {
+            if (innerEvent != null) {
                 if (innerEvent.isStartElement()) {
                     return true;
                 }
@@ -152,16 +152,16 @@ public class XmlReader {
     }
 
     private XMLEvent skipCommentsAndWhitespace() throws XMLStreamException {
-        while( eventReader.hasNext() ) {
+        while (eventReader.hasNext()) {
             XMLEvent event = eventReader.peek();
-            if( event.getEventType() == XMLStreamConstants.COMMENT ) {
+            if (event.getEventType() == XMLStreamConstants.COMMENT) {
                 eventReader.nextEvent();
                 continue;
             }
 
-            if( event.isCharacters() ) {
+            if (event.isCharacters()) {
                 Characters chars = event.asCharacters();
-                if( chars.isWhiteSpace() ) {
+                if (chars.isWhiteSpace()) {
                     eventReader.nextEvent();
                     continue;
                 }
@@ -235,7 +235,8 @@ public class XmlReader {
     private Object resolveValueOfElement(String value, StartElement startElement) {
         // it could be instance-identifier Built-In Type
         if (value.startsWith("/")) {
-            IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml(startElement));
+            IdentityValuesDTO iiValue = RestUtil.asInstanceIdentifier(value, new RestUtil.PrefixMapingFromXml(
+                    startElement));
             if (iiValue != null) {
                 return iiValue;
             }
@@ -245,7 +246,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],value);
+                return new IdentityValuesDTO(namespace, namespaceAndValue[1], namespaceAndValue[0], value);
             }
         }
         // it is not "prefix:value" but just "value"
index 3834a383db8e94da989f7b1bd4076285cd250935..4e1adbc598be2807bccf1f0e8bd76926e141d55d 100644 (file)
@@ -25,6 +25,7 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Future;
 import javax.ws.rs.core.Response;
@@ -1136,6 +1137,7 @@ public class RestconfImpl implements RestconfService {
             final DataNodeContainer schema, final MountInstance mountPoint,
             final QName currentAugment ) {
         final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
+        checkNodeMultiplicityAccordingToSchema(schema,children);
         for (final NodeWrapper<? extends Object> child : children) {
             final List<DataSchemaNode> potentialSchemaNodes =
                     this.controllerContext.findInstanceDataChildrenByName(
@@ -1196,6 +1198,29 @@ public class RestconfImpl implements RestconfService {
         }
     }
 
+    private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
+            final List<NodeWrapper<?>> nodes) {
+        Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
+        for (NodeWrapper<?> child : nodes) {
+            Integer count = equalNodeNamesToCounts.get(child.getLocalName());
+            equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count);
+        }
+
+        for (DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) {
+            if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) {
+                String localName = childSchemaNode.getQName().getLocalName();
+                Integer count = equalNodeNamesToCounts.get(localName);
+                if (count != null && count > 1) {
+                    throw new RestconfDocumentedException(
+                            "Multiple input data elements were specified for '"
+                            + childSchemaNode.getQName().getLocalName()
+                            + "'. The data for this element type can only be specified once.",
+                            ErrorType.APPLICATION, ErrorTag.BAD_ELEMENT);
+                }
+            }
+        }
+    }
+
     private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder,
             final DataSchemaNode schema, final QName previousAugment,
             final MountInstance mountPoint) {
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MultipleEqualNamesForDataNodesTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MultipleEqualNamesForDataNodesTest.java
new file mode 100644 (file)
index 0000000..4213102
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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.restconf.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.ext.MessageBodyReader;
+import org.junit.Test;
+import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
+import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * If more then one data element with equal name exists where container or
+ * leaf schema node should be present the RestconfDocumentedException has to
+ * be raised
+ *
+ * Tests for BUG 1204
+ */
+public class MultipleEqualNamesForDataNodesTest {
+
+    @Test
+    public void multipleEqualNameDataNodeTestForContainerJsonTest() {
+        multipleEqualNameDataNodeTest("/equal-data-node-names/equal-name-data-for-container.json", ErrorType.APPLICATION,
+                ErrorTag.BAD_ELEMENT,JsonToCompositeNodeProvider.INSTANCE);
+    }
+
+    @Test
+    public void multipleEqualNameDataNodeTestForLeafJsonTest() {
+        multipleEqualNameDataNodeTest("/equal-data-node-names/equal-name-data-for-leaf.json", ErrorType.PROTOCOL,
+                ErrorTag.MALFORMED_MESSAGE,JsonToCompositeNodeProvider.INSTANCE);
+    }
+
+    @Test
+    public void multipleEqualNameDataNodeTestForContainerXmlTest() {
+        multipleEqualNameDataNodeTest("/equal-data-node-names/equal-name-data-for-container.xml", ErrorType.APPLICATION,
+                ErrorTag.BAD_ELEMENT,XmlToCompositeNodeProvider.INSTANCE);
+    }
+
+    @Test
+    public void multipleEqualNameDataNodeTestForLeafXmlTest() {
+        multipleEqualNameDataNodeTest("/equal-data-node-names/equal-name-data-for-leaf.xml", ErrorType.APPLICATION,
+                ErrorTag.BAD_ELEMENT,XmlToCompositeNodeProvider.INSTANCE);
+    }
+
+    private void multipleEqualNameDataNodeTest(String path, ErrorType errorType, ErrorTag errorTag,MessageBodyReader<CompositeNode> messageBodyReader) {
+        try {
+            CompositeNode compositeNode = TestUtils.readInputToCnSn(path, false,messageBodyReader);
+            assertNotNull(compositeNode);
+
+            Set<Module> modules = null;
+            modules = TestUtils.loadModulesFrom("/equal-data-node-names/yang");
+            assertNotNull(modules);
+
+            TestUtils.normalizeCompositeNode(compositeNode, modules, "equal-data-node-names" + ":" + "cont");
+            fail("Exception RestconfDocumentedException should be raised");
+        } catch (RestconfDocumentedException e) {
+            List<RestconfError> errors = e.getErrors();
+            assertNotNull(errors);
+
+            assertEquals(1, errors.size());
+
+            RestconfError restconfError = errors.get(0);
+
+            assertEquals(errorType, restconfError.getErrorType());
+            assertEquals(errorTag, restconfError.getErrorTag());
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-container.json b/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-container.json
new file mode 100644 (file)
index 0000000..78b097d
--- /dev/null
@@ -0,0 +1,16 @@
+{
+    "cont":{
+        "cont1":[
+            {
+                "lst11":{
+                    "lf111":"value1"
+                }
+            },
+            {
+                "lst11":{
+                    "lf111":"value2"
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-container.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-container.xml
new file mode 100644 (file)
index 0000000..26a1f5f
--- /dev/null
@@ -0,0 +1,12 @@
+<cont>
+    <cont1>
+        <lst11>
+            <lf111>value1</lf111>
+        </lst11>
+    </cont1>
+    <cont1>
+        <lst11>
+            <lf111>value1</lf111>
+        </lst11>
+    </cont1>
+</cont>
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.json b/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.json
new file mode 100644 (file)
index 0000000..d0a9fd4
--- /dev/null
@@ -0,0 +1,15 @@
+{
+    "cont":{
+        "cont1":{
+            "lst11":[
+             {
+                "lf111":"value1",
+                "lf111":"value2"
+             },
+             {
+                "lf111":"value3"
+             }
+             ]
+         }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.xml
new file mode 100644 (file)
index 0000000..4221c2d
--- /dev/null
@@ -0,0 +1,11 @@
+<cont>
+    <cont1>
+        <lst11>
+          <lf111>value1</lf111>,
+          <lf111>value2</lf111>,
+        </lst11>
+        <lst11>
+          <lf111>value3</lf111>,
+        </lst11>
+    </cont1>
+</cont>
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/yang/equal-data-node-names.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/equal-data-node-names/yang/equal-data-node-names.yang
new file mode 100644 (file)
index 0000000..3a3653a
--- /dev/null
@@ -0,0 +1,18 @@
+/* bug 1204 */
+module equal-data-node-names {
+  namespace "ns:equal:data:node:names";
+
+  prefix "eqdanona";
+  revision 2014-06-26 {
+  }
+
+  container cont {
+    container cont1 {
+        list lst11 {
+            leaf lf111 {
+                type string;
+            }
+        }
+    }
+  }
+}
\ No newline at end of file
index 10d964d6449595e13971a80428046ac6508871a2..b886fc9da9621459dc12531c22ea1d6ac9d360e2 100644 (file)
@@ -1,5 +1,5 @@
 {
-       "cont": {       
-               "lf":                           
-       }
+    "cont": {
+        "lf":
+    }
 }
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/multiple-nodes/multiple-nodes.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/multiple-nodes/multiple-nodes.yang
new file mode 100644 (file)
index 0000000..22a1dae
--- /dev/null
@@ -0,0 +1,17 @@
+module multiple-nodes {
+    namespace "multiple:nodes";
+    prefix "mod1";
+    revision "2014-06-23";
+
+    container cont {
+        container cont1 {
+            leaf lf11 {
+                type string;
+            }
+        }
+
+        leaf lf1 {
+            type string;
+        }
+    }
+}
\ No newline at end of file