YANG range restriction listener

Change-Id: I51af1d5d85068bb35c88ed1f86778b6ffc407036
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 75f5396..e46bfc7 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
@@ -58,6 +58,7 @@
 import org.onosproject.yangutils.parser.impl.listeners.PositionListener;
 import org.onosproject.yangutils.parser.impl.listeners.PrefixListener;
 import org.onosproject.yangutils.parser.impl.listeners.PresenceListener;
+import org.onosproject.yangutils.parser.impl.listeners.RangeRestrictionListener;
 import org.onosproject.yangutils.parser.impl.listeners.ReferenceListener;
 import org.onosproject.yangutils.parser.impl.listeners.RevisionDateListener;
 import org.onosproject.yangutils.parser.impl.listeners.RevisionListener;
@@ -555,7 +556,7 @@
 
     @Override
     public void enterRangeStatement(GeneratedYangParser.RangeStatementContext ctx) {
-        // TODO: implement the method.
+        RangeRestrictionListener.processRangeRestrictionEntry(this, ctx);
     }
 
     @Override
diff --git a/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/listeners/RangeRestrictionListener.java b/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/listeners/RangeRestrictionListener.java
new file mode 100644
index 0000000..5c6edce
--- /dev/null
+++ b/utils/yangutils/src/main/java/org/onosproject/yangutils/parser/impl/listeners/RangeRestrictionListener.java
@@ -0,0 +1,168 @@
+/*
+ * 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.math.BigInteger;
+import java.util.regex.Pattern;
+
+import org.onosproject.yangutils.datamodel.YangType;
+import org.onosproject.yangutils.datamodel.YangRangeRestriction;
+import org.onosproject.yangutils.datamodel.YangRangeInterval;
+import org.onosproject.yangutils.datamodel.YangDataTypes;
+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.utils.YangConstructType.RANGE_DATA;
+import static org.onosproject.yangutils.utils.YangConstructType.TYPE_DATA;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerErrorLocation.ENTRY;
+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.INVALID_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.ListenerValidation.checkStackIsNotEmpty;
+import static org.onosproject.yangutils.parser.impl.parserutils.ListenerUtil.removeQuotesAndHandleConcat;
+
+/*
+ * Reference: RFC6020 and YANG ANTLR Grammar
+ *
+ * ABNF grammar as per RFC6020
+ *  range-stmt          = range-keyword sep range-arg-str optsep
+ *                        (";" /
+ *                         "{" stmtsep
+ *                             ;; these stmts can appear in any order
+ *                             [error-message-stmt stmtsep]
+ *                             [error-app-tag-stmt stmtsep]
+ *                             [description-stmt stmtsep]
+ *                             [reference-stmt stmtsep]
+ *                          "}")
+ *
+ * ANTLR grammar rule
+ *  rangeStatement : RANGE_KEYWORD range (STMTEND | LEFT_CURLY_BRACE commonStatements RIGHT_CURLY_BRACE);
+ */
+
+/**
+ * Implements listener based call back function corresponding to the "range"
+ * rule defined in ANTLR grammar file for corresponding ABNF rule in RFC 6020.
+ */
+public final class RangeRestrictionListener {
+
+    private static final String PIPE = "|";
+    private static final String RANGE_INTERVAL = "..";
+
+    /**
+     * Creates a new range restriction listener.
+     */
+    private RangeRestrictionListener() {
+    }
+
+    /**
+     * It is called when parser receives an input matching the grammar
+     * rule (range), performs validation and updates the data model
+     * tree.
+     *
+     * @param listener listener's object.
+     * @param ctx context object of the grammar rule.
+     */
+    public static void processRangeRestrictionEntry(TreeWalkListener listener,
+                                                      GeneratedYangParser.RangeStatementContext ctx) {
+
+        // Check for stack to be non empty.
+        checkStackIsNotEmpty(listener, MISSING_HOLDER, RANGE_DATA, ctx.range().getText(), ENTRY);
+
+        Parsable tmpData = listener.getParsedDataStack().peek();
+        if (tmpData.getYangConstructType() == TYPE_DATA) {
+            YangType type = (YangType) tmpData;
+            setRangeRestriction(type, ctx);
+        } else {
+            throw new ParserException(constructListenerErrorMessage(INVALID_HOLDER, RANGE_DATA,
+                    ctx.range().getText(), ENTRY));
+        }
+    }
+
+    /**
+     * Sets the range restriction to type.
+     *
+     * @param type YANG type for which range restriction to be added.
+     * @param ctx context object of the grammar rule.
+     */
+    private static void setRangeRestriction(YangType type,
+                                              GeneratedYangParser.RangeStatementContext ctx) {
+        YangRangeRestriction<?> rangeRestriction = null;
+        YangRangeInterval rangeInterval;
+
+        String rangeArgument = removeQuotesAndHandleConcat(ctx.range().getText());
+        String[] rangeArguments = rangeArgument.trim().split(Pattern.quote(PIPE));
+
+        for (String rangePart : rangeArguments) {
+            String[] rangeBoundary = rangePart.trim().split(Pattern.quote(RANGE_INTERVAL));
+
+            if (rangeBoundary.length == 1) {
+                rangeBoundary[1] = rangeBoundary[0];
+            }
+
+            if (type.getDataType() == YangDataTypes.INT8) {
+                rangeRestriction = new YangRangeRestriction<Byte>();
+                rangeInterval = new YangRangeInterval<Byte>();
+                rangeInterval.setStartValue(Byte.parseByte(rangeBoundary[0]));
+                rangeInterval.setEndValue(Byte.parseByte(rangeBoundary[1]));
+            } else if ((type.getDataType() == YangDataTypes.INT16)
+                    || (type.getDataType() == YangDataTypes.UINT8)) {
+                rangeRestriction = new YangRangeRestriction<Short>();
+                rangeInterval = new YangRangeInterval<Short>();
+                rangeInterval.setStartValue(Short.parseShort(rangeBoundary[0]));
+                rangeInterval.setEndValue(Short.parseShort(rangeBoundary[1]));
+            } else if ((type.getDataType() == YangDataTypes.INT32)
+                    || (type.getDataType() == YangDataTypes.UINT16)) {
+                rangeRestriction = new YangRangeRestriction<Integer>();
+                rangeInterval = new YangRangeInterval<Integer>();
+                rangeInterval.setStartValue(Integer.parseInt(rangeBoundary[0]));
+                rangeInterval.setEndValue(Integer.parseInt(rangeBoundary[1]));
+            } else if ((type.getDataType() == YangDataTypes.INT64)
+                    || (type.getDataType() == YangDataTypes.UINT32)) {
+                rangeRestriction = new YangRangeRestriction<Long>();
+                rangeInterval = new YangRangeInterval<Long>();
+                rangeInterval.setStartValue(Long.parseLong(rangeBoundary[0]));
+                rangeInterval.setEndValue(Long.parseLong(rangeBoundary[1]));
+            } else if (type.getDataType() == YangDataTypes.UINT64) {
+                rangeRestriction = new YangRangeRestriction<BigInteger>();
+                rangeInterval = new YangRangeInterval<BigInteger>();
+                rangeInterval.setStartValue(new BigInteger(rangeBoundary[0]));
+                rangeInterval.setEndValue(new BigInteger(rangeBoundary[0]));
+            } else {
+                //TODO: support derived for base built in type of string
+                throw new ParserException(constructListenerErrorMessage(INVALID_HOLDER, RANGE_DATA,
+                        rangeArgument, ENTRY));
+            }
+
+            try {
+                rangeRestriction.addRangeRestrictionInterval(rangeInterval);
+            } catch (DataModelException e) {
+                throw new ParserException(constructExtendedListenerErrorMessage(UNHANDLED_PARSED_DATA, RANGE_DATA,
+                        rangeArgument, ENTRY, e.getMessage()));
+            }
+        }
+
+        if (rangeRestriction != null) {
+            type.setDataTypeExtendedInfo(rangeRestriction);
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/utils/yangutils/src/main/java/org/onosproject/yangutils/utils/YangConstructType.java b/utils/yangutils/src/main/java/org/onosproject/yangutils/utils/YangConstructType.java
index 5281c537..f7e091e 100644
--- a/utils/yangutils/src/main/java/org/onosproject/yangutils/utils/YangConstructType.java
+++ b/utils/yangutils/src/main/java/org/onosproject/yangutils/utils/YangConstructType.java
@@ -272,7 +272,12 @@
     /**
      * Identifies the derived data type.
      */
-    DERIVED;
+    DERIVED,
+
+    /**
+     * Identifies the YANG range element parsed data.
+     */
+    RANGE_DATA;
 
     /**
      * Returns the YANG construct keyword corresponding to enum values.
@@ -385,6 +390,8 @@
                 return "notification";
             case UNION_DATA:
                 return "union";
+            case RANGE_DATA:
+                return "range";
             default:
                 return "yang";
         }
diff --git a/utils/yangutils/src/test/java/org/onosproject/yangutils/parser/impl/listeners/RangeRestrictionListenerTest.java b/utils/yangutils/src/test/java/org/onosproject/yangutils/parser/impl/listeners/RangeRestrictionListenerTest.java
new file mode 100644
index 0000000..fea06d5
--- /dev/null
+++ b/utils/yangutils/src/test/java/org/onosproject/yangutils/parser/impl/listeners/RangeRestrictionListenerTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.YangLeaf;
+import org.onosproject.yangutils.datamodel.YangLeafList;
+import org.onosproject.yangutils.datamodel.YangRangeRestriction;
+import org.onosproject.yangutils.datamodel.YangRangeInterval;
+import org.onosproject.yangutils.datamodel.YangModule;
+import org.onosproject.yangutils.datamodel.YangNodeType;
+import org.onosproject.yangutils.datamodel.YangDataTypes;
+import org.onosproject.yangutils.parser.exceptions.ParserException;
+import org.onosproject.yangutils.parser.impl.YangUtilsParserManager;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+/**
+ * Test cases for range restriction listener.
+ */
+public class RangeRestrictionListenerTest {
+
+    private final YangUtilsParserManager manager = new YangUtilsParserManager();
+
+    /**
+     * Checks valid range statement as sub-statement of leaf statement.
+     */
+    @Test
+    public void processValidRangeStatement() throws IOException, ParserException {
+
+        YangNode node = manager.getDataModel("src/test/resources/ValidRangeStatement.yang");
+
+        assertThat((node instanceof YangModule), is(true));
+        assertThat(node.getNodeType(), is(YangNodeType.MODULE_NODE));
+        YangModule yangNode = (YangModule) node;
+        assertThat(yangNode.getName(), is("Test"));
+
+        ListIterator<YangLeaf> leafIterator = yangNode.getListOfLeaf().listIterator();
+        YangLeaf leafInfo = leafIterator.next();
+
+        assertThat(leafInfo.getLeafName(), is("invalid-interval"));
+        assertThat(leafInfo.getDataType().getDataTypeName(), is("int32"));
+        assertThat(leafInfo.getDataType().getDataType(), is(YangDataTypes.INT32));
+        YangRangeRestriction rangeRestriction = (YangRangeRestriction) leafInfo
+                .getDataType().getDataTypeExtendedInfo();
+
+        ListIterator<YangRangeInterval> rangeListIterator = rangeRestriction.getAscendingRangeIntervals()
+                                                    .listIterator();
+        YangRangeInterval rangeInterval = rangeListIterator.next();
+        assertThat(rangeInterval.getStartValue(), is(10));
+        assertThat(rangeInterval.getEndValue(), is(20));
+    }
+
+    /**
+     * Checks valid range statement as sub-statement of leaf-list.
+     */
+    @Test
+    public void processRangeStatementInsideLeafList() throws IOException, ParserException {
+
+        YangNode node = manager.getDataModel("src/test/resources/RangeStatementInsideLeafList.yang");
+
+        assertThat((node instanceof YangModule), is(true));
+        assertThat(node.getNodeType(), is(YangNodeType.MODULE_NODE));
+        YangModule yangNode = (YangModule) node;
+        assertThat(yangNode.getName(), is("Test"));
+
+        ListIterator<YangLeafList> leafListIterator = yangNode.getListOfLeafList().listIterator();
+        YangLeafList leafListInfo = leafListIterator.next();
+
+        assertThat(leafListInfo.getLeafName(), is("invalid-interval"));
+        assertThat(leafListInfo.getDataType().getDataTypeName(), is("int32"));
+        assertThat(leafListInfo.getDataType().getDataType(), is(YangDataTypes.INT32));
+        YangRangeRestriction rangeRestriction = (YangRangeRestriction) leafListInfo
+                .getDataType().getDataTypeExtendedInfo();
+
+        ListIterator<YangRangeInterval> rangeListIterator = rangeRestriction.getAscendingRangeIntervals()
+                .listIterator();
+        YangRangeInterval rangeInterval = rangeListIterator.next();
+
+        assertThat(rangeInterval.getStartValue(), is(10));
+        assertThat(rangeInterval.getEndValue(), is(20));
+    }
+}
\ No newline at end of file
diff --git a/utils/yangutils/src/test/resources/RangeStatementInsideLeafList.yang b/utils/yangutils/src/test/resources/RangeStatementInsideLeafList.yang
new file mode 100644
index 0000000..9f5808d
--- /dev/null
+++ b/utils/yangutils/src/test/resources/RangeStatementInsideLeafList.yang
@@ -0,0 +1,10 @@
+module Test {
+    yang-version 1;
+    namespace http://huawei.com;
+    prefix Ant;
+    leaf-list invalid-interval {
+        type int32 {
+            range "1..4 | 10..20";
+         }
+    }
+}
diff --git a/utils/yangutils/src/test/resources/ValidRangeStatement.yang b/utils/yangutils/src/test/resources/ValidRangeStatement.yang
new file mode 100644
index 0000000..4243040
--- /dev/null
+++ b/utils/yangutils/src/test/resources/ValidRangeStatement.yang
@@ -0,0 +1,11 @@
+module Test {
+    yang-version 1;
+    namespace http://huawei.com;
+    prefix Ant;
+    leaf invalid-interval {
+        type int32 {
+            range "1..4 | 10..20";
+         }
+    }
+}
+