Bug 7246 - Fix of SchemaTracker initialization and lookup of schema nodes 40/62540/2
authorPeter Kajsa <pkajsa@cisco.com>
Mon, 14 Aug 2017 17:35:33 +0000 (19:35 +0200)
committerRobert Varga <nite@hq.sk>
Tue, 12 Sep 2017 07:50:01 +0000 (07:50 +0000)
SchemaUtils methods perform lookup in both the namespace of groupings and
the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs,
actions, notifications, anydatas, and anyxmls. In consequence, performed
lookups are ambiguous due to possible name conflicts between these namespaces.

Change-Id: Icd3e141b21d0adaf0126b539454e640c0ea0b5a6
Signed-off-by: Peter Kajsa <pkajsa@cisco.com>
(cherry picked from commit d77eb113748d9571c5bd9588e8f6c745182b8b05)

yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug7246Test.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug7246/json/expected-output.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/bug7246/yang/rpc-test.yang [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaOrderedNormalizedNodeWriter.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtils.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtilsTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/schema-utils-test/foo.yang [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/schema-utils-test/name-conflicts.yang [new file with mode: 0644]

diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug7246Test.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/Bug7246Test.java
new file mode 100644 (file)
index 0000000..682fcc6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017 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.yangtools.yang.data.codec.gson;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Bug7246Test {
+    private static String NS = "my-namespace";
+    private static String REV = "1970-01-01";
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource("/bug7246/yang/rpc-test.yang");
+        final JsonParser parser = new JsonParser();
+        final JsonElement expextedJson = parser
+                .parse(new FileReader(new File(getClass().getResource("/bug7246/json/expected-output.json").toURI())));
+
+        final DataContainerChild<? extends PathArgument, ?> inputStructure = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(qN("my-name")))
+                .withChild(ImmutableNodes.leafNode(new NodeIdentifier(qN("my-name")), "my-value")).build();
+        final SchemaPath rootPath = SchemaPath.create(true, qN("my-name"), qN("input"));
+        final Writer writer = new StringWriter();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(schemaContext, rootPath, writer,
+                inputStructure);
+        final JsonElement serializedJson = parser.parse(jsonOutput);
+
+        assertEquals(expextedJson, serializedJson);
+    }
+
+    private QName qN(final String localName) {
+        return QName.create(NS, REV, localName);
+    }
+
+    private static String normalizedNodeToJsonStreamTransformation(final SchemaContext schemaContext,
+            final SchemaPath path, final Writer writer, final NormalizedNode<?, ?> inputStructure)
+            throws IOException, URISyntaxException {
+
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter(
+                JSONCodecFactory.getShared(schemaContext), path, new URI(NS),
+                JsonWriterFactory.createJsonWriter(writer, 2));
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+        nodeWriter.write(inputStructure);
+
+        nodeWriter.close();
+        return writer.toString();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug7246/json/expected-output.json b/yang/yang-data-codec-gson/src/test/resources/bug7246/json/expected-output.json
new file mode 100644 (file)
index 0000000..6dba63e
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "my-name": {
+        "my-name": "my-value"
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/bug7246/yang/rpc-test.yang b/yang/yang-data-codec-gson/src/test/resources/bug7246/yang/rpc-test.yang
new file mode 100644 (file)
index 0000000..004183e
--- /dev/null
@@ -0,0 +1,37 @@
+module rpc-test {
+    namespace my-namespace;
+    prefix p;
+
+    feature my-name;
+
+    identity my-name;
+
+    extension my-name;
+
+    typedef my-name {
+        type string;
+    }
+
+    grouping my-name {
+        leaf my-name {
+            type my-name;
+        }
+    }
+
+    rpc my-name {
+        input {
+            container my-name {
+                leaf my-name {
+                    type my-name;
+                }
+            }
+        }
+        output {
+            container my-name {
+                leaf my-name {
+                    type my-name;
+                }
+            }
+        }
+    }
+}
index 75722e7f68c6cfa3443aed94e25f744f90c2947f..1609aaa597c29847eff9b3cec1de50808616d917 100644 (file)
@@ -12,8 +12,10 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.util.ArrayDeque;
+import java.util.Collection;
 import java.util.Deque;
 import java.util.HashSet;
+import java.util.Optional;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -52,9 +54,18 @@ public final class SchemaTracker {
     private final DataNodeContainer root;
 
     private SchemaTracker(final SchemaContext context, final SchemaPath path) {
-        SchemaNode current = SchemaUtils.findParentSchemaOnPath(context, path);
-        Preconditions.checkArgument(current instanceof DataNodeContainer,"Schema path must point to container or list or an rpc input/output. Supplied path %s pointed to: %s",path,current);
-        root = (DataNodeContainer) current;
+        final Collection<SchemaNode> schemaNodes = SchemaUtils.findParentSchemaNodesOnPath(context, path);
+        Preconditions.checkArgument(!schemaNodes.isEmpty(), "Unable to find schema node for supplied schema path: %s",
+                path);
+        if (schemaNodes.size() > 1) {
+            LOG.warn("More possible schema nodes {} for supplied schema path {}", schemaNodes, path);
+        }
+        final Optional<SchemaNode> current = schemaNodes.stream().filter(node -> node instanceof DataNodeContainer)
+                .findFirst();
+        Preconditions.checkArgument(current.isPresent(),
+                "Schema path must point to container or list or an rpc input/output. Supplied path %s pointed to: %s",
+                path, current);
+        root = (DataNodeContainer) current.get();
     }
 
     /**
@@ -174,7 +185,7 @@ public final class SchemaTracker {
             return (LeafListSchemaNode) parent;
         }
 
-        final SchemaNode child = SchemaUtils.findChildSchemaByQName((SchemaNode) parent, qname);
+        final SchemaNode child = SchemaUtils.findDataChildSchemaByQName((SchemaNode) parent, qname);
         Preconditions.checkArgument(child instanceof LeafListSchemaNode,
             "Node %s is neither a leaf-list nor currently in a leaf-list", child.getPath());
         return (LeafListSchemaNode) child;
index 037265f94f00e4764e5d082bf4df4cb2541638f3..4cd619b8116767cefbd006c99efb4561ca1ca1d5 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ArrayListMultimap;
 import java.io.IOException;
 import java.util.Collection;
@@ -26,13 +27,15 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * This is an iterator over a {@link NormalizedNode}. Unlike {@link NormalizedNodeWriter},
  * this iterates over elements in order as they are defined in .yang file.
  */
 public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
-
+    private static final Logger LOG = LoggerFactory.getLogger(SchemaOrderedNormalizedNodeWriter.class);
     private final SchemaContext schemaContext;
     private final SchemaNode root;
     private final NormalizedNodeStreamWriter writer;
@@ -41,15 +44,26 @@ public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
 
     /**
      * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
-     * @param writer Back-end writer
-     * @param schemaContext Schema context
-     * @param path path
+     *
+     * @param writer
+     *            Back-end writer
+     * @param schemaContext
+     *            Schema context
+     * @param path
+     *            path
      */
-    public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, final SchemaPath path) {
+    public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext,
+            final SchemaPath path) {
         super(writer);
         this.writer = writer;
         this.schemaContext = schemaContext;
-        this.root = SchemaUtils.findParentSchemaOnPath(schemaContext, path);
+        final Collection<SchemaNode> schemaNodes = SchemaUtils.findParentSchemaNodesOnPath(schemaContext, path);
+        Preconditions.checkArgument(!schemaNodes.isEmpty(), "Unable to find schema node for supplied schema path: %s",
+                path);
+        if (schemaNodes.size() > 1) {
+            LOG.warn("More possible schema nodes {} for supplied schema path {}", schemaNodes, path);
+        }
+        this.root = schemaNodes.iterator().next();
     }
 
     @Override
@@ -102,19 +116,20 @@ public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
     }
 
     private void write(final List<NormalizedNode<?, ?>> nodes, final SchemaNode dataSchemaNode) throws IOException {
-        for (NormalizedNode<?, ?> node : nodes) {
+        for (final NormalizedNode<?, ?> node : nodes) {
             write(node, dataSchemaNode);
         }
     }
 
+    @Override
     protected boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException {
         return writeChildren(children, currentSchemaNode, true);
     }
 
-    private boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children, final SchemaNode parentSchemaNode, boolean endParent) throws IOException {
+    private boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children, final SchemaNode parentSchemaNode, final boolean endParent) throws IOException {
         //Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved
-        ArrayListMultimap<QName, NormalizedNode<?, ?>> qNameToNodes = ArrayListMultimap.create();
-        for (NormalizedNode<?, ?> child : children) {
+        final ArrayListMultimap<QName, NormalizedNode<?, ?>> qNameToNodes = ArrayListMultimap.create();
+        for (final NormalizedNode<?, ?> child : children) {
             if (child instanceof AugmentationNode) {
                 qNameToNodes.putAll(resolveAugmentations(child));
             } else {
@@ -126,20 +141,20 @@ public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
             if (parentSchemaNode instanceof ListSchemaNode && qNameToNodes.containsKey(parentSchemaNode.getQName())) {
                 write(qNameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode);
             } else {
-                for (DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
+                for (final DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) {
                     write(qNameToNodes.get(schemaNode.getQName()), schemaNode);
                 }
             }
         } else if (parentSchemaNode instanceof ChoiceSchemaNode) {
-            for (ChoiceCaseNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases()) {
-                for (DataSchemaNode dsn : ccNode.getChildNodes()) {
+            for (final ChoiceCaseNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases()) {
+                for (final DataSchemaNode dsn : ccNode.getChildNodes()) {
                     if (qNameToNodes.containsKey(dsn.getQName())) {
                         write(qNameToNodes.get(dsn.getQName()), dsn);
                     }
                 }
             }
         } else {
-            for (NormalizedNode<?, ?> child : children) {
+            for (final NormalizedNode<?, ?> child : children) {
                 writeLeaf(child);
             }
         }
@@ -159,7 +174,7 @@ public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter {
 
     private ArrayListMultimap<QName, NormalizedNode<?, ?>> resolveAugmentations(final NormalizedNode<?, ?> child) {
         final ArrayListMultimap<QName, NormalizedNode<?, ?>> resolvedAugs = ArrayListMultimap.create();
-        for (NormalizedNode<?, ?> node : ((AugmentationNode) child).getValue()) {
+        for (final NormalizedNode<?, ?> node : ((AugmentationNode) child).getValue()) {
             if (node instanceof AugmentationNode) {
                 resolvedAugs.putAll(resolveAugmentations(node));
             } else {
index 4c9eb686c6adeb1b4b130ce04a3c8b792bc3bc6a..1999c657d5c8f80c83bcd9e7dbbfa2ecdc9ae5fb 100644 (file)
@@ -11,13 +11,16 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.annotation.Nullable;
@@ -25,12 +28,14 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
@@ -49,19 +54,19 @@ public final class SchemaUtils {
     public static Optional<DataSchemaNode> findFirstSchema(final QName qname, final Iterable<DataSchemaNode> dataSchemaNode) {
         DataSchemaNode sNode = null;
         if (dataSchemaNode != null && qname != null) {
-            for (DataSchemaNode dsn : dataSchemaNode) {
+            for (final DataSchemaNode dsn : dataSchemaNode) {
                 if (qname.isEqualWithoutRevision(dsn.getQName())) {
                     if (sNode == null || sNode.getQName().getRevision().compareTo(dsn.getQName().getRevision()) < 0) {
                         sNode = dsn;
                     }
                 } else if (dsn instanceof ChoiceSchemaNode) {
-                    for (ChoiceCaseNode choiceCase : ((ChoiceSchemaNode) dsn).getCases()) {
+                    for (final ChoiceCaseNode choiceCase : ((ChoiceSchemaNode) dsn).getCases()) {
 
                         final DataSchemaNode dataChildByName = choiceCase.getDataChildByName(qname);
                         if (dataChildByName != null) {
                             return Optional.of(dataChildByName);
                         }
-                        Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
+                        final Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
                         if (foundDsn.isPresent()) {
                             return foundDsn;
                         }
@@ -93,7 +98,7 @@ public final class SchemaUtils {
             return findSchemaForChild(schema, qname);
         }
 
-        Optional<DataSchemaNode> childSchemaOptional = findFirstSchema(qname, schema.getChildNodes());
+        final Optional<DataSchemaNode> childSchemaOptional = findFirstSchema(qname, schema.getChildNodes());
         if (!childSchemaOptional.isPresent()) {
             return null;
         }
@@ -101,14 +106,14 @@ public final class SchemaUtils {
     }
 
     public static DataSchemaNode findSchemaForChild(final DataNodeContainer schema, final QName qname, final Iterable<DataSchemaNode> childNodes) {
-        Optional<DataSchemaNode> childSchema = findFirstSchema(qname, childNodes);
+        final Optional<DataSchemaNode> childSchema = findFirstSchema(qname, childNodes);
         Preconditions.checkState(childSchema.isPresent(),
                 "Unknown child(ren) node(s) detected, identified by: %s, in: %s", qname, schema);
         return childSchema.get();
     }
 
     public static AugmentationSchema findSchemaForAugment(final AugmentationTarget schema, final Set<QName> qNames) {
-        Optional<AugmentationSchema> schemaForAugment = findAugment(schema, qNames);
+        final Optional<AugmentationSchema> schemaForAugment = findAugment(schema, qNames);
         Preconditions.checkState(schemaForAugment.isPresent(), "Unknown augmentation node detected, identified by: %s, in: %s",
                 qNames, schema);
         return schemaForAugment.get();
@@ -117,7 +122,7 @@ public final class SchemaUtils {
     public static AugmentationSchema findSchemaForAugment(final ChoiceSchemaNode schema, final Set<QName> qNames) {
         Optional<AugmentationSchema> schemaForAugment = Optional.absent();
 
-        for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
+        for (final ChoiceCaseNode choiceCaseNode : schema.getCases()) {
             schemaForAugment = findAugment(choiceCaseNode, qNames);
             if (schemaForAugment.isPresent()) {
                 break;
@@ -130,8 +135,8 @@ public final class SchemaUtils {
     }
 
     private static Optional<AugmentationSchema> findAugment(final AugmentationTarget schema, final Set<QName> qNames) {
-        for (AugmentationSchema augment : schema.getAvailableAugmentations()) {
-            HashSet<QName> qNamesFromAugment = Sets.newHashSet(Collections2.transform(augment.getChildNodes(),
+        for (final AugmentationSchema augment : schema.getAvailableAugmentations()) {
+            final HashSet<QName> qNamesFromAugment = Sets.newHashSet(Collections2.transform(augment.getChildNodes(),
                 DataSchemaNode::getQName));
 
             if (qNamesFromAugment.equals(qNames)) {
@@ -143,8 +148,8 @@ public final class SchemaUtils {
     }
 
     public static DataSchemaNode findSchemaForChild(final ChoiceSchemaNode schema, final QName childPartialQName) {
-        for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
-            Optional<DataSchemaNode> childSchema = findFirstSchema(childPartialQName, choiceCaseNode.getChildNodes());
+        for (final ChoiceCaseNode choiceCaseNode : schema.getCases()) {
+            final Optional<DataSchemaNode> childSchema = findFirstSchema(childPartialQName, choiceCaseNode.getChildNodes());
             if (childSchema.isPresent()) {
                 return childSchema.get();
             }
@@ -166,7 +171,7 @@ public final class SchemaUtils {
     }
 
     private static Map<QName, ChoiceSchemaNode> mapChildElementsFromChoices(final DataNodeContainer schema, final Iterable<DataSchemaNode> childNodes) {
-        Map<QName, ChoiceSchemaNode> mappedChoices = Maps.newLinkedHashMap();
+        final Map<QName, ChoiceSchemaNode> mappedChoices = Maps.newLinkedHashMap();
 
         for (final DataSchemaNode childSchema : childNodes) {
             if (childSchema instanceof ChoiceSchemaNode) {
@@ -175,9 +180,9 @@ public final class SchemaUtils {
                     continue;
                 }
 
-                for (ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) childSchema).getCases()) {
+                for (final ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) childSchema).getCases()) {
 
-                    for (QName qName : getChildNodesRecursive(choiceCaseNode)) {
+                    for (final QName qName : getChildNodesRecursive(choiceCaseNode)) {
                         mappedChoices.put(qName, (ChoiceSchemaNode) childSchema);
                     }
                 }
@@ -192,7 +197,7 @@ public final class SchemaUtils {
             return false;
         }
 
-        for (AugmentationSchema augmentationSchema : ((AugmentationTarget) schema).getAvailableAugmentations()) {
+        for (final AugmentationSchema augmentationSchema : ((AugmentationTarget) schema).getAvailableAugmentations()) {
             if (augmentationSchema.getDataChildByName(childSchema.getQName()) != null) {
                 return true;
             }
@@ -209,12 +214,12 @@ public final class SchemaUtils {
      */
     public static Map<QName, AugmentationSchema> mapChildElementsFromAugments(final AugmentationTarget schema) {
 
-        Map<QName, AugmentationSchema> childNodesToAugmentation = Maps.newLinkedHashMap();
+        final Map<QName, AugmentationSchema> childNodesToAugmentation = Maps.newLinkedHashMap();
 
         // Find QNames of augmented child nodes
-        Map<QName, AugmentationSchema> augments = Maps.newHashMap();
+        final Map<QName, AugmentationSchema> augments = Maps.newHashMap();
         for (final AugmentationSchema augmentationSchema : schema.getAvailableAugmentations()) {
-            for (DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
+            for (final DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
                 augments.put(dataSchemaNode.getQName(), augmentationSchema);
             }
         }
@@ -223,22 +228,22 @@ public final class SchemaUtils {
         // because nodes from augment do not contain nodes from other augmentations
         if (schema instanceof DataNodeContainer) {
 
-            for (DataSchemaNode child : ((DataNodeContainer) schema).getChildNodes()) {
+            for (final DataSchemaNode child : ((DataNodeContainer) schema).getChildNodes()) {
                 // If is not augmented child, continue
                 if (!(augments.containsKey(child.getQName()))) {
                     continue;
                 }
 
-                AugmentationSchema mostTopAugmentation = augments.get(child.getQName());
+                final AugmentationSchema mostTopAugmentation = augments.get(child.getQName());
 
                 // recursively add all child nodes in case of augment, case and choice
                 if (child instanceof AugmentationSchema || child instanceof ChoiceCaseNode) {
-                    for (QName qName : getChildNodesRecursive((DataNodeContainer) child)) {
+                    for (final QName qName : getChildNodesRecursive((DataNodeContainer) child)) {
                         childNodesToAugmentation.put(qName, mostTopAugmentation);
                     }
                 } else if (child instanceof ChoiceSchemaNode) {
-                    for (ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) child).getCases()) {
-                        for (QName qName : getChildNodesRecursive(choiceCaseNode)) {
+                    for (final ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) child).getCases()) {
+                        for (final QName qName : getChildNodesRecursive(choiceCaseNode)) {
                             childNodesToAugmentation.put(qName, mostTopAugmentation);
                         }
                     }
@@ -250,12 +255,12 @@ public final class SchemaUtils {
 
         // Choice Node has to map child nodes from all its cases
         if (schema instanceof ChoiceSchemaNode) {
-            for (ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) schema).getCases()) {
+            for (final ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) schema).getCases()) {
                 if (!(augments.containsKey(choiceCaseNode.getQName()))) {
                     continue;
                 }
 
-                for (QName qName : getChildNodesRecursive(choiceCaseNode)) {
+                for (final QName qName : getChildNodesRecursive(choiceCaseNode)) {
                     childNodesToAugmentation.put(qName, augments.get(choiceCaseNode.getQName()));
                 }
             }
@@ -273,11 +278,11 @@ public final class SchemaUtils {
      * @return set of QNames
      */
     public static Set<QName> getChildNodesRecursive(final DataNodeContainer nodeContainer) {
-        Set<QName> allChildNodes = Sets.newHashSet();
+        final Set<QName> allChildNodes = Sets.newHashSet();
 
-        for (DataSchemaNode childSchema : nodeContainer.getChildNodes()) {
+        for (final DataSchemaNode childSchema : nodeContainer.getChildNodes()) {
             if (childSchema instanceof ChoiceSchemaNode) {
-                for (ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) childSchema).getCases()) {
+                for (final ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) childSchema).getCases()) {
                     allChildNodes.addAll(getChildNodesRecursive(choiceCaseNode));
                 }
             } else if (childSchema instanceof AugmentationSchema || childSchema instanceof ChoiceCaseNode) {
@@ -311,8 +316,8 @@ public final class SchemaUtils {
         if (targetSchema instanceof DataNodeContainer) {
             realChildNodes = getRealSchemasForAugment((DataNodeContainer)targetSchema, augmentSchema);
         } else if (targetSchema instanceof ChoiceSchemaNode) {
-            for (DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
-                for (ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) targetSchema).getCases()) {
+            for (final DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
+                for (final ChoiceCaseNode choiceCaseNode : ((ChoiceSchemaNode) targetSchema).getCases()) {
                     if (getChildNodesRecursive(choiceCaseNode).contains(dataSchemaNode.getQName())) {
                         realChildNodes.add(choiceCaseNode.getDataChildByName(dataSchemaNode.getQName()));
                     }
@@ -325,16 +330,16 @@ public final class SchemaUtils {
 
     public static Set<DataSchemaNode> getRealSchemasForAugment(final DataNodeContainer targetSchema,
             final AugmentationSchema augmentSchema) {
-        Set<DataSchemaNode> realChildNodes = Sets.newHashSet();
-        for (DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
-            DataSchemaNode realChild = targetSchema.getDataChildByName(dataSchemaNode.getQName());
+        final Set<DataSchemaNode> realChildNodes = Sets.newHashSet();
+        for (final DataSchemaNode dataSchemaNode : augmentSchema.getChildNodes()) {
+            final DataSchemaNode realChild = targetSchema.getDataChildByName(dataSchemaNode.getQName());
             realChildNodes.add(realChild);
         }
         return realChildNodes;
     }
 
     public static Optional<ChoiceCaseNode> detectCase(final ChoiceSchemaNode schema, final DataContainerChild<?, ?> child) {
-        for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
+        for (final ChoiceCaseNode choiceCaseNode : schema.getCases()) {
             if (child instanceof AugmentationNode
                     && belongsToCaseAugment(choiceCaseNode, (AugmentationIdentifier) child.getIdentifier())) {
                 return Optional.of(choiceCaseNode);
@@ -347,10 +352,10 @@ public final class SchemaUtils {
     }
 
     public static boolean belongsToCaseAugment(final ChoiceCaseNode caseNode, final AugmentationIdentifier childToProcess) {
-        for (AugmentationSchema augmentationSchema : caseNode.getAvailableAugmentations()) {
+        for (final AugmentationSchema augmentationSchema : caseNode.getAvailableAugmentations()) {
 
-            Set<QName> currentAugmentChildNodes = Sets.newHashSet();
-            for (DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
+            final Set<QName> currentAugmentChildNodes = Sets.newHashSet();
+            for (final DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
                 currentAugmentChildNodes.add(dataSchemaNode.getQName());
             }
 
@@ -372,8 +377,8 @@ public final class SchemaUtils {
      */
     public static AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
         if (parent instanceof AugmentationTarget && !(parent instanceof ChoiceSchemaNode)) {
-            for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
-                DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
+            for (final AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
+                final DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
                 if (childInAugmentation != null) {
                     return augmentation;
                 }
@@ -388,11 +393,46 @@ public final class SchemaUtils {
     }
 
     /**
-     * Finds schema node for given path in schema context.
-     * @param schemaContext schema context
-     * @param path path
+     * Finds schema node for given path in schema context. This method performs
+     * lookup in the namespace of all leafs, leaf-lists, lists, containers,
+     * choices, rpcs, actions, notifications, anydatas, and anyxmls according to
+     * Rfc6050/Rfc7950 section 6.2.1.
+     *
+     * @param schemaContext
+     *            schema context
+     * @param path
+     *            path
+     * @return schema node on path
+     */
+    public static SchemaNode findDataParentSchemaOnPath(final SchemaContext schemaContext, final SchemaPath path) {
+        SchemaNode current = Preconditions.checkNotNull(schemaContext);
+        for (final QName qname : path.getPathFromRoot()) {
+            current = findDataChildSchemaByQName(current, qname);
+        }
+        return current;
+    }
+
+    /**
+     * Finds schema node for given path in schema context. This method performs
+     * lookup in both the namespace of groupings and the namespace of all leafs,
+     * leaf-lists, lists, containers, choices, rpcs, actions, notifications,
+     * anydatas, and anyxmls according to Rfc6050/Rfc7950 section 6.2.1.
+     *
+     * This method is deprecated, because name conflicts can occur between the
+     * namespace of groupings and namespace of data nodes and in consequence
+     * lookup could be ambiguous.
+     *
+     * @param schemaContext
+     *            schema context
+     * @param path
+     *            path
      * @return schema node on path
+     *
+     * @deprecated use
+     *             {@link #findParentSchemaNodesOnPath(SchemaContext, SchemaPath)}
+     *             instead.
      */
+    @Deprecated
     public static SchemaNode findParentSchemaOnPath(final SchemaContext schemaContext, final SchemaPath path) {
         SchemaNode current = Preconditions.checkNotNull(schemaContext);
         for (final QName qname : path.getPathFromRoot()) {
@@ -402,39 +442,46 @@ public final class SchemaUtils {
     }
 
     /**
-     * Find child schema node identified by its QName within a provided schema node.
-     * @param node schema node
-     * @param qname QName
-     * @return child schema node
-     * @throws java.lang.IllegalArgumentException if the schema node does not allow children
+     * Find child data schema node identified by its QName within a provided
+     * schema node. This method performs lookup in the namespace of all leafs,
+     * leaf-lists, lists, containers, choices, rpcs, actions, notifications,
+     * anydatas, and anyxmls according to Rfc6050/Rfc7950 section 6.2.1.
+     *
+     * @param node
+     *            schema node
+     * @param qname
+     *            QName
+     * @return data child schema node
+     * @throws java.lang.IllegalArgumentException
+     *             if the schema node does not allow children
      */
-    public static SchemaNode findChildSchemaByQName(final SchemaNode node, final QName qname) {
+    @Nullable
+    public static SchemaNode findDataChildSchemaByQName(final SchemaNode node, final QName qname) {
         SchemaNode child = null;
-
         if (node instanceof DataNodeContainer) {
             child = ((DataNodeContainer) node).getDataChildByName(qname);
-
             if (child == null && node instanceof SchemaContext) {
-                child = tryFindGroupings((SchemaContext) node, qname).orNull();
+                child = tryFindRpc((SchemaContext) node, qname).orNull();
             }
-
-            if (child == null && node instanceof SchemaContext) {
-                child = tryFindNotification((SchemaContext) node, qname)
-                        .or(tryFindRpc(((SchemaContext) node), qname)).orNull();
+            if (child == null && node instanceof NotificationNodeContainer) {
+                child = tryFindNotification((NotificationNodeContainer) node, qname).orNull();
+            }
+            if (child == null && node instanceof ActionNodeContainer) {
+                child = tryFindAction((ActionNodeContainer) node, qname).orNull();
             }
         } else if (node instanceof ChoiceSchemaNode) {
             child = ((ChoiceSchemaNode) node).getCaseNodeByName(qname);
         } else if (node instanceof RpcDefinition) {
             switch (qname.getLocalName()) {
-                case "input":
-                    child = ((RpcDefinition) node).getInput();
-                    break;
-                case "output":
-                    child = ((RpcDefinition) node).getOutput();
-                    break;
-                default:
-                    child = null;
-                    break;
+            case "input":
+                child = ((RpcDefinition) node).getInput();
+                break;
+            case "output":
+                child = ((RpcDefinition) node).getOutput();
+                break;
+            default:
+                child = null;
+                break;
             }
         } else {
             throw new IllegalArgumentException(String.format("Schema node %s does not allow children.", node));
@@ -443,16 +490,127 @@ public final class SchemaUtils {
         return child;
     }
 
-    private static Optional<SchemaNode> tryFindGroupings(final SchemaContext ctx, final QName qname) {
-        return Optional.fromNullable(Iterables.find(ctx.getGroupings(), new SchemaNodePredicate(qname), null));
+    /**
+     * Find child schema node identified by its QName within a provided schema
+     * node. This method performs lookup in both the namespace of groupings and
+     * the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs,
+     * actions, notifications, anydatas, and anyxmls according to
+     * Rfc6050/Rfc7950 section 6.2.1.
+     *
+     * This method is deprecated, because name conflicts can occur between the
+     * namespace of groupings and namespace of data nodes and in consequence
+     * lookup could be ambiguous.
+     *
+     * @param node
+     *            schema node
+     * @param qname
+     *            QName
+     * @return child schema node
+     * @throws java.lang.IllegalArgumentException
+     *             if the schema node does not allow children
+     *
+     * @deprecated use
+     *             {@link #findChildSchemaNodesByQName(SchemaNode, QName)}
+     *             instead.
+     */
+    @Deprecated
+    public static SchemaNode findChildSchemaByQName(final SchemaNode node, final QName qname) {
+        SchemaNode child = findDataChildSchemaByQName(node, qname);
+        if (child == null && node instanceof DataNodeContainer) {
+            child = tryFindGroupings((DataNodeContainer) node, qname).orNull();
+        }
+
+        return child;
+    }
+
+    /**
+     * Finds schema node for given path in schema context. This method performs
+     * lookup in both the namespace of groupings and the namespace of all leafs,
+     * leaf-lists, lists, containers, choices, rpcs, actions, notifications,
+     * anydatas, and anyxmls according to Rfc6050/Rfc7950 section 6.2.1.
+     *
+     * This method returns collection of SchemaNodes, because name conflicts can
+     * occur between the namespace of groupings and namespace of data nodes.
+     * This method finds and collects all schema nodes that matches supplied
+     * SchemaPath and returns them all as collection of schema nodes.
+     *
+     * @param schemaContext
+     *            schema context
+     * @param path
+     *            path
+     * @return collection of schema nodes on path
+     *
+     */
+    public static Collection<SchemaNode> findParentSchemaNodesOnPath(final SchemaContext schemaContext,
+            final SchemaPath path) {
+        final Collection<SchemaNode> currentNodes = new ArrayList<>();
+        final Collection<SchemaNode> childNodes = new ArrayList<>();
+        currentNodes.add(Preconditions.checkNotNull(schemaContext));
+        for (final QName qname : path.getPathFromRoot()) {
+            for (final SchemaNode current : currentNodes) {
+                childNodes.addAll(findChildSchemaNodesByQName(current, qname));
+            }
+            currentNodes.clear();
+            currentNodes.addAll(childNodes);
+            childNodes.clear();
+        }
+
+        return currentNodes;
+    }
+
+    /**
+     * Find child schema node identified by its QName within a provided schema
+     * node. This method performs lookup in both the namespace of groupings and
+     * the namespace of all leafs, leaf-lists, lists, containers, choices, rpcs,
+     * actions, notifications, anydatas, and anyxmls according to
+     * Rfc6050/Rfc7950 section 6.2.1.
+     *
+     * This method returns collection of SchemaNodes, because name conflicts can
+     * occur between the namespace of groupings and namespace of data nodes.
+     * This method finds and collects all schema nodes with supplied QName and
+     * returns them all as collection of schema nodes.
+     *
+     * @param node
+     *            schema node
+     * @param qname
+     *            QName
+     * @return collection of child schema nodes
+     * @throws java.lang.IllegalArgumentException
+     *             if the schema node does not allow children
+     *
+     */
+    public static Collection<SchemaNode> findChildSchemaNodesByQName(final SchemaNode node, final QName qname) {
+        final List<SchemaNode> childNodes = new ArrayList<>();
+        final SchemaNode dataNode = findDataChildSchemaByQName(node, qname);
+        if (dataNode != null) {
+            childNodes.add(dataNode);
+        }
+        if (node instanceof DataNodeContainer) {
+            final SchemaNode groupingNode = tryFindGroupings((DataNodeContainer) node, qname).orNull();
+            if (groupingNode != null) {
+                childNodes.add(groupingNode);
+            }
+        }
+        return childNodes.isEmpty() ? Collections.emptyList() : ImmutableList.copyOf(childNodes);
+    }
+
+    private static Optional<SchemaNode> tryFindGroupings(final DataNodeContainer dataNodeContainer, final QName qname) {
+        return Optional
+                .fromNullable(Iterables.find(dataNodeContainer.getGroupings(), new SchemaNodePredicate(qname), null));
     }
 
     private static Optional<SchemaNode> tryFindRpc(final SchemaContext ctx, final QName qname) {
         return Optional.fromNullable(Iterables.find(ctx.getOperations(), new SchemaNodePredicate(qname), null));
     }
 
-    private static Optional<SchemaNode> tryFindNotification(final SchemaContext ctx, final QName qname) {
-        return Optional.fromNullable(Iterables.find(ctx.getNotifications(), new SchemaNodePredicate(qname), null));
+    private static Optional<SchemaNode> tryFindNotification(final NotificationNodeContainer notificationContanier,
+            final QName qname) {
+        return Optional.fromNullable(
+                Iterables.find(notificationContanier.getNotifications(), new SchemaNodePredicate(qname), null));
+    }
+
+    private static Optional<SchemaNode> tryFindAction(final ActionNodeContainer actionContanier, final QName qname) {
+        return Optional.fromNullable(Iterables.find(actionContanier.getActions(), new SchemaNodePredicate(qname), null));
     }
 
     private static final class SchemaNodePredicate implements Predicate<SchemaNode> {
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtilsTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/SchemaUtilsTest.java
new file mode 100644 (file)
index 0000000..7fb8e4a
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017 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.yangtools.yang.data.impl.schema;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class SchemaUtilsTest {
+    private static String NS = "my-namespace";
+    private static String REV = "1970-01-01";
+
+    @Test
+    public void test() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangSource("/schema-utils-test/foo.yang");
+        assertTrue(SchemaUtils.findDataParentSchemaOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name"), qN("my-name"))) instanceof ContainerSchemaNode);
+        assertTrue(SchemaUtils.findDataParentSchemaOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-2"), qN("my-name"))) instanceof NotificationDefinition);
+        assertTrue(SchemaUtils.findDataParentSchemaOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-2"), qN("my-name-2"))) instanceof ActionDefinition);
+    }
+
+    @Test
+    public void testNameConflicts() throws Exception {
+        final SchemaContext schemaContext = YangParserTestUtils
+                .parseYangSource("/schema-utils-test/name-conflicts.yang");
+        // test my-name conflicts
+        assertEquals(8, SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name"), qN("my-name"), qN("my-name"))).size());
+
+        // test target container
+        final Collection<SchemaNode> target = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-2"), qN("my-name-2"), qN("target")));
+        assertEquals(1, target.size());
+        assertTrue(target.iterator().next() instanceof ContainerSchemaNode);
+
+        // test l schema nodes (i.e. container and two leafs)
+        Collection<SchemaNode> l = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-3"), qN("input"), qN("con-3"), qN("l")));
+        assertEquals(1, l.size());
+        assertTrue(l.iterator().next() instanceof ContainerSchemaNode);
+
+        l = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-3"), qN("input"), qN("con-1"), qN("l")));
+        assertEquals(1, l.size());
+        assertTrue(l.iterator().next() instanceof LeafSchemaNode);
+
+        l = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-3"), qN("input"), qN("con-2"), qN("l")));
+        assertTrue(l.isEmpty());
+
+        l = SchemaUtils.findParentSchemaNodesOnPath(schemaContext,
+                SchemaPath.create(true, qN("my-name-3"), qN("output"), qN("con-2"), qN("l")));
+        assertEquals(1, l.size());
+        assertTrue(l.iterator().next() instanceof LeafSchemaNode);
+    }
+
+    private QName qN(final String localName) {
+        return QName.create(NS, REV, localName);
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/resources/schema-utils-test/foo.yang b/yang/yang-data-impl/src/test/resources/schema-utils-test/foo.yang
new file mode 100644 (file)
index 0000000..4311cd2
--- /dev/null
@@ -0,0 +1,39 @@
+module foo {
+    namespace my-namespace;
+    prefix p;
+    yang-version 1.1;
+
+    feature my-name;
+
+    identity my-name;
+
+    extension my-name;
+
+    typedef my-name {
+        type string;
+    }
+
+    grouping my-name {
+    }
+
+    grouping my-name-2 {
+    }
+
+    notification my-name {
+        grouping my-name {
+        }
+        container my-name {
+        }
+    }
+
+    container my-name-2 {
+        grouping my-name {
+        }
+        notification my-name {
+        }
+        grouping my-name-2 {
+        }
+        action my-name-2 {
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/resources/schema-utils-test/name-conflicts.yang b/yang/yang-data-impl/src/test/resources/schema-utils-test/name-conflicts.yang
new file mode 100644 (file)
index 0000000..e0209c7
--- /dev/null
@@ -0,0 +1,90 @@
+module name-conflicts {
+    namespace "my-namespace";
+    prefix nc;
+
+    grouping my-name {
+        grouping my-name {
+            grouping my-name {
+            }
+            container my-name {
+            }
+        }
+        container my-name {
+            grouping my-name {
+            }
+            container my-name {
+            }
+        }
+    }
+
+    container my-name {
+        grouping my-name {
+            grouping my-name {
+            }
+            container my-name {
+            }
+        }
+        container my-name {
+            grouping my-name {
+            }
+            container my-name {
+            }
+        }
+    }
+
+    grouping my-name-2 {
+        grouping my-name-2 {
+            grouping my-name-2 {
+            }
+            container my-name-2 {
+            }
+        }
+        container my-name-2 {
+            grouping my-name-2 {
+            }
+            container my-name-2 {
+            }
+        }
+    }
+
+    container my-name-2 {
+        grouping my-name-2 {
+            grouping my-name-2 {
+            }
+            container target {
+            }
+        }
+        container my-name-2 {
+            grouping my-name-2 {
+            }
+            container my-name-2 {
+            }
+        }
+    }
+
+    rpc my-name-3 {
+        input {
+            container con-1 {
+                leaf l {
+                    type string;
+                }
+            }
+        }
+        output {
+            container con-2 {
+                leaf l {
+                    type string;
+                }
+            }
+        }
+    }
+
+    grouping my-name-3 {
+        container input {
+            container con-3 {
+                container l {
+                }
+            }
+        }
+    }
+}