[ONOS-6694] Multi augment with same name.

Change-Id: Ib319944816679c5c9a703ff771bdfbd0d6af67ab
diff --git a/compiler/base/datamodel/src/main/java/org/onosproject/yang/compiler/datamodel/YangAugment.java b/compiler/base/datamodel/src/main/java/org/onosproject/yang/compiler/datamodel/YangAugment.java
index 934a869..c7a2422 100644
--- a/compiler/base/datamodel/src/main/java/org/onosproject/yang/compiler/datamodel/YangAugment.java
+++ b/compiler/base/datamodel/src/main/java/org/onosproject/yang/compiler/datamodel/YangAugment.java
@@ -158,6 +158,11 @@
     private String setterMethodName;
 
     /**
+     * Logical node of YANG augment.
+     */
+    private YangAugment logicalNode;
+
+    /**
      * Create a YANG augment node.
      */
     public YangAugment() {
@@ -569,4 +574,22 @@
     public void setSetterMethodName(String name) {
         setterMethodName = name;
     }
+
+    /**
+     * Returns the logical YANG augment node.
+     *
+     * @return logical YANG augment
+     */
+    public YangAugment getLogicalNode() {
+        return logicalNode;
+    }
+
+    /**
+     * Sets the logical YANG augment node.
+     *
+     * @param logicalNode logical YANG augment
+     */
+    public void setLogicalNode(YangAugment logicalNode) {
+        this.logicalNode = logicalNode;
+    }
 }
diff --git a/compiler/base/parser/src/main/java/org/onosproject/yang/compiler/parser/impl/listeners/AugmentListener.java b/compiler/base/parser/src/main/java/org/onosproject/yang/compiler/parser/impl/listeners/AugmentListener.java
index 4f77d69..dbe46a8 100644
--- a/compiler/base/parser/src/main/java/org/onosproject/yang/compiler/parser/impl/listeners/AugmentListener.java
+++ b/compiler/base/parser/src/main/java/org/onosproject/yang/compiler/parser/impl/listeners/AugmentListener.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.yang.compiler.parser.impl.listeners;
 
+import org.antlr.v4.runtime.Token;
 import org.onosproject.yang.compiler.datamodel.YangAtomicPath;
 import org.onosproject.yang.compiler.datamodel.YangAugment;
 import org.onosproject.yang.compiler.datamodel.YangModule;
@@ -49,9 +50,12 @@
 import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerErrorType.MISSING_CURRENT_HOLDER;
 import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerErrorType.MISSING_HOLDER;
 import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerErrorType.UNHANDLED_PARSED_DATA;
+import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerUtil.checkAugNameCollision;
 import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerUtil.getPrefixRemovedName;
 import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerUtil.getValidAbsoluteSchemaNodeId;
+import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerUtil.isDuplicateNode;
 import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerUtil.parseUsesAugment;
+import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerUtil.removeAugment;
 import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerUtil.removeQuotesAndHandleConcat;
 import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerValidation.checkStackIsNotEmpty;
 import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerValidation.validateCardinalityEitherOne;
@@ -141,6 +145,7 @@
         augment.setName(removeQuotesAndHandleConcat(ctx.augment().getText()));
         augment.setPrefixRemovedName(name);
 
+        checkAugNameCollision(root, augment);
         try {
             root.addChild(augment);
         } catch (DataModelException e) {
@@ -149,11 +154,6 @@
                     ctx.augment().getText(), ENTRY, e.getMessage()));
         }
         listener.getParsedDataStack().push(augment);
-
-        // Adds resolution info to the list
-        YangResolutionInfoImpl<YangAugment> info =
-                new YangResolutionInfoImpl<>(augment, root, line, pos);
-        addToResolution(info, ctx);
     }
 
     /**
@@ -175,7 +175,19 @@
                     MISSING_CURRENT_HOLDER, AUGMENT_DATA,
                     ctx.augment().getText(), EXIT));
         }
-        listener.getParsedDataStack().pop();
+        YangAugment augment = (YangAugment) listener.getParsedDataStack().pop();
+
+        boolean isDup = isDuplicateNode(augment);
+        if (isDup) {
+            removeAugment(augment);
+        } else {
+            Token txt = ctx.getStart();
+            YangResolutionInfoImpl<YangAugment> info =
+                    new YangResolutionInfoImpl<>(augment, augment.getParent(),
+                                                 txt.getLine(),
+                                                 txt.getCharPositionInLine());
+            addToResolution(info, ctx);
+        }
     }
 
     /**
diff --git a/compiler/base/parser/src/main/java/org/onosproject/yang/compiler/parser/impl/parserutils/ListenerUtil.java b/compiler/base/parser/src/main/java/org/onosproject/yang/compiler/parser/impl/parserutils/ListenerUtil.java
index aaadaf2..25ec87b 100644
--- a/compiler/base/parser/src/main/java/org/onosproject/yang/compiler/parser/impl/parserutils/ListenerUtil.java
+++ b/compiler/base/parser/src/main/java/org/onosproject/yang/compiler/parser/impl/parserutils/ListenerUtil.java
@@ -18,7 +18,10 @@
 
 import org.antlr.v4.runtime.ParserRuleContext;
 import org.onosproject.yang.compiler.datamodel.YangAtomicPath;
+import org.onosproject.yang.compiler.datamodel.YangAugment;
 import org.onosproject.yang.compiler.datamodel.YangImport;
+import org.onosproject.yang.compiler.datamodel.YangLeaf;
+import org.onosproject.yang.compiler.datamodel.YangLeafList;
 import org.onosproject.yang.compiler.datamodel.YangLeafRef;
 import org.onosproject.yang.compiler.datamodel.YangModule;
 import org.onosproject.yang.compiler.datamodel.YangNode;
@@ -29,9 +32,11 @@
 import org.onosproject.yang.compiler.datamodel.YangRelativePath;
 import org.onosproject.yang.compiler.datamodel.YangSubModule;
 import org.onosproject.yang.compiler.datamodel.YangUniqueHolder;
+import org.onosproject.yang.compiler.datamodel.exceptions.DataModelException;
 import org.onosproject.yang.compiler.datamodel.utils.YangConstructType;
 import org.onosproject.yang.compiler.parser.antlrgencode.GeneratedYangParser.AugmentStatementContext;
 import org.onosproject.yang.compiler.parser.exceptions.ParserException;
+import org.onosproject.yang.compiler.translator.tojava.javamodel.YangJavaAugmentTranslator;
 import org.slf4j.Logger;
 
 import java.text.ParseException;
@@ -51,6 +56,9 @@
 import static org.onosproject.yang.compiler.datamodel.utils.YangConstructType.getYangConstructType;
 import static org.onosproject.yang.compiler.parser.antlrgencode.GeneratedYangParser.PathStatementContext;
 import static org.onosproject.yang.compiler.parser.antlrgencode.GeneratedYangParser.YangVersionStatementContext;
+import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerErrorLocation.ENTRY;
+import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerErrorMessageConstruction.constructExtendedListenerErrorMessage;
+import static org.onosproject.yang.compiler.parser.impl.parserutils.ListenerErrorType.UNHANDLED_PARSED_DATA;
 import static org.onosproject.yang.compiler.utils.UtilConstants.ADD;
 import static org.onosproject.yang.compiler.utils.UtilConstants.ANCESTOR;
 import static org.onosproject.yang.compiler.utils.UtilConstants.AT;
@@ -1030,4 +1038,145 @@
         }
         return builder.toString();
     }
+
+    /**
+     * Checks if the augment node is a duplicate node. If the augment is
+     * duplicate, then it adds all its children to the logical node.
+     *
+     * @param augment YANG augment
+     * @return true if it is a duplicate node; false otherwise
+     */
+    public static boolean isDuplicateNode(YangAugment augment) {
+        YangAugment logical = augment.getLogicalNode();
+        if (logical == null) {
+            return false;
+        }
+        YangNode lastChild = logical;
+
+        YangNode child = logical.getChild();
+        while (child != null) {
+            lastChild = child;
+            child = child.getNextSibling();
+        }
+        addChildToLogicalNode(logical, augment, lastChild);
+        addLeafAndLeafList(logical, augment);
+        return true;
+    }
+
+    /**
+     * Adds the child and its sibling in duplicate augment to the logical
+     * augment node.
+     *
+     * @param logical   logical augment node
+     * @param augment   YANG augment
+     * @param lastChild logical node's last node
+     */
+    private static void addChildToLogicalNode(YangAugment logical,
+                                              YangAugment augment,
+                                              YangNode lastChild) {
+        YangNode augChild = augment.getChild();
+        while (augChild != null) {
+            if (lastChild == logical) {
+                augChild.setParent(lastChild);
+                try {
+                    lastChild.addChild(augChild);
+                } catch (DataModelException e) {
+                    throw new ParserException(
+                            constructExtendedListenerErrorMessage(
+                                    UNHANDLED_PARSED_DATA, AUGMENT_DATA,
+                                    augment.getName(), ENTRY, e.getMessage()));
+                }
+            } else {
+                augChild.setParent(lastChild.getParent());
+                augChild.setPreviousSibling(lastChild);
+                lastChild.setNextSibling(augChild);
+            }
+            lastChild = augChild;
+            augChild = augChild.getNextSibling();
+        }
+    }
+
+    /**
+     * Adds leaf and leaf-list from the YANG augment to the logical augment
+     * node.
+     *
+     * @param logical logical YANG augment
+     * @param augment duplicate YANG augment
+     */
+    private static void addLeafAndLeafList(YangAugment logical,
+                                           YangAugment augment) {
+
+        List<YangLeaf> logLeaves = logical.getListOfLeaf();
+        List<YangLeafList> logLl = logical.getListOfLeafList();
+        List<YangLeaf> augLeaves = augment.getListOfLeaf();
+        List<YangLeafList> augLl = augment.getListOfLeafList();
+
+        if (augLeaves != null && !augLeaves.isEmpty()) {
+            for (YangLeaf leaf : augLeaves) {
+                leaf.setContainedIn(logical);
+                if (logLeaves == null) {
+                    logLeaves = new LinkedList<>();
+                }
+                logLeaves.add(leaf);
+            }
+        }
+        if (augLl != null && !augLl.isEmpty()) {
+            for (YangLeafList ll : augLl) {
+                ll.setContainedIn(logical);
+                if (logLl == null) {
+                    logLl = new LinkedList<>();
+                }
+                logLl.add(ll);
+            }
+        }
+    }
+
+    /**
+     * Checks for the augment name name collision. If there are augments with
+     * the same name present, then the new augment will be set with a logical
+     * node.
+     *
+     * @param root augment parent node
+     * @param aug  YANG augment node
+     */
+    public static void checkAugNameCollision(YangNode root, YangAugment aug) {
+        YangNode child = root.getChild();
+        while (child != null) {
+            if (child instanceof YangAugment) {
+                if (child.getName().equals(aug.getName())) {
+                    aug.setLogicalNode((YangAugment) child);
+                }
+            }
+            child = child.getNextSibling();
+        }
+    }
+
+    /**
+     * Removes the duplicate YANG augment from the data tree by detaching it
+     * from its parent and from its sibling.
+     *
+     * @param augment duplicate YANG augment
+     */
+    public static void removeAugment(YangAugment augment) {
+        YangNode root = augment.getParent();
+        YangNode child = root.getChild();
+        YangNode pSib = null;
+        if (child == null) {
+            throw new ParserException("The root node of augment " + augment
+                    .getName() + " must have atleast one child");
+        }
+        while (child != null) {
+            if (child == augment) {
+                if (pSib == null) {
+                    root.setChild(null);
+                } else {
+                    pSib.setNextSibling(null);
+                }
+            }
+            pSib = child;
+            child = child.getNextSibling();
+        }
+        augment = new YangJavaAugmentTranslator();
+        augment = null;
+    }
 }
diff --git a/compiler/plugin/maven/src/test/java/org/onosproject/yang/compiler/plugin/maven/AugmentTranslatorTest.java b/compiler/plugin/maven/src/test/java/org/onosproject/yang/compiler/plugin/maven/AugmentTranslatorTest.java
index 156194e..97e2a86 100644
--- a/compiler/plugin/maven/src/test/java/org/onosproject/yang/compiler/plugin/maven/AugmentTranslatorTest.java
+++ b/compiler/plugin/maven/src/test/java/org/onosproject/yang/compiler/plugin/maven/AugmentTranslatorTest.java
@@ -435,4 +435,67 @@
         YangPluginConfig.compileCode(COMP);
         deleteDirectory(DIR);
     }
+
+    /**
+     * Checks multiple augment handling which has the same name for open
+     * config YANG files.
+     *
+     * @throws IOException            if any error occurs during IO on files
+     * @throws ParserException        if any error occurs during parsing
+     * @throws MojoExecutionException if any mojo operation fails
+     */
+    @Test
+    public void processOpenConfigAugment() throws IOException,
+            ParserException, MojoExecutionException {
+
+        deleteDirectory(DIR);
+        String dir = "src/test/resources/augwithsamename/oc/";
+
+        Set<Path> paths = new HashSet<>();
+        for (String file : getYangFiles(dir)) {
+            paths.add(Paths.get(file));
+        }
+
+        utilManager.createYangFileInfoSet(paths);
+        utilManager.parseYangFileInfoSet();
+        utilManager.createYangNodeSet();
+        utilManager.resolveDependenciesUsingLinker();
+
+        YangPluginConfig yangPluginConfig = new YangPluginConfig();
+        yangPluginConfig.setCodeGenDir(DIR);
+        utilManager.translateToJava(yangPluginConfig);
+        YangPluginConfig.compileCode(COMP);
+        deleteDirectory(DIR);
+    }
+
+    /**
+     * Checks multiple augment handling which has the same name.
+     *
+     * @throws IOException            if any error occurs during IO on files
+     * @throws ParserException        if any error occurs during parsing
+     * @throws MojoExecutionException if any mojo operation fail
+     */
+    @Test
+    public void processSameAugName() throws IOException,
+            ParserException, MojoExecutionException {
+
+        deleteDirectory(DIR);
+        String dir = "src/test/resources/augwithsamename/multiaugwithsamename/";
+
+        Set<Path> paths = new HashSet<>();
+        for (String file : getYangFiles(dir)) {
+            paths.add(Paths.get(file));
+        }
+
+        utilManager.createYangFileInfoSet(paths);
+        utilManager.parseYangFileInfoSet();
+        utilManager.createYangNodeSet();
+        utilManager.resolveDependenciesUsingLinker();
+
+        YangPluginConfig yangPluginConfig = new YangPluginConfig();
+        yangPluginConfig.setCodeGenDir(DIR);
+        utilManager.translateToJava(yangPluginConfig);
+        YangPluginConfig.compileCode(COMP);
+        deleteDirectory(DIR);
+    }
 }
diff --git a/compiler/plugin/maven/src/test/resources/augwithsamename/multiaugwithsamename/module1.yang b/compiler/plugin/maven/src/test/resources/augwithsamename/multiaugwithsamename/module1.yang
new file mode 100644
index 0000000..fedf735
--- /dev/null
+++ b/compiler/plugin/maven/src/test/resources/augwithsamename/multiaugwithsamename/module1.yang
@@ -0,0 +1,14 @@
+module module1 {
+
+    namespace "urn:ietf:params:xml:ns:aug:module:1";
+
+    prefix mod-a;
+
+    container cont{
+        container val {
+            leaf-list create {
+                type string;
+            }
+        }
+    }
+}
diff --git a/compiler/plugin/maven/src/test/resources/augwithsamename/multiaugwithsamename/module2.yang b/compiler/plugin/maven/src/test/resources/augwithsamename/multiaugwithsamename/module2.yang
new file mode 100644
index 0000000..7ec09aa
--- /dev/null
+++ b/compiler/plugin/maven/src/test/resources/augwithsamename/multiaugwithsamename/module2.yang
@@ -0,0 +1,39 @@
+module module2 {
+
+    namespace "urn:ietf:params:xml:ns:aug:module:2";
+
+    prefix mod-b;
+
+    import module1 {
+        prefix mod-a;
+    }
+
+    augment "/mod-a:cont/mod-a:val" {
+        leaf arg {
+            type string;
+        }
+    }
+
+    augment "/mod-a:cont/mod-a:val" {
+        leaf-list arg-lis {
+            type string;
+        }
+    }
+
+    augment "/mod-a:cont/mod-a:val" {
+        container cont {
+            leaf ll {
+                type binary;
+            }
+        }
+    }
+
+    augment "/mod-a:cont/mod-a:val" {
+        list contlist {
+            key true;
+            leaf true {
+                type boolean;
+            }
+        }
+    }
+}
diff --git a/compiler/plugin/maven/src/test/resources/augwithsamename/oc/openconfig-if-ip.yang b/compiler/plugin/maven/src/test/resources/augwithsamename/oc/openconfig-if-ip.yang
new file mode 100644
index 0000000..1c7a8ce
--- /dev/null
+++ b/compiler/plugin/maven/src/test/resources/augwithsamename/oc/openconfig-if-ip.yang
@@ -0,0 +1,69 @@
+module openconfig-if-ip {
+
+  yang-version "1";
+
+  namespace "http://openconfig.net/yang/interfaces/ip";
+
+  prefix "oc-ip";
+
+  import openconfig-interfaces {
+   prefix oc-if;
+  }
+
+  grouping ip-vrrp-top {
+    container vrrp {
+      list vrrp-group {
+        key "virtual-router-id";
+        leaf virtual-router-id {
+          type string;
+        }
+      }
+    }
+  }
+
+  grouping ipv4-top {
+    container ipv4 {
+      container addresses {
+        list address {
+          key "ip";
+          leaf ip {
+            type string;
+          }
+        }
+      }
+    }
+  }
+
+  grouping ipv6-top {
+    container ipv6 {
+      container addresses {
+        list address {
+          key "ip";
+          leaf ip {
+            type string;
+          }
+        }
+      }
+    }
+  }
+
+  augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" +
+    "oc-if:subinterface" {
+    uses ipv4-top;
+  }
+
+  augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" +
+    "oc-if:subinterface" {
+    uses ipv6-top;
+  }
+
+  augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" +
+    "oc-if:subinterface/oc-ip:ipv4/oc-ip:addresses/oc-ip:address" {
+    uses ip-vrrp-top;
+  }
+
+  augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" +
+    "oc-if:subinterface/oc-ip:ipv6/oc-ip:addresses/oc-ip:address" {
+    uses ip-vrrp-top;
+  }
+}
diff --git a/compiler/plugin/maven/src/test/resources/augwithsamename/oc/openconfig-interfaces.yang b/compiler/plugin/maven/src/test/resources/augwithsamename/oc/openconfig-interfaces.yang
new file mode 100644
index 0000000..2804dc6
--- /dev/null
+++ b/compiler/plugin/maven/src/test/resources/augwithsamename/oc/openconfig-interfaces.yang
@@ -0,0 +1,42 @@
+module openconfig-interfaces {
+
+  yang-version "1";
+
+  namespace "http://openconfig.net/yang/interfaces";
+
+  prefix "oc-if";
+
+  contact
+    "OpenConfig working group
+    netopenconfig@googlegroups.com";
+
+  grouping subinterfaces-top {
+    container subinterfaces {
+      list subinterface {
+        key "index";
+        leaf index {
+          type int8;
+        }
+      }
+    }
+  }
+
+  grouping interfaces-top {
+    description
+      "Top-level grouping for interface configuration and
+      operational state data";
+
+    container interfaces {
+      list interface {
+        key "name";
+
+        leaf name {
+          type string;
+        }
+        uses subinterfaces-top;
+      }
+    }
+  }
+
+  uses interfaces-top;
+}