YANG augment listener

Change-Id: I11ece665a7627d784f82247d5a33e3453632d0f9
diff --git a/utils/yangutils/src/main/java/org/onosproject/yangutils/datamodel/YangAugment.java b/utils/yangutils/src/main/java/org/onosproject/yangutils/datamodel/YangAugment.java
index b2bd1ef..efe2561 100644
--- a/utils/yangutils/src/main/java/org/onosproject/yangutils/datamodel/YangAugment.java
+++ b/utils/yangutils/src/main/java/org/onosproject/yangutils/datamodel/YangAugment.java
@@ -22,6 +22,8 @@
 import org.onosproject.yangutils.parser.Parsable;
 import org.onosproject.yangutils.utils.YangConstructType;
 
+import static org.onosproject.yangutils.datamodel.utils.DataModelUtils.detectCollidingChildUtil;
+
 /*-
  * Reference RFC 6020.
  *
@@ -77,12 +79,12 @@
  * Data model node to maintain information defined in YANG augment.
  */
 public class YangAugment extends YangNode
-        implements YangLeavesHolder, YangCommonInfo, Parsable {
+        implements YangLeavesHolder, YangCommonInfo, Parsable, CollisionDetector {
 
     /**
      * Augment target node.
      */
-    private String targetNode;
+    private String name;
 
     /**
      * Description of augment.
@@ -100,6 +102,11 @@
     private List<YangLeafList> listOfLeafList;
 
     /**
+     * List of node identifiers.
+     */
+    private List<YangNodeIdentifier> targetNode;
+
+    /**
      * Reference of the YANG augment.
      */
     private String reference;
@@ -121,17 +128,17 @@
      *
      * @return the augmented node
      */
-    public String getTargetNode() {
+    public List<YangNodeIdentifier> getTargetNode() {
         return targetNode;
     }
 
     /**
      * Set the augmented node.
      *
-     * @param targetNode the augmented node
+     * @param nodeIdentifiers the augmented node
      */
-    public void setTargetNode(String targetNode) {
-        this.targetNode = targetNode;
+    public void setTargetNode(List<YangNodeIdentifier> nodeIdentifiers) {
+        this.targetNode = nodeIdentifiers;
     }
 
     /**
@@ -154,6 +161,20 @@
         this.description = description;
     }
 
+    @Override
+    public void detectCollidingChild(String identifierName, YangConstructType dataType) throws DataModelException {
+        // Detect colliding child.
+        detectCollidingChildUtil(identifierName, dataType, this);
+    }
+
+    @Override
+    public void detectSelfCollision(String identifierName, YangConstructType dataType) throws DataModelException {
+        if (this.getName().equals(identifierName)) {
+            throw new DataModelException("YANG file error: Duplicate input identifier detected, same as input \""
+                    + this.getName() + "\"");
+        }
+    }
+
     /**
      * Get the list of leaves.
      *
@@ -297,7 +318,7 @@
      */
     @Override
     public String getName() {
-        return targetNode;
+        return name;
     }
 
     /**
@@ -307,7 +328,7 @@
      */
     @Override
     public void setName(String name) {
-        targetNode = name;
+        this.name = name;
 
     }
 
diff --git a/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/TreeWalkListener.java b/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/TreeWalkListener.java
index 029a4d7..473ee0e 100644
--- a/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/TreeWalkListener.java
+++ b/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/TreeWalkListener.java
@@ -25,6 +25,7 @@
 import org.onosproject.yangutils.parser.Parsable;
 import org.onosproject.yangutils.parser.antlrgencode.GeneratedYangListener;
 import org.onosproject.yangutils.parser.antlrgencode.GeneratedYangParser;
+import org.onosproject.yangutils.parser.impl.listeners.AugmentListener;
 import org.onosproject.yangutils.parser.impl.listeners.BaseFileListener;
 import org.onosproject.yangutils.parser.impl.listeners.BelongsToListener;
 import org.onosproject.yangutils.parser.impl.listeners.BitListener;
@@ -1041,12 +1042,12 @@
 
     @Override
     public void enterAugmentStatement(GeneratedYangParser.AugmentStatementContext ctx) {
-        // TODO: implement the method.
+        AugmentListener.processAugmentEntry(this, ctx);
     }
 
     @Override
     public void exitAugmentStatement(GeneratedYangParser.AugmentStatementContext ctx) {
-        // TODO: implement the method.
+        AugmentListener.processAugmentExit(this, ctx);
     }
 
     @Override
diff --git a/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/listeners/AugmentListener.java b/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/listeners/AugmentListener.java
new file mode 100644
index 0000000..d332ab9
--- /dev/null
+++ b/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/listeners/AugmentListener.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yangutils.parser.impl.listeners;
+
+import java.util.List;
+import org.onosproject.yangutils.datamodel.YangAugment;
+import org.onosproject.yangutils.datamodel.YangNode;
+import org.onosproject.yangutils.datamodel.YangModule;
+import org.onosproject.yangutils.datamodel.YangSubModule;
+import org.onosproject.yangutils.datamodel.YangNodeIdentifier;
+import org.onosproject.yangutils.datamodel.exceptions.DataModelException;
+import org.onosproject.yangutils.parser.Parsable;
+import org.onosproject.yangutils.parser.antlrgencode.GeneratedYangParser;
+import org.onosproject.yangutils.parser.exceptions.ParserException;
+import org.onosproject.yangutils.parser.impl.TreeWalkListener;
+
+import static org.onosproject.yangutils.datamodel.utils.GeneratedLanguage.JAVA_GENERATION;
+import static org.onosproject.yangutils.datamodel.utils.YangDataModelFactory.getYangAugmentNode;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerCollisionDetector.detectCollidingChildUtil;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerErrorLocation.ENTRY;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerErrorLocation.EXIT;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerErrorMessageConstruction.constructExtendedListenerErrorMessage;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerErrorMessageConstruction.constructListenerErrorMessage;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerErrorType.MISSING_CURRENT_HOLDER;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerErrorType.MISSING_HOLDER;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerErrorType.UNHANDLED_PARSED_DATA;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerErrorType.INVALID_HOLDER;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerValidation.checkStackIsNotEmpty;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerValidation.validateCardinalityMaxOne;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerValidation.validateMutuallyExclusiveChilds;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerUtil.getValidAbsoluteSchemaNodeId;
+import static org.onosproject.yangutils.utils.YangConstructType.AUGMENT_DATA;
+import static org.onosproject.yangutils.utils.YangConstructType.DATA_DEF_DATA;
+import static org.onosproject.yangutils.utils.YangConstructType.STATUS_DATA;
+import static org.onosproject.yangutils.utils.YangConstructType.REFERENCE_DATA;
+import static org.onosproject.yangutils.utils.YangConstructType.DESCRIPTION_DATA;
+import static org.onosproject.yangutils.utils.YangConstructType.WHEN_DATA;
+import static org.onosproject.yangutils.utils.YangConstructType.CASE_DATA;
+
+/*
+ * Reference: RFC6020 and YANG ANTLR Grammar
+ *
+ * ABNF grammar as per RFC6020
+ *  augment-stmt        = augment-keyword sep augment-arg-str optsep
+ *                        "{" stmtsep
+ *                            ;; these stmts can appear in any order
+ *                            [when-stmt stmtsep]
+ *                            *(if-feature-stmt stmtsep)
+ *                            [status-stmt stmtsep]
+ *                            [description-stmt stmtsep]
+ *                            [reference-stmt stmtsep]
+ *                            1*((data-def-stmt stmtsep) /
+ *                               (case-stmt stmtsep))
+ *                         "}"
+ *
+ * ANTLR grammar rule
+ * augmentStatement : AUGMENT_KEYWORD augment LEFT_CURLY_BRACE (whenStatement | ifFeatureStatement | statusStatement
+ *      | descriptionStatement | referenceStatement | dataDefStatement  | caseStatement)* RIGHT_CURLY_BRACE;
+ */
+
+/**
+ * Implements listener based call back function corresponding to the "augment"
+ * rule defined in ANTLR grammar file for corresponding ABNF rule in RFC 6020.
+ */
+public final class AugmentListener {
+
+    /**
+     * Creates a new augment listener.
+     */
+    private AugmentListener() {
+    }
+
+    /**
+     * It is called when parser receives an input matching the grammar rule
+     * (augment), performs validation and updates the data model tree.
+     *
+     * @param listener listener's object
+     * @param ctx context object of the grammar rule
+     */
+    public static void processAugmentEntry(TreeWalkListener listener,
+                                       GeneratedYangParser.AugmentStatementContext ctx) {
+
+        // Check for stack to be non empty.
+        checkStackIsNotEmpty(listener, MISSING_HOLDER, AUGMENT_DATA, ctx.augment().getText(), ENTRY);
+
+        // Validate augment argument string
+        List<YangNodeIdentifier> targetNodes = getValidAbsoluteSchemaNodeId(ctx.augment().getText(),
+                AUGMENT_DATA, ctx);
+
+        // Validate sub statement cardinality.
+        validateSubStatementsCardinality(ctx);
+
+        // Check for identifier collision
+        int line = ctx.getStart().getLine();
+        int charPositionInLine = ctx.getStart().getCharPositionInLine();
+        detectCollidingChildUtil(listener, line, charPositionInLine, "", AUGMENT_DATA);
+
+        Parsable curData = listener.getParsedDataStack().peek();
+        if (curData instanceof YangModule || curData instanceof YangSubModule) {
+
+            YangNode curNode = (YangNode) curData;
+            YangAugment yangAugment = getYangAugmentNode(JAVA_GENERATION);
+            yangAugment.setTargetNode(targetNodes);
+            try {
+                curNode.addChild(yangAugment);
+            } catch (DataModelException e) {
+                throw new ParserException(constructExtendedListenerErrorMessage(UNHANDLED_PARSED_DATA,
+                        AUGMENT_DATA, ctx.augment().getText(), ENTRY, e.getMessage()));
+            }
+            listener.getParsedDataStack().push(yangAugment);
+        } else {
+            throw new ParserException(constructListenerErrorMessage(INVALID_HOLDER, AUGMENT_DATA,
+                    ctx.augment().getText(), ENTRY));
+        }
+
+    }
+
+    /**
+     * It is called when parser exits from grammar rule (augment), it perform
+     * validations and updates the data model tree.
+     *
+     * @param listener listener's object
+     * @param ctx context object of the grammar rule
+     */
+    public static void processAugmentExit(TreeWalkListener listener,
+                                      GeneratedYangParser.AugmentStatementContext ctx) {
+
+        //Check for stack to be non empty.
+        checkStackIsNotEmpty(listener, MISSING_HOLDER, AUGMENT_DATA, ctx.augment().getText(), EXIT);
+
+        if (!(listener.getParsedDataStack().peek() instanceof YangAugment)) {
+            throw new ParserException(constructListenerErrorMessage(MISSING_CURRENT_HOLDER, AUGMENT_DATA,
+                    ctx.augment().getText(), EXIT));
+        }
+        listener.getParsedDataStack().pop();
+    }
+
+    /**
+     * Validates the cardinality of augment sub-statements as per grammar.
+     *
+     * @param ctx context object of the grammar rule
+     */
+    private static void validateSubStatementsCardinality(GeneratedYangParser.AugmentStatementContext ctx) {
+
+        validateCardinalityMaxOne(ctx.statusStatement(), STATUS_DATA, AUGMENT_DATA, ctx.augment().getText());
+        validateCardinalityMaxOne(ctx.descriptionStatement(), DESCRIPTION_DATA, AUGMENT_DATA, ctx.augment().getText());
+        validateCardinalityMaxOne(ctx.referenceStatement(), REFERENCE_DATA, AUGMENT_DATA, ctx.augment().getText());
+        validateCardinalityMaxOne(ctx.whenStatement(), WHEN_DATA, AUGMENT_DATA, ctx.augment().getText());
+        validateMutuallyExclusiveChilds(ctx.dataDefStatement(), DATA_DEF_DATA, ctx.caseStatement(),
+                                        CASE_DATA, AUGMENT_DATA, ctx.augment().getText());
+    }
+}
diff --git a/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/parserutils/ListenerUtil.java b/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/parserutils/ListenerUtil.java
index 8c3f36f..7b297f2 100644
--- a/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/parserutils/ListenerUtil.java
+++ b/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/parserutils/ListenerUtil.java
@@ -16,13 +16,14 @@
 
 package org.onosproject.yangutils.parser.impl.parserutils;
 
-import org.antlr.v4.runtime.ParserRuleContext;
-
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.regex.Pattern;
 
+import org.antlr.v4.runtime.ParserRuleContext;
 import org.onosproject.yangutils.datamodel.YangNodeIdentifier;
 import org.onosproject.yangutils.parser.antlrgencode.GeneratedYangParser;
 import org.onosproject.yangutils.utils.YangConstructType;
@@ -45,6 +46,7 @@
     private static final String SLASH = "/";
     private static final String SPACE = " ";
     private static final String COLON = ":";
+    private static final String CARET = "^";
 
     /**
      * Creates a new listener util.
@@ -237,4 +239,36 @@
             throw parserException;
         }
     }
+
+    /**
+     * Checks and return valid absolute schema node id.
+     *
+     * @param argumentString string from yang file
+     * @param yangConstructType yang construct for creating error message
+     * @param ctx yang construct's context to get the line number and character position
+     * @return target nodes list of absolute schema node id
+     */
+    public static List<YangNodeIdentifier> getValidAbsoluteSchemaNodeId(String argumentString,
+            YangConstructType yangConstructType, ParserRuleContext ctx) {
+
+        List<YangNodeIdentifier> targetNodes = new LinkedList<>();
+        YangNodeIdentifier yangNodeIdentifier;
+        String tmpSchemaNodeId = removeQuotesAndHandleConcat(argumentString);
+
+        // absolute-schema-nodeid = 1*("/" node-identifier)
+        if (!tmpSchemaNodeId.startsWith(SLASH)) {
+            ParserException parserException = new ParserException("YANG file error : " +
+                    YangConstructType.getYangConstructType(yangConstructType) + " name " + argumentString +
+                    "is not valid");
+            parserException.setLine(ctx.getStart().getLine());
+            parserException.setCharPosition(ctx.getStart().getCharPositionInLine());
+            throw parserException;
+        }
+        String[] tmpData = tmpSchemaNodeId.replaceFirst(CARET + SLASH, EMPTY_STRING).split(SLASH);
+        for (String nodeIdentifiers : tmpData) {
+            yangNodeIdentifier = getValidNodeIdentifier(nodeIdentifiers, yangConstructType, ctx);
+            targetNodes.add(yangNodeIdentifier);
+        }
+        return targetNodes;
+    }
 }
\ No newline at end of file
diff --git a/utils/yangutils/src/main/resources/GeneratedYang.g4 b/utils/yangutils/src/main/resources/GeneratedYang.g4
index 3fc0f9a..a0e4468 100644
--- a/utils/yangutils/src/main/resources/GeneratedYang.g4
+++ b/utils/yangutils/src/main/resources/GeneratedYang.g4
@@ -1055,7 +1055,7 @@
      *                         "}"
      * TODO : 0..1 occurance to be checked in listener
      */
-    augmentStatement : AUGMENT_KEYWORD string LEFT_CURLY_BRACE (whenStatement | ifFeatureStatement | statusStatement
+    augmentStatement : AUGMENT_KEYWORD augment LEFT_CURLY_BRACE (whenStatement | ifFeatureStatement | statusStatement
                    | descriptionStatement | referenceStatement | dataDefStatement  | caseStatement)* RIGHT_CURLY_BRACE;
 
     /**
diff --git a/utils/yangutils/src/test/java/org/onosproject/yangutils/parser/impl/listeners/AugmentListenerTest.java b/utils/yangutils/src/test/java/org/onosproject/yangutils/parser/impl/listeners/AugmentListenerTest.java
new file mode 100644
index 0000000..2173183
--- /dev/null
+++ b/utils/yangutils/src/test/java/org/onosproject/yangutils/parser/impl/listeners/AugmentListenerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yangutils.parser.impl.listeners;
+
+import java.io.IOException;
+import java.util.ListIterator;
+
+import org.junit.Test;
+import org.onosproject.yangutils.datamodel.YangNode;
+import org.onosproject.yangutils.datamodel.YangModule;
+import org.onosproject.yangutils.datamodel.YangAugment;
+import org.onosproject.yangutils.datamodel.YangLeaf;
+import org.onosproject.yangutils.datamodel.YangNodeIdentifier;
+import org.onosproject.yangutils.datamodel.YangDataTypes;
+import org.onosproject.yangutils.datamodel.YangNodeType;
+import org.onosproject.yangutils.parser.exceptions.ParserException;
+import org.onosproject.yangutils.parser.impl.YangUtilsParserManager;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test cases for testing augment listener functionality.
+ */
+public class AugmentListenerTest {
+
+    private final YangUtilsParserManager manager = new YangUtilsParserManager();
+
+    /**
+     * Checks valid augment statement.
+     */
+    @Test
+    public void processValidAugmentStatement() throws IOException, ParserException {
+
+        YangNode node = manager.getDataModel("src/test/resources/ValidAugmentStatement.yang");
+
+        assertThat((node instanceof YangModule), is(true));
+        assertThat(node.getNodeType(), is(YangNodeType.MODULE_NODE));
+        YangModule yangNode = (YangModule) node;
+        assertThat(yangNode.getName(), is("Test"));
+
+        YangAugment yangAugment = (YangAugment) yangNode.getChild();
+        if (yangAugment.getTargetNode().isEmpty()) {
+            System.out.println("list is empty");
+        }
+        ListIterator<YangNodeIdentifier> nodeIdentifierIterator = yangAugment.getTargetNode().listIterator();
+        YangNodeIdentifier yangNodeIdentifier = nodeIdentifierIterator.next();
+        assertThat(yangNodeIdentifier.getPrefix(), is("if"));
+        assertThat(yangNodeIdentifier.getName(), is("interfaces"));
+
+        ListIterator<YangLeaf> leafIterator = yangAugment.getListOfLeaf().listIterator();
+        YangLeaf leafInfo = leafIterator.next();
+
+        assertThat(leafInfo.getLeafName(), is("ds0ChannelNumber"));
+        assertThat(leafInfo.getDataType().getDataTypeName(), is("ChannelNumber"));
+        assertThat(leafInfo.getDataType().getDataType(), is(YangDataTypes.DERIVED));
+    }
+}
diff --git a/utils/yangutils/src/test/resources/ValidAugmentStatement.yang b/utils/yangutils/src/test/resources/ValidAugmentStatement.yang
new file mode 100644
index 0000000..59ebe67
--- /dev/null
+++ b/utils/yangutils/src/test/resources/ValidAugmentStatement.yang
@@ -0,0 +1,15 @@
+module Test {
+    yang-version 1;
+    namespace http://example.com/schema/ds0;
+    prefix On;
+
+    import interface-module {
+        prefix "if";
+    }
+    augment "/if:interfaces/if:ifEntry" {
+        when "if:ifType='ds0'";
+        leaf ds0ChannelNumber {
+            type P:ChannelNumber;
+        }
+    }
+}