Yang validation moved to validator package and validation listener refactored. 49/249/1
authorMaros Marsalek <mmarsale@cisco.com>
Thu, 18 Apr 2013 10:32:54 +0000 (12:32 +0200)
committerMaros Marsalek <mmarsale@cisco.com>
Thu, 25 Apr 2013 10:31:52 +0000 (12:31 +0200)
Added validations for yang statements according to RFC-6020 (section 6 and 7).
Added new tests for statement validation and utility classes created.

This is only initial validation performed on every yang file without any context
from other yang files.

Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
15 files changed:
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/pom.xml
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserImpl.java
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelParserListenerImpl.java
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListener.java [deleted file]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/util/YangValidationException.java [moved from opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangValidationException.java with 78% similarity]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/BasicValidations.java [new file with mode: 0644]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/ValidationUtil.java [new file with mode: 0644]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidationListener.java [new file with mode: 0644]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidator.java [new file with mode: 0644]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_Module.java [deleted file]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_SubModule.java [deleted file]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationListTest.java [new file with mode: 0644]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationModuleTest.java [new file with mode: 0644]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationSubModuleTest.java [new file with mode: 0644]
opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationTest.java [new file with mode: 0644]

index add138e8b98f9658359349b432a25c4433642adc..ce0543dafd7673c3b64de5dd1633096c5d928956 100644 (file)
                        <groupId>org.mockito</groupId>\r
                        <artifactId>mockito-all</artifactId>\r
                        <version>1.8.4</version>\r
                        <groupId>org.mockito</groupId>\r
                        <artifactId>mockito-all</artifactId>\r
                        <version>1.8.4</version>\r
-               </dependency>\r
+               </dependency>
+        <dependency>\r
+          <groupId>com.google.guava</groupId>\r
+          <artifactId>guava</artifactId>\r
+          <version>14.0.1</version>\r
+        </dependency>\r
+\r
        </dependencies>\r
        <build>\r
                <plugins>\r
        </dependencies>\r
        <build>\r
                <plugins>\r
index e2ebea7f883235daa613af19fd1e4f77ba773abc..af972f08a5a687849d0bc2a67f41b312a0063133 100644 (file)
@@ -78,6 +78,7 @@ import org.opendaylight.controller.yang.model.parser.util.YangParseException;
 import org.opendaylight.controller.yang.model.util.ExtendedType;
 import org.opendaylight.controller.yang.model.util.IdentityrefType;
 import org.opendaylight.controller.yang.model.util.UnknownType;
 import org.opendaylight.controller.yang.model.util.ExtendedType;
 import org.opendaylight.controller.yang.model.util.IdentityrefType;
 import org.opendaylight.controller.yang.model.util.UnknownType;
+import org.opendaylight.controller.yang.model.validator.YangModelBasicValidator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -161,19 +162,8 @@ public class YangModelParserImpl implements YangModelParser {
         final List<ParseTree> trees = parseStreams(yangFiles);
         final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
 
         final List<ParseTree> trees = parseStreams(yangFiles);
         final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
 
-        // validation
-        // if validation fails with any file, do not continue and throw
-        // exception
-        for (int i = 0; i < trees.size(); i++) {
-            try {
-                final YangModelValidationListener yangModelParser = new YangModelValidationListener();
-                walker.walk(yangModelParser, trees.get(i));
-            } catch (IllegalStateException e) {
-                // wrap exception to add information about which file failed
-                throw new YangValidationException(
-                        "Yang validation failed for file" + yangFiles[i], e);
-            }
-        }
+        // validate yang
+        new YangModelBasicValidator(walker).validate(trees);
 
         YangModelParserListenerImpl yangModelParser = null;
         for (int i = 0; i < trees.size(); i++) {
 
         YangModelParserListenerImpl yangModelParser = null;
         for (int i = 0; i < trees.size(); i++) {
index 76eafaa26d02deae270b6f46c926b2936849f236..eacc5460b3743c7a4f9c62a9215a2cf7daa9bc3e 100644 (file)
@@ -78,8 +78,8 @@ import org.opendaylight.controller.yang.model.util.YangTypesConverter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-final class YangModelParserListenerImpl extends YangParserBaseListener {
-
+public final class YangModelParserListenerImpl extends YangParserBaseListener {\r
+\r
     private static final Logger logger = LoggerFactory
             .getLogger(YangModelParserListenerImpl.class);
 
     private static final Logger logger = LoggerFactory
             .getLogger(YangModelParserListenerImpl.class);
 
@@ -90,7 +90,7 @@ final class YangModelParserListenerImpl extends YangParserBaseListener {
     private String yangModelPrefix;
     private Date revision = new Date(0L);
 
     private String yangModelPrefix;
     private Date revision = new Date(0L);
 
-    final static DateFormat simpleDateFormat = new SimpleDateFormat(
+    public final static DateFormat simpleDateFormat = new SimpleDateFormat(\r
             "yyyy-MM-dd");
     private final Stack<String> actualPath = new Stack<String>();
 
             "yyyy-MM-dd");
     private final Stack<String> actualPath = new Stack<String>();
 
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListener.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListener.java
deleted file mode 100644 (file)
index 68887a1..0000000
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.yang.model.parser.impl;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-import org.antlr.v4.runtime.tree.ParseTree;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Import_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Include_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_header_stmtsContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtsContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yang_version_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParserBaseListener;
-import org.opendaylight.controller.yang.model.parser.util.YangModelBuilderUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Validation listener that validates yang statements according to RFC-6020.
- * This validator expects only one module or submodule per file.
- */
-
-/*
- * TODO is this assumption(module per file) correct ? if so, should a check be
- * performed ?
- * 
- * TODO break into smaller classes e.g. class for header statements, body
- * statements...
- */
-final class YangModelValidationListener extends YangParserBaseListener {
-
-    private static final Logger logger = LoggerFactory
-            .getLogger(YangModelValidationListener.class);
-
-    private final Set<String> uniquePrefixes;
-    private final Set<String> uniqueImports;
-    private final Set<String> uniqueIncludes;
-
-    public YangModelValidationListener() {
-        super();
-        uniquePrefixes = new HashSet<String>();
-        uniqueImports = new HashSet<String>();
-        uniqueIncludes = new HashSet<String>();
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Identifier contains only permitted characters</li>
-     * <li>One revision statements present</li>
-     * <li>One header statements present</li>
-     * </ol>
-     */
-    @Override
-    public void enterModule_stmt(YangParser.Module_stmtContext ctx) {
-        String moduleName = getName(ctx);
-
-        checkIdentifier(moduleName, "Module");
-
-        checkPresentChildOfType(ctx, Revision_stmtsContext.class,
-                f("Missing revision statements in module:%s", moduleName), true);
-
-        checkPresentChildOfType(ctx, Module_header_stmtsContext.class,
-                f("Missing header statements in module:%s", moduleName), true);
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Identifier contains only permitted characters</li>
-     * <li>One revision statements present</li>
-     * <li>One header statements present</li>
-     * </ol>
-     */
-    @Override
-    public void enterSubmodule_stmt(Submodule_stmtContext ctx) {
-        String submoduleName = getName(ctx);
-
-        checkIdentifier(submoduleName, "Submodule");
-
-        checkPresentChildOfType(
-                ctx,
-                Revision_stmtsContext.class,
-                f("Missing revision statements in submodule:%s", submoduleName),
-                true);
-
-        checkPresentChildOfType(ctx, Submodule_header_stmtsContext.class,
-                f("Missing header statements in submodule:%s", submoduleName),
-                true);
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>One Belongs-to statement present</li>
-     * </ol>
-     */
-    @Override
-    public void enterSubmodule_header_stmts(Submodule_header_stmtsContext ctx) {
-        String submoduleName = getRootParentName(ctx);
-
-        checkPresentChildOfType(
-                ctx,
-                Belongs_to_stmtContext.class,
-                f("Missing belongs-to statement in submodule:%s", submoduleName),
-                true);
-
-        // check Yang version present, if not issue warning
-        checkYangVersion(ctx, submoduleName);
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Identifier contains only permitted characters</li>
-     * <li>One Prefix statement child</li>
-     * </ol>
-     */
-    @Override
-    public void enterBelongs_to_stmt(Belongs_to_stmtContext ctx) {
-        String belongToName = getName(ctx);
-        String rootParentName = getRootParentName(ctx);
-
-        checkIdentifier(belongToName,
-                f("In (sub)module:%s , Belongs-to statement", rootParentName));
-
-        checkPresentChildOfType(
-                ctx,
-                Prefix_stmtContext.class,
-                f("Missing prefix statement in belongs-to:%s, in (sub)module:%s",
-                        belongToName, rootParentName), true);
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>At least one Revision statement present</li>
-     * </ol>
-     */
-    @Override
-    public void enterRevision_stmts(Revision_stmtsContext ctx) {
-        String rootParentName = getRootParentName(ctx);
-
-        checkPresentChildOfType(
-                ctx,
-                Revision_stmtContext.class,
-                f("Missing at least one revision statement in (sub)module:%s",
-                        rootParentName), false);
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>One Namespace statement present</li>
-     * </ol>
-     */
-    @Override
-    public void enterModule_header_stmts(Module_header_stmtsContext ctx) {
-        String moduleName = getRootParentName(ctx);
-
-        checkPresentChildOfType(ctx, Namespace_stmtContext.class,
-                f("Missing namespace statement in module:%s", moduleName), true);
-
-        // check Yang version present, if not issue warning
-        checkYangVersion(ctx, moduleName);
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Namespace string can be parsed as URI</li>
-     * </ol>
-     */
-    @Override
-    public void enterNamespace_stmt(Namespace_stmtContext ctx) {
-        String namespaceName = getName(ctx);
-        String rootParentName = getRootParentName(ctx);
-
-        try {
-            new URI(namespaceName);
-        } catch (URISyntaxException e) {
-            throw new YangValidationException(f(
-                    "Namespace:%s in module:%s cannot be parsed as URI",
-                    namespaceName, rootParentName));
-        }
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Identifier contains only permitted characters</li>
-     * <li>Every import(identified by identifier) within a module/submodule is
-     * present only once</li>
-     * <li>One prefix statement child</li>
-     * <li>One revision-date statement child</li>
-     * </ol>
-     */
-    @Override
-    public void enterImport_stmt(Import_stmtContext ctx) {
-        String importName = getName(ctx);
-        String rootParentName = getRootParentName(ctx);
-
-        checkIdentifier(importName,
-                f("In (sub)module:%s , Import statement", rootParentName));
-
-        if (uniqueImports.contains(importName))
-            throw new YangValidationException(f(
-                    "Module:%s imported twice in (sub)module:%s", importName,
-                    rootParentName));
-        uniqueImports.add(importName);
-
-        checkPresentChildOfType(
-                ctx,
-                Prefix_stmtContext.class,
-                f("Missing prefix statement in import:%s, in (sub)module:%s",
-                        importName, rootParentName), true);
-        //checkPresentChildOfType(
-        //        ctx,
-        //        Revision_date_stmtContext.class,
-        //        f("Missing revision-date statement in import:%s, in (sub)module:%s",
-        //                importName, rootParentName), true);
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Date is in valid format</li>
-     * </ol>
-     */
-    @Override
-    public void enterRevision_date_stmt(Revision_date_stmtContext ctx) {
-        String rootParentName = getRootParentName(ctx);
-        String exceptionMessage = f(
-                "Invalid date format for revision-date:%s in import/include statement:%s, in (sub)module:%s , expected date format is:%s",
-                getName(ctx), getRootParentName(ctx), rootParentName,
-                YangModelParserListenerImpl.simpleDateFormat.format(new Date()));
-
-        validateDateFormat(getName(ctx),
-                YangModelParserListenerImpl.simpleDateFormat, exceptionMessage);
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Identifier contains only permitted characters</li>
-     * <li>Every include(identified by identifier) within a module/submodule is
-     * present only once</li>
-     * <li>One Revision-date statement child</li>
-     * </ol>
-     */
-    @Override
-    public void enterInclude_stmt(Include_stmtContext ctx) {
-        String includeName = getName(ctx);
-        String rootParentName = getRootParentName(ctx);
-
-        checkIdentifier(includeName,
-                f("In (sub)module:%s , Include statement", rootParentName));
-
-        if (uniqueIncludes.contains(includeName))
-            throw new YangValidationException(f(
-                    "Submodule:%s included twice in (sub)module:%s",
-                    includeName, rootParentName));
-        uniqueIncludes.add(includeName);
-
-        checkPresentChildOfType(
-                ctx,
-                Revision_date_stmtContext.class,
-                f("Missing revision-date statement in include:%s, in (sub)module:%s",
-                        includeName, rootParentName), true);
-    }
-
-    static final String SUPPORTED_YANG_VERSION = "1";
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Yang-version is specified as 1</li>
-     * </ol>
-     */
-    @Override
-    public void enterYang_version_stmt(YangParser.Yang_version_stmtContext ctx) {
-        String version = getName(ctx);
-        String rootParentName = getRootParentName(ctx);
-        if (!version.equals(SUPPORTED_YANG_VERSION)) {
-            throw new YangValidationException(
-                    f("Unsupported yang version:%s, in (sub)module:%s, supported version:%s",
-                            version, rootParentName, SUPPORTED_YANG_VERSION));
-        }
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Date is in valid format</li>
-     * </ol>
-     */
-    @Override
-    public void enterRevision_stmt(YangParser.Revision_stmtContext ctx) {
-        String parentName = getRootParentName(ctx);
-        String exceptionMessage = f(
-                "Invalid date format for revision:%s in (sub)module:%s, expected date format is:%s",
-                getName(ctx), parentName,
-                YangModelParserListenerImpl.simpleDateFormat.format(new Date()));
-
-        validateDateFormat(getName(ctx),
-                YangModelParserListenerImpl.simpleDateFormat, exceptionMessage);
-    }
-
-    /**
-     * Rules:
-     * <ol>
-     * <li>Identifier contains only permitted characters</li>
-     * <li>Every prefix(identified by identifier) within a module/submodule is
-     * presented only once</li>
-     * </ol>
-     */
-    @Override
-    public void enterPrefix_stmt(Prefix_stmtContext ctx) {
-        String name = getName(ctx);
-        checkIdentifier(
-                name,
-                f("In module or import statement:%s , Prefix",
-                        getRootParentName(ctx)));
-
-        if (uniquePrefixes.contains(name))
-            throw new YangValidationException(f(
-                    "Not a unique prefix:%s, in (sub)module:%s", name,
-                    getRootParentName(ctx)));
-        uniquePrefixes.add(name);
-    }
-
-    private String getRootParentName(ParseTree ctx) {
-        ParseTree root = ctx;
-        while (root.getParent() != null) {
-            root = root.getParent();
-        }
-        return getName(root);
-    }
-
-    private static String getName(ParseTree child) {
-        return YangModelBuilderUtil.stringFromNode(child);
-    }
-
-    private static String f(String base, Object... args) {
-        return String.format(base, args);
-    }
-
-    private static void checkYangVersion(ParseTree ctx, String moduleName) {
-        if (!checkPresentChildOfType(ctx, Yang_version_stmtContext.class, true))
-            logger.warn(f(
-                    "Yang version statement not present in module:%s, Validating as yang version:%s",
-                    moduleName, SUPPORTED_YANG_VERSION));
-    }
-
-    private static void validateDateFormat(String string, DateFormat format,
-            String message) {
-        try {
-            format.parse(string);
-        } catch (ParseException e) {
-            throw new YangValidationException(message);
-        }
-    }
-
-    private static Pattern identifierPattern = Pattern
-            .compile("[a-zA-Z_][a-zA-Z0-9_.-]*");
-
-    static void checkIdentifier(String name, String messagePrefix) {
-        if (!identifierPattern.matcher(name).matches())
-            throw new YangValidationException(f(
-                    "%s identifier:%s is not in required format:%s",
-                    messagePrefix, name, identifierPattern.toString()));
-    }
-
-    private static void checkPresentChildOfType(ParseTree ctx,
-            Class<?> expectedChildType, String message, boolean atMostOne) {
-        if (!checkPresentChildOfType(ctx, expectedChildType, atMostOne))
-            throw new YangValidationException(message);
-    }
-
-    private static boolean checkPresentChildOfType(ParseTree ctx,
-            Class<?> expectedChildType, boolean atMostOne) {
-
-        int count = 0;
-
-        for (int i = 0; i < ctx.getChildCount(); i++) {
-            ParseTree child = ctx.getChild(i);
-            if (expectedChildType.isInstance(child))
-                count++;
-        }
-
-        return atMostOne ? count == 1 ? true : false : count != 0 ? true
-                : false;
-    }
-}
@@ -5,13 +5,13 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.controller.yang.model.parser.impl;
+package org.opendaylight.controller.yang.model.parser.util;
 
 /**
  * Unchecked exception thrown if yang definition is not valid according to
 
 /**
  * Unchecked exception thrown if yang definition is not valid according to
- * {@link YangModelValidationListener}
+ * {@link YangModelBasicValidationListener}
  */
  */
-public class YangValidationException extends RuntimeException {
+public final class YangValidationException extends RuntimeException {
 
     private static final long serialVersionUID = 7414330400390825381L;
 
 
     private static final long serialVersionUID = 7414330400390825381L;
 
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/BasicValidations.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/BasicValidations.java
new file mode 100644 (file)
index 0000000..f9e3fc2
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2013 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/eplv10.html
+ */
+package org.opendaylight.controller.yang.model.validator;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yang_version_stmtContext;
+import org.opendaylight.controller.yang.model.parser.impl.YangModelParserListenerImpl;
+import org.opendaylight.controller.yang.model.parser.util.YangValidationException;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Reusable checks of basic constraints on yang statements
+ */
+final class BasicValidations {
+
+    static final String SUPPORTED_YANG_VERSION = "1";
+
+    static void checkNotPresentBoth(ParseTree parent,
+            Class<? extends ParseTree> childType1,
+            Class<? extends ParseTree> childType2) {
+        if (BasicValidations.checkPresentChildOfTypeSafe(parent, childType1,
+                true)
+                && BasicValidations.checkPresentChildOfTypeSafe(parent,
+                        childType2, false))
+            ValidationUtil
+                    .ex(ValidationUtil
+                            .f("(In (sub)module:%s) Both %s and %s statement present in %s:%s",
+                                    ValidationUtil.getRootParentName(parent),
+                                    ValidationUtil
+                                            .getSimpleStatementName(childType1),
+                                    ValidationUtil
+                                            .getSimpleStatementName(childType2),
+                                    ValidationUtil
+                                            .getSimpleStatementName(parent
+                                                    .getClass()),
+                                    ValidationUtil.getName(parent)));
+    }
+
+    static void checkOnlyPermittedValues(ParseTree ctx,
+            Set<String> permittedValues) {
+        String mandatory = ValidationUtil.getName(ctx);
+        String rootParentName = ValidationUtil.getRootParentName(ctx);
+
+        if (!permittedValues.contains(mandatory))
+            ValidationUtil
+                    .ex(ValidationUtil
+                            .f("(In (sub)module:%s) %s:%s, illegal value for %s statement, only permitted:%s",
+                                    rootParentName, ValidationUtil
+                                            .getSimpleStatementName(ctx
+                                                    .getClass()), mandatory,
+                                    ValidationUtil.getSimpleStatementName(ctx
+                                            .getClass()), permittedValues));
+    }
+
+    static void checkUniquenessInNamespace(ParseTree stmt, Set<String> uniques) {
+        String name = ValidationUtil.getName(stmt);
+        String rootParentName = ValidationUtil.getRootParentName(stmt);
+
+        if (uniques.contains(name))
+            ValidationUtil.ex(ValidationUtil.f(
+                    "(In (sub)module:%s) %s:%s not unique in (sub)module",
+                    rootParentName,
+                    ValidationUtil.getSimpleStatementName(stmt.getClass()),
+                    name));
+        uniques.add(name);
+    }
+
+    /**
+     * Check if only one module or submodule is present in session(one yang
+     * file)
+     */
+    static void checkOnlyOneModulePresent(String moduleName, String globalId) {
+        if (globalId != null)
+            ValidationUtil.ex(ValidationUtil
+                    .f("Multiple (sub)modules per file"));
+    }
+
+    static void checkPresentYangVersion(ParseTree ctx, String moduleName) {
+        if (!checkPresentChildOfTypeSafe(ctx, Yang_version_stmtContext.class,
+                true))
+            ValidationUtil
+                    .ex(ValidationUtil
+                            .f("Yang version statement not present in module:%s, Validating as yang version:%s",
+                                    moduleName, SUPPORTED_YANG_VERSION));
+    }
+
+    static void checkDateFormat(ParseTree stmt, DateFormat format) {
+        try {
+            format.parse(ValidationUtil.getName(stmt));
+        } catch (ParseException e) {
+            String exceptionMessage = ValidationUtil
+                    .f("(In (sub)module:%s) %s:%s, invalid date format expected date format is:%s",
+                            ValidationUtil.getRootParentName(stmt),
+                            ValidationUtil.getSimpleStatementName(stmt
+                                    .getClass()), ValidationUtil.getName(stmt),
+                            YangModelParserListenerImpl.simpleDateFormat
+                                    .format(new Date()));
+            ValidationUtil.ex(exceptionMessage);
+        }
+    }
+
+    static Pattern identifierPattern = Pattern
+            .compile("[a-zA-Z_][a-zA-Z0-9_.-]*");
+
+    static void checkIdentifier(ParseTree statement) {
+        checkIdentifierInternal(statement, ValidationUtil.getName(statement));
+    }
+
+    static void checkIdentifierInternal(ParseTree statement, String name) {
+        if (!identifierPattern.matcher(name).matches()) {
+
+            String message = ValidationUtil
+                    .f("%s statement identifier:%s is not in required format:%s",
+                            ValidationUtil.getSimpleStatementName(statement
+                                    .getClass()), name, identifierPattern
+                                    .toString());
+            String parent = ValidationUtil.getRootParentName(statement);
+            message = parent.equals(name) ? message : ValidationUtil.f(
+                    "(In (sub)module:%s) %s", parent, message);
+
+            ValidationUtil.ex(message);
+        }
+    }
+
+    static Pattern prefixedIdentifierPattern = Pattern.compile("(.+):(.+)");
+
+    static void checkPrefixedIdentifier(ParseTree statement) {
+        checkPrefixedIdentifierInternal(statement,
+                ValidationUtil.getName(statement));
+    }
+
+    private static void checkPrefixedIdentifierInternal(ParseTree statement,
+            String id) {
+        Matcher matcher = prefixedIdentifierPattern.matcher(id);
+
+        if (matcher.matches()) {
+            try {
+                // check prefix
+                checkIdentifierInternal(statement, matcher.group(1));
+                // check ID
+                checkIdentifierInternal(statement, matcher.group(2));
+            } catch (YangValidationException e) {
+                ValidationUtil.ex(ValidationUtil.f(
+                        "Prefixed id:%s not in required format, details:%s",
+                        id, e.getMessage()));
+            }
+        } else
+            checkIdentifierInternal(statement, id);
+    }
+
+    static void checkSchemaNodeIdentifier(ParseTree statement) {
+        String id = ValidationUtil.getName(statement);
+
+        try {
+            for (String oneOfId : id.split("/")) {
+                if (oneOfId.isEmpty())
+                    continue;
+                checkPrefixedIdentifierInternal(statement, oneOfId);
+            }
+        } catch (YangValidationException e) {
+            ValidationUtil.ex(ValidationUtil.f(
+                    "Schema node id:%s not in required format, details:%s", id,
+                    e.getMessage()));
+        }
+    }
+
+    private static interface MessageProvider {
+        String getMessage();
+    }
+
+    static void checkPresentChildOfTypeInternal(ParseTree parent,
+            Set<Class<? extends ParseTree>> expectedChildType,
+            MessageProvider message, boolean atMostOne) {
+        if (!checkPresentChildOfTypeSafe(parent, expectedChildType, atMostOne)) {
+            String str = atMostOne ? "(Expected exactly one statement) "
+                    + message.getMessage() : message.getMessage();
+            ValidationUtil.ex(str);
+        }
+    }
+
+    static void checkPresentChildOfType(final ParseTree parent,
+            final Class<? extends ParseTree> expectedChildType,
+            boolean atMostOne) {
+
+        // Construct message in checkPresentChildOfTypeInternal only if
+        // validaiton fails, not in advance
+        MessageProvider message = new MessageProvider() {
+
+            @Override
+            public String getMessage() {
+                String message = ValidationUtil
+                        .f("Missing %s statement in %s:%s", ValidationUtil
+                                .getSimpleStatementName(expectedChildType),
+                                ValidationUtil.getSimpleStatementName(parent
+                                        .getClass()), ValidationUtil
+                                        .getName(parent));
+
+                String root = ValidationUtil.getRootParentName(parent);
+                message = parent.equals(ValidationUtil
+                        .getRootParentName(parent)) ? message : ValidationUtil
+                        .f("(In (sub)module:%s) %s", root, message);
+                return message;
+            }
+        };
+
+        Set<Class<? extends ParseTree>> expectedChildTypeSet = Sets
+                .newHashSet();
+        expectedChildTypeSet.add(expectedChildType);
+
+        checkPresentChildOfTypeInternal(parent, expectedChildTypeSet, message,
+                atMostOne);
+    }
+
+    static void checkPresentChildOfTypes(final ParseTree parent,
+            final Set<Class<? extends ParseTree>> expectedChildTypes,
+            boolean atMostOne) {
+
+        // Construct message in checkPresentChildOfTypeInternal only if
+        // validaiton fails, not in advance
+        MessageProvider message = new MessageProvider() {
+
+            @Override
+            public String getMessage() {
+                StringBuilder childTypes = new StringBuilder();
+                String orStr = " OR ";
+                for (Class<? extends ParseTree> type : expectedChildTypes) {
+                    childTypes.append(ValidationUtil
+                            .getSimpleStatementName(type));
+                    childTypes.append(orStr);
+                }
+
+                String message = ValidationUtil
+                        .f("Missing %s statement in %s:%s", childTypes
+                                .toString(), ValidationUtil
+                                .getSimpleStatementName(parent.getClass()),
+                                ValidationUtil.getName(parent));
+
+                String root = ValidationUtil.getRootParentName(parent);
+                message = parent.equals(ValidationUtil
+                        .getRootParentName(parent)) ? message : ValidationUtil
+                        .f("(In (sub)module:%s) %s", root, message);
+
+                return message;
+            }
+        };
+
+        checkPresentChildOfTypeInternal(parent, expectedChildTypes, message,
+                atMostOne);
+    }
+
+    static boolean checkPresentChildOfTypeSafe(ParseTree parent,
+            Set<Class<? extends ParseTree>> expectedChildType, boolean atMostOne) {
+
+        int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(
+                parent, expectedChildType);
+
+        return atMostOne ? foundChildrenOfType == 1 ? true : false
+                : foundChildrenOfType != 0 ? true : false;
+    }
+
+    static boolean checkPresentChildOfTypeSafe(ParseTree parent,
+            Class<? extends ParseTree> expectedChildType, boolean atMostOne) {
+
+        int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(
+                parent, expectedChildType);
+
+        return atMostOne ? foundChildrenOfType == 1 ? true : false
+                : foundChildrenOfType != 0 ? true : false;
+    }
+
+    static List<String> getAndCheckUniqueKeys(ParseTree ctx) {
+        String key = ValidationUtil.getName(ctx);
+        ParseTree parent = ctx.getParent();
+        String rootParentName = ValidationUtil.getRootParentName(ctx);
+
+        List<String> keyList = ValidationUtil.listKeysFromId(key);
+        Set<String> duplicates = ValidationUtil.getDuplicates(keyList);
+
+        if (duplicates.size() != 0)
+            ValidationUtil.ex(ValidationUtil.f(
+                    "(In (sub)module:%s) %s:%s, %s:%s contains duplicates:%s",
+                    rootParentName,
+                    ValidationUtil.getSimpleStatementName(parent.getClass()),
+                    ValidationUtil.getName(parent),
+                    ValidationUtil.getSimpleStatementName(ctx.getClass()), key,
+                    duplicates));
+        return keyList;
+    }
+}
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/ValidationUtil.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/ValidationUtil.java
new file mode 100644 (file)
index 0000000..31c4b8a
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2013 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/eplv10.html
+ */
+package org.opendaylight.controller.yang.model.validator;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext;
+import org.opendaylight.controller.yang.model.parser.util.YangModelBuilderUtil;
+import org.opendaylight.controller.yang.model.parser.util.YangValidationException;
+
+/**
+ * Validation utilities
+ */
+final class ValidationUtil {
+
+    static void ex(String message) {
+        throw new YangValidationException(message);
+    }
+
+    static Set<String> getDuplicates(Collection<String> keyList) {
+        Set<String> all = new HashSet<String>();
+        Set<String> duplicates = new HashSet<String>();
+
+        for (String key : keyList) {
+            if (!all.add(key))
+                duplicates.add(key);
+        }
+        return duplicates;
+    }
+
+    static List<String> listKeysFromId(String keys) {
+        return Arrays.asList(keys.split(" "));
+    }
+
+    static String getRootParentName(ParseTree ctx) {
+        ParseTree root = getRootParent(ctx);
+        return ValidationUtil.getName(root);
+    }
+
+    private static ParseTree getRootParent(ParseTree ctx) {
+        ParseTree root = ctx;
+        while (root.getParent() != null) {
+            if (root.getClass().equals(Module_stmtContext.class)
+                    || root.getClass().equals(Submodule_stmtContext.class))
+                break;
+            root = root.getParent();
+        }
+        return root;
+    }
+
+    static String getName(ParseTree child) {
+        return YangModelBuilderUtil.stringFromNode(child);
+    }
+
+    static String f(String base, Object... args) {
+        return String.format(base, args);
+    }
+
+    /**
+     * Get simple name from statement class e.g. Module from Module_stmt_context
+     */
+    static String getSimpleStatementName(
+            Class<? extends ParseTree> typeOfStatement) {
+
+        String className = typeOfStatement.getSimpleName();
+        int lastIndexOf = className.indexOf('$');
+        className = lastIndexOf == -1 ? className : className
+                .substring(lastIndexOf + 1);
+        int indexOfStmt = className.indexOf("_stmt");
+        int index = indexOfStmt == -1 ? className.indexOf("_arg") : indexOfStmt;
+        return className.substring(0, index).replace('_', '-');
+    }
+
+    static int countPresentChildrenOfType(ParseTree parent,
+            Set<Class<? extends ParseTree>> expectedChildTypes) {
+        int foundChildrenOfType = 0;
+
+        for (Class<? extends ParseTree> type : expectedChildTypes) {
+            foundChildrenOfType += countPresentChildrenOfType(parent, type);
+        }
+        return foundChildrenOfType;
+    }
+
+    static int countPresentChildrenOfType(ParseTree parent,
+            Class<? extends ParseTree> expectedChildType) {
+        int foundChildrenOfType = 0;
+
+        for (int i = 0; i < parent.getChildCount(); i++) {
+            ParseTree child = parent.getChild(i);
+            if (expectedChildType.isInstance(child))
+                foundChildrenOfType++;
+        }
+        return foundChildrenOfType;
+    }
+
+}
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidationListener.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidationListener.java
new file mode 100644 (file)
index 0000000..37e2a21
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.yang.model.validator;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Set;
+
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Anyxml_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Argument_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Augment_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Base_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Case_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Choice_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Config_argContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Container_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Default_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviate_add_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviation_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Extension_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Feature_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Grouping_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Identity_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.If_feature_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Import_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Include_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Key_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Leaf_list_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Leaf_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.List_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Mandatory_argContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Mandatory_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_header_stmtsContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Notification_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Ordered_by_argContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Refine_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Rpc_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Status_argContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Type_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Typedef_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Unique_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Uses_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yin_element_argContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParserBaseListener;
+import org.opendaylight.controller.yang.model.parser.impl.YangModelParserListenerImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Validation listener that validates yang statements according to RFC-6020.
+ * This validator expects only one module or submodule per file and performs
+ * only basic validation where context from all yang models is not present.
+ */
+final class YangModelBasicValidationListener extends YangParserBaseListener {
+
+    private static final Logger logger = LoggerFactory
+            .getLogger(YangModelBasicValidationListener.class);
+
+    private final Set<String> uniquePrefixes;
+    private final Set<String> uniqueImports;
+    private final Set<String> uniqueIncludes;
+
+    private String globalModuleId;
+
+    YangModelBasicValidationListener() {
+        super();
+        uniquePrefixes = Sets.newHashSet();
+        uniqueImports = Sets.newHashSet();
+        uniqueIncludes = Sets.newHashSet();
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>Header statements present(mandatory prefix and namespace statements
+     * are in header)</li>
+     * <li>Only one module or submodule per file</li>
+     * </ol>
+     */
+    @Override
+    public void enterModule_stmt(Module_stmtContext ctx) {
+
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkPresentChildOfType(ctx,
+                Module_header_stmtsContext.class, true);
+
+        String moduleName = ValidationUtil.getName(ctx);
+        BasicValidations.checkOnlyOneModulePresent(moduleName, globalModuleId);
+        globalModuleId = moduleName;
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>Header statements present(mandatory belongs-to statement is in
+     * header)</li>
+     * <li>Only one module or submodule per file</li>
+     * </ol>
+     */
+    @Override
+    public void enterSubmodule_stmt(Submodule_stmtContext ctx) {
+
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkPresentChildOfType(ctx,
+                Submodule_header_stmtsContext.class, true);
+
+        String submoduleName = ValidationUtil.getName(ctx);
+        BasicValidations.checkOnlyOneModulePresent(submoduleName,
+                globalModuleId);
+        globalModuleId = submoduleName;
+
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>One Belongs-to statement present</li>
+     * </ol>
+     */
+    @Override
+    public void enterSubmodule_header_stmts(Submodule_header_stmtsContext ctx) {
+        BasicValidations.checkPresentChildOfType(ctx,
+                Belongs_to_stmtContext.class, true);
+
+        // check Yang version present, if not log
+        try {
+            BasicValidations.checkPresentYangVersion(ctx,
+                    ValidationUtil.getRootParentName(ctx));
+        } catch (Exception e) {
+            logger.debug(e.getMessage());
+        }
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>One Namespace statement present</li>
+     * <li>One Prefix statement present</li>
+     * </ol>
+     */
+    @Override
+    public void enterModule_header_stmts(Module_header_stmtsContext ctx) {
+        String moduleName = ValidationUtil.getRootParentName(ctx);
+
+        BasicValidations.checkPresentChildOfType(ctx,
+                Namespace_stmtContext.class, true);
+        BasicValidations.checkPresentChildOfType(ctx, Prefix_stmtContext.class,
+                true);
+
+        // check Yang version present, if not log
+        try {
+            BasicValidations.checkPresentYangVersion(ctx, moduleName);
+        } catch (Exception e) {
+            logger.debug(e.getMessage());
+        }
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Date is in valid format</li>
+     * </ol>
+     */
+    @Override
+    public void enterRevision_stmt(Revision_stmtContext ctx) {
+        BasicValidations.checkDateFormat(ctx,
+                YangModelParserListenerImpl.simpleDateFormat);
+
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>One Prefix statement child</li>
+     * </ol>
+     */
+    @Override
+    public void enterBelongs_to_stmt(Belongs_to_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkPresentChildOfType(ctx, Prefix_stmtContext.class,
+                true);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Namespace string can be parsed as URI</li>
+     * </ol>
+     */
+    @Override
+    public void enterNamespace_stmt(Namespace_stmtContext ctx) {
+        String namespaceName = ValidationUtil.getName(ctx);
+        String rootParentName = ValidationUtil.getRootParentName(ctx);
+
+        try {
+            new URI(namespaceName);
+        } catch (URISyntaxException e) {
+            ValidationUtil.ex(ValidationUtil.f(
+                    "(In module:%s) Namespace:%s cannot be parsed as URI",
+                    rootParentName, namespaceName));
+        }
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>Every import(identified by identifier) within a module/submodule is
+     * present only once</li>
+     * <li>One prefix statement child</li>
+     * </ol>
+     */
+    @Override
+    public void enterImport_stmt(Import_stmtContext ctx) {
+
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkUniquenessInNamespace(ctx, uniqueImports);
+
+        BasicValidations.checkPresentChildOfType(ctx, Prefix_stmtContext.class,
+                true);
+
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Date is in valid format</li>
+     * </ol>
+     */
+    @Override
+    public void enterRevision_date_stmt(Revision_date_stmtContext ctx) {
+        BasicValidations.checkDateFormat(ctx,
+                YangModelParserListenerImpl.simpleDateFormat);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>Every include(identified by identifier) within a module/submodule is
+     * present only once</li>
+     * </ol>
+     */
+    @Override
+    public void enterInclude_stmt(Include_stmtContext ctx) {
+
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkUniquenessInNamespace(ctx, uniqueIncludes);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Yang-version is specified as 1</li>
+     * </ol>
+     */
+    @Override
+    public void enterYang_version_stmt(YangParser.Yang_version_stmtContext ctx) {
+        String version = ValidationUtil.getName(ctx);
+        String rootParentName = ValidationUtil.getRootParentName(ctx);
+        if (!version.equals(BasicValidations.SUPPORTED_YANG_VERSION)) {
+            ValidationUtil
+                    .ex(ValidationUtil
+                            .f("(In (sub)module:%s) Unsupported yang version:%s, supported version:%s",
+                                    rootParentName, version,
+                                    BasicValidations.SUPPORTED_YANG_VERSION));
+        }
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>Every prefix(identified by identifier) within a module/submodule is
+     * presented only once</li>
+     * </ol>
+     */
+    @Override
+    public void enterPrefix_stmt(Prefix_stmtContext ctx) {
+
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkUniquenessInNamespace(ctx, uniquePrefixes);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>One type statement child</li>
+     * </ol>
+     */
+    @Override
+    public void enterTypedef_stmt(Typedef_stmtContext ctx) {
+
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkPresentChildOfType(ctx, Type_stmtContext.class,
+                true);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>(Prefix):Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterType_stmt(Type_stmtContext ctx) {
+        BasicValidations.checkPrefixedIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterContainer_stmt(Container_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>One type statement child</li>
+     * <li>Default statement must not be present if mandatory statement is</li>
+     * </ol>
+     */
+    @Override
+    public void enterLeaf_stmt(Leaf_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkPresentChildOfType(ctx, Type_stmtContext.class,
+                true);
+
+        BasicValidations.checkNotPresentBoth(ctx, Mandatory_stmtContext.class,
+                Default_stmtContext.class);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>One type statement child</li>
+     * </ol>
+     */
+    @Override
+    public void enterLeaf_list_stmt(Leaf_list_stmtContext ctx) {
+
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkPresentChildOfType(ctx, Type_stmtContext.class,
+                true);
+    }
+
+    private static final Set<String> permittedOrderByArgs = Sets.newHashSet(
+            "system", "user");
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Value must be one of: system, user</li>
+     * </ol>
+     */
+    @Override
+    public void enterOrdered_by_arg(Ordered_by_argContext ctx) {
+        BasicValidations.checkOnlyPermittedValues(ctx, permittedOrderByArgs);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterList_stmt(List_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+        // TODO check: "if config==true then key must be present" could be
+        // performed
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>No duplicate keys</li>
+     * </ol>
+     */
+    @Override
+    public void enterKey_stmt(Key_stmtContext ctx) {
+        BasicValidations.getAndCheckUniqueKeys(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <liNo duplicate uniques</li>
+     * </ol>
+     */
+    @Override
+    public void enterUnique_stmt(Unique_stmtContext ctx) {
+        BasicValidations.getAndCheckUniqueKeys(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * <li>Default statement must not be present if mandatory statement is</li>
+     * </ol>
+     */
+    @Override
+    public void enterChoice_stmt(Choice_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+
+        BasicValidations.checkNotPresentBoth(ctx, Mandatory_stmtContext.class,
+                Default_stmtContext.class);
+
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterCase_stmt(Case_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    private static final Set<String> permittedBooleanArgs = Sets.newHashSet(
+            "true", "false");
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Value must be one of: true, false</li>
+     * </ol>
+     */
+    @Override
+    public void enterMandatory_arg(Mandatory_argContext ctx) {
+        BasicValidations.checkOnlyPermittedValues(ctx, permittedBooleanArgs);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterAnyxml_stmt(Anyxml_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterGrouping_stmt(Grouping_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>(Prefix):Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterUses_stmt(Uses_stmtContext ctx) {
+        BasicValidations.checkPrefixedIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterRefine_stmt(Refine_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterRpc_stmt(Rpc_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterNotification_stmt(Notification_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Schema Node Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterAugment_stmt(Augment_stmtContext ctx) {
+        BasicValidations.checkSchemaNodeIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterIdentity_stmt(Identity_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>(Prefix):Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterBase_stmt(Base_stmtContext ctx) {
+        BasicValidations.checkPrefixedIdentifier(ctx);
+
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Value must be one of: true, false</li>
+     * </ol>
+     */
+    @Override
+    public void enterYin_element_arg(Yin_element_argContext ctx) {
+        BasicValidations.checkOnlyPermittedValues(ctx, permittedBooleanArgs);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterExtension_stmt(Extension_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterArgument_stmt(Argument_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterFeature_stmt(Feature_stmtContext ctx) {
+        BasicValidations.checkIdentifier(ctx);
+
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>(Prefix):Identifier is in required format</li>
+     * </ol>
+     */
+    @Override
+    public void enterIf_feature_stmt(If_feature_stmtContext ctx) {
+        BasicValidations.checkPrefixedIdentifier(ctx);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Schema Node Identifier is in required format</li>
+     * <li>At least one deviate-* statement child</li>
+     * </ol>
+     */
+    @Override
+    public void enterDeviation_stmt(Deviation_stmtContext ctx) {
+        BasicValidations.checkSchemaNodeIdentifier(ctx);
+
+        Set<Class<? extends ParseTree>> types = Sets.newHashSet();
+        types.add(Deviate_add_stmtContext.class);
+        types.add(Deviate_add_stmtContext.class);
+        BasicValidations.checkPresentChildOfTypes(ctx, types, false);
+    }
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Value must be one of: true, false</li>
+     * </ol>
+     */
+    @Override
+    public void enterConfig_arg(Config_argContext ctx) {
+        BasicValidations.checkOnlyPermittedValues(ctx, permittedBooleanArgs);
+    }
+
+    private static final Set<String> permittedStatusArgs = Sets.newHashSet(
+            "current", "deprecated", "obsolete");
+
+    /**
+     * Constraints:
+     * <ol>
+     * <li>Value must be one of: "current", "deprecated", "obsolete"</li>
+     * </ol>
+     */
+    @Override
+    public void enterStatus_arg(Status_argContext ctx) {
+        BasicValidations.checkOnlyPermittedValues(ctx, permittedStatusArgs);
+    }
+
+}
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidator.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/main/java/org/opendaylight/controller/yang/model/validator/YangModelBasicValidator.java
new file mode 100644 (file)
index 0000000..dc80dba
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.yang.model.validator;
+
+import java.util.List;
+
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.controller.yang.model.parser.util.YangValidationException;
+
+/**
+ * Exposed basic yang validation.
+ *
+ * Every file is validated using {@link YangModelBasicValidationListener}.
+ */
+public final class YangModelBasicValidator {
+
+    private final ParseTreeWalker walker;
+
+    public YangModelBasicValidator(ParseTreeWalker walker) {
+        this.walker = walker;
+    }
+
+    public YangModelBasicValidator() {
+        this.walker = new ParseTreeWalker();
+    }
+
+    public void validate(List<ParseTree> trees) {
+        for (int i = 0; i < trees.size(); i++) {
+            try {
+                final YangModelBasicValidationListener yangModelParser = new YangModelBasicValidationListener();
+                walker.walk(yangModelParser, trees.get(i));
+            } catch (YangValidationException e) {
+                // wrap exception to add information about which file failed
+                throw new YangValidationException(
+                        "Yang validation failed for file" + e);
+            }
+        }
+    }
+
+}
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_Module.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_Module.java
deleted file mode 100644 (file)
index 76715e8..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-package org.opendaylight.controller.yang.model.parser.impl;
-
-import static org.hamcrest.core.Is.*;
-import static org.junit.Assert.*;
-import static org.junit.matchers.JUnitMatchers.*;
-import static org.mockito.Mockito.*;
-
-import java.util.Date;
-
-import org.antlr.v4.runtime.tree.ParseTree;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Import_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Include_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_header_stmtsContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtsContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.StringContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yang_version_stmtContext;
-
-public class YangModelValidationListenerTest_Module {
-
-    private YangModelValidationListener valid;
-    private Module_stmtContext ctx;
-
-    @Before
-    public void setUp() {
-        valid = new YangModelValidationListener();
-    }
-
-    @Test(expected = YangValidationException.class)
-    public void testRevisionInvalidDateFormat() {
-        Revision_stmtContext mockedRev = mockModuleWithRevision(2, "badFormat");
-
-        try {
-            valid.enterRevision_stmt(mockedRev);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Invalid date format for revision:badFormat in (sub)module:module1, expected date format is:"));
-            throw e;
-        }
-    }
-
-    private Revision_stmtContext mockModuleWithRevision(int moduleChildren,
-            String date) {
-        Revision_stmtContext mockedRev = mock(Revision_stmtContext.class);
-        doReturn(1).when(mockedRev).getChildCount();
-        mockName(mockedRev, date);
-
-        Revision_stmtsContext revs = mockRevisionsParent(2, mockedRev);
-
-        mockModuleParent(moduleChildren, revs, "module1");
-        return mockedRev;
-    }
-
-    @Test
-    public void testRevisionValidDateFormat() {
-        Revision_stmtContext mockedRev = mockModuleWithRevision(2,
-                getFormattedDate());
-
-        valid.enterRevision_stmt(mockedRev);
-    }
-
-    private String getFormattedDate() {
-        return YangModelParserListenerImpl.simpleDateFormat.format(new Date());
-    }
-
-    @Test(expected = YangValidationException.class)
-    public void testNoRevision() {
-
-        Module_stmtContext ctx = mock(Module_stmtContext.class);
-        doReturn(1).when(ctx).getChildCount();
-        mockName(ctx, "module1");
-
-        try {
-            valid.enterModule_stmt(ctx);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Missing revision statements in module:module1"));
-            throw e;
-        }
-    }
-
-    @Test(expected = YangValidationException.class)
-    public void testNoHeaderStmts() {
-        mockModuleWithRevision(2, "1999-4-5");
-
-        try {
-            valid.enterModule_stmt(ctx);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Missing header statements in module:module1"));
-            throw e;
-        }
-    }
-
-    @Test(expected = YangValidationException.class)
-    public void testNoNamespace() {
-        Module_header_stmtsContext header = mock(Module_header_stmtsContext.class);
-        mockModuleParent(2, header, "module1");
-
-        try {
-            valid.enterModule_header_stmts(header);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Missing namespace statement in module:module1"));
-            throw e;
-        }
-    }
-
-    @Test
-    public void testPrefixes() {
-        Prefix_stmtContext pref = mock(Prefix_stmtContext.class);
-        doReturn(1).when(pref).getChildCount();
-        mockName(pref, "unique1");
-        mockModuleParent(2, pref, "module1");
-        valid.enterPrefix_stmt(pref);
-
-        pref = mock(Prefix_stmtContext.class);
-        doReturn(1).when(pref).getChildCount();
-        mockName(pref, "unique1");
-        mockModuleParent(2, pref, "module1");
-
-        try {
-            valid.enterPrefix_stmt(pref);
-        } catch (Exception e) {
-            return;
-        }
-
-        fail("Validation Exception should have occured");
-    }
-
-    @Test
-    public void testNamespace() {
-        Namespace_stmtContext namespace = mock(Namespace_stmtContext.class);
-        doReturn(1).when(namespace).getChildCount();
-        mockName(namespace, "http://test.parsing.uri.com");
-        mockModuleParent(2, namespace, "module1");
-        valid.enterNamespace_stmt(namespace);
-
-        namespace = mock(Namespace_stmtContext.class);
-        doReturn(1).when(namespace).getChildCount();
-        mockName(namespace, "invalid uri");
-        mockModuleParent(2, namespace, "module1");
-        try {
-            valid.enterNamespace_stmt(namespace);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Namespace:invalid uri in module:module1 cannot be parsed as URI"));
-            return;
-        }
-
-        fail("Validation Exception should have occured");
-    }
-
-    @Test
-    public void testImports() {
-        Import_stmtContext impor = mockImport("unique1", "p1");
-        mockModuleParent(2, impor, "module1");
-        valid.enterImport_stmt(impor);
-
-        impor = mockImport("unique1", "p2");
-        mockModuleParent(2, impor, "module1");
-        mockName(impor, "unique1");
-
-        try {
-            valid.enterImport_stmt(impor);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Module:unique1 imported twice in (sub)module:module1"));
-            return;
-        }
-
-        fail("Validation Exception should have occured");
-    }
-
-    @Test
-    public void testIncludes() {
-        Include_stmtContext impor = mockInclude("unique1");
-        mockModuleParent(2, impor, "module1");
-        valid.enterInclude_stmt(impor);
-
-        impor = mockInclude("unique1");
-        mockModuleParent(2, impor, "module1");
-        mockName(impor, "unique1");
-
-        try {
-            valid.enterInclude_stmt(impor);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Submodule:unique1 included twice in (sub)module:module1"));
-            return;
-        }
-
-        fail("Validation Exception should have occured");
-    }
-
-    private Import_stmtContext mockImport(String name, String prefixName) {
-        Import_stmtContext impor = mock(Import_stmtContext.class);
-        doReturn(3).when(impor).getChildCount();
-        Prefix_stmtContext prefix = mock(Prefix_stmtContext.class);
-        mockName(prefix, prefixName);
-        doReturn(prefix).when(impor).getChild(1);
-        Revision_date_stmtContext revDate = mock(Revision_date_stmtContext.class);
-        mockName(revDate, getFormattedDate());
-        doReturn(revDate).when(impor).getChild(2);
-        mockName(impor, name);
-        return impor;
-    }
-
-    private Include_stmtContext mockInclude(String name) {
-        Include_stmtContext impor = mock(Include_stmtContext.class);
-        doReturn(2).when(impor).getChildCount();
-        Revision_date_stmtContext revDate = mock(Revision_date_stmtContext.class);
-        mockName(revDate, getFormattedDate());
-        doReturn(revDate).when(impor).getChild(1);
-        mockName(impor, name);
-        return impor;
-    }
-
-    @Test(expected = YangValidationException.class)
-    public void testInvalidYangVersion() {
-
-        Yang_version_stmtContext yangVersion = mock(Yang_version_stmtContext.class);
-        doReturn(1).when(yangVersion).getChildCount();
-        mockName(yangVersion, "55Unsup");
-
-        mockModuleParent(2, yangVersion, "module1");
-
-        try {
-            valid.enterYang_version_stmt(yangVersion);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Unsupported yang version:55Unsup, in (sub)module:module1, supported version:"
-                            + YangModelValidationListener.SUPPORTED_YANG_VERSION));
-            throw e;
-        }
-    }
-
-    private void mockModuleParent(int moduleChildren, ParseTree child,
-            String moduleName) {
-        ctx = mock(Module_stmtContext.class);
-        doReturn(moduleChildren).when(ctx).getChildCount();
-        mockName(ctx, moduleName);
-        doReturn(child).when(ctx).getChild(1);
-        doReturn(ctx).when(child).getParent();
-    }
-
-    static Revision_stmtsContext mockRevisionsParent(int moduleChildren,
-            Revision_stmtContext mockedRev) {
-        Revision_stmtsContext revs = mock(Revision_stmtsContext.class);
-        doReturn(moduleChildren).when(revs).getChildCount();
-        doReturn(mockedRev).when(revs).getChild(1);
-        doReturn(revs).when(mockedRev).getParent();
-        return revs;
-    }
-
-    @Test
-    public void testValidYangVersion() {
-
-        Yang_version_stmtContext ctx = mock(Yang_version_stmtContext.class);
-        doReturn(1).when(ctx).getChildCount();
-        mockName(ctx, "1");
-
-        valid.enterYang_version_stmt(ctx);
-    }
-
-    @Test
-    public void testIdentifierMatching() {
-        YangModelValidationListener.checkIdentifier("_ok98-.87.-.8...88-asdAD",
-                null);
-        YangModelValidationListener.checkIdentifier("AA.bcd", null);
-        YangModelValidationListener.checkIdentifier("a", null);
-
-        int thrown = 0;
-
-        try {
-            YangModelValidationListener.checkIdentifier("9aa", null);
-        } catch (YangValidationException e) {
-            thrown++;
-        }
-        try {
-            YangModelValidationListener.checkIdentifier("-", null);
-        } catch (YangValidationException e) {
-            thrown++;
-        }
-        try {
-            YangModelValidationListener.checkIdentifier(".", null);
-        } catch (YangValidationException e) {
-            thrown++;
-        }
-
-        assertThat(thrown, is(3));
-    }
-
-    static void mockName(ParseTree mockedRev, String name) {
-        StringContext nameCtx = mock(StringContext.class);
-        ParseTree internalName = mock(ParseTree.class);
-        doReturn(name).when(internalName).getText();
-        doReturn(internalName).when(nameCtx).getChild(0);
-        doReturn(nameCtx).when(mockedRev).getChild(0);
-    }
-}
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_SubModule.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/parser/impl/YangModelValidationListenerTest_SubModule.java
deleted file mode 100644 (file)
index 056e869..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-package org.opendaylight.controller.yang.model.parser.impl;
-
-import static org.junit.Assert.*;
-import static org.junit.matchers.JUnitMatchers.*;
-import static org.mockito.Mockito.*;
-
-import org.antlr.v4.runtime.tree.ParseTree;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtsContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext;
-import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext;
-
-public class YangModelValidationListenerTest_SubModule {
-
-    private YangModelValidationListener valid;
-    private Submodule_stmtContext ctx;
-
-    @Before
-    public void setUp() {
-        valid = new YangModelValidationListener();
-    }
-
-    @Test(expected = YangValidationException.class)
-    public void testNoRevision() {
-
-        Submodule_stmtContext ctx = mock(Submodule_stmtContext.class);
-        doReturn(1).when(ctx).getChildCount();
-        YangModelValidationListenerTest_Module.mockName(ctx, "submodule1");
-
-        try {
-            valid.enterSubmodule_stmt(ctx);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Missing revision statements in submodule:submodule1"));
-            throw e;
-        }
-    }
-
-    @Test(expected = YangValidationException.class)
-    public void testNoHeaderStmts() {
-        mockSubmoduleWithRevision(2, "1999-4-5", "submodule");
-
-        try {
-            valid.enterSubmodule_stmt(ctx);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Missing header statements in submodule:submodule"));
-            throw e;
-        }
-    }
-
-    @Test(expected = YangValidationException.class)
-    public void testNoBelongsTo() {
-        Submodule_header_stmtsContext header = mock(Submodule_header_stmtsContext.class);
-        mockSubmoduleParent(2, header, "submodule");
-
-        try {
-            valid.enterSubmodule_header_stmts(header);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Missing belongs-to statement in submodule:submodule"));
-            throw e;
-        }
-    }
-
-    @Test(expected = YangValidationException.class)
-    public void testBelongsToNoPrefix() {
-        Belongs_to_stmtContext belongsTo = mock(Belongs_to_stmtContext.class);
-        doReturn(1).when(belongsTo).getChildCount();
-        YangModelValidationListenerTest_Module.mockName(belongsTo,
-                "supermodule");
-
-        mockSubmoduleParent(2, belongsTo, "submodule");
-
-        try {
-            valid.enterBelongs_to_stmt(belongsTo);
-        } catch (YangValidationException e) {
-            assertThat(
-                    e.getMessage(),
-                    containsString("Missing prefix statement in belongs-to:supermodule, in (sub)module:submodule"));
-            throw e;
-        }
-    }
-
-    @Test
-    public void testBelongsTo() {
-        Belongs_to_stmtContext belongsTo = mock(Belongs_to_stmtContext.class);
-        doReturn(2).when(belongsTo).getChildCount();
-        YangModelValidationListenerTest_Module.mockName(belongsTo,
-                "supermodule");
-
-        Prefix_stmtContext prefix = mock(Prefix_stmtContext.class);
-        doReturn(prefix).when(belongsTo).getChild(1);
-        doReturn(belongsTo).when(prefix).getParent();
-
-        mockSubmoduleParent(2, belongsTo, "submodule");
-        valid.enterBelongs_to_stmt(belongsTo);
-
-    }
-
-    private Revision_stmtContext mockSubmoduleWithRevision(int moduleChildren,
-            String date, String nameOfSubmodule) {
-        Revision_stmtContext mockedRev = mock(Revision_stmtContext.class);
-        doReturn(1).when(mockedRev).getChildCount();
-        YangModelValidationListenerTest_Module.mockName(mockedRev, date);
-
-        Revision_stmtsContext revs = YangModelValidationListenerTest_Module
-                .mockRevisionsParent(2, mockedRev);
-
-        mockSubmoduleParent(moduleChildren, revs, nameOfSubmodule);
-        return mockedRev;
-    }
-
-    private void mockSubmoduleParent(int moduleChildren, ParseTree child,
-            String moduleName) {
-        ctx = mock(Submodule_stmtContext.class);
-        doReturn(moduleChildren).when(ctx).getChildCount();
-        YangModelValidationListenerTest_Module.mockName(ctx, moduleName);
-        doReturn(child).when(ctx).getChild(1);
-        doReturn(ctx).when(child).getParent();
-    }
-}
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationListTest.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationListTest.java
new file mode 100644 (file)
index 0000000..4e05f48
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.yang.model.validator;
+
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Default_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Key_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Leaf_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.List_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Mandatory_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Ordered_by_argContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Type_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Unique_stmtContext;
+import org.opendaylight.controller.yang.model.parser.util.YangValidationException;
+
+public class YangModelValidationListTest {
+
+    private YangModelBasicValidationListener valid;
+
+    @Before
+    public void setUp() {
+        valid = new YangModelBasicValidationListener();
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testKeyValidationDuplicates() {
+
+        List_stmtContext list = YangModelValidationTest.mockStatement(
+                List_stmtContext.class, "list");
+        Key_stmtContext key = YangModelValidationTest.mockStatement(
+                Key_stmtContext.class, "leaf1 leaf2 leaf1 leaf1");
+        YangModelValidationTest.addChild(list, key);
+
+        try {
+            valid.enterKey_stmt(key);
+        } catch (YangValidationException e) {
+            assertThat(e.getMessage(),
+                    containsString("contains duplicates:[leaf1]"));
+            throw e;
+        }
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testUniqueValidationDuplicates() {
+        List_stmtContext list = YangModelValidationTest.mockStatement(
+                List_stmtContext.class, "list");
+        Unique_stmtContext unique = YangModelValidationTest.mockStatement(
+                Unique_stmtContext.class, "leaf1/a leaf2/n leaf1/a leaf1");
+        YangModelValidationTest.addChild(list, unique);
+
+        try {
+            valid.enterUnique_stmt(unique);
+        } catch (YangValidationException e) {
+            assertThat(e.getMessage(),
+                    containsString("contains duplicates:[leaf1/a]"));
+            throw e;
+        }
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testOrderBy() {
+        Ordered_by_argContext ctx = YangModelValidationTest.mockStatement(
+                Ordered_by_argContext.class, "unknown");
+
+        try {
+            valid.enterOrdered_by_arg(ctx);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Ordered-by:unknown, illegal value for Ordered-by statement, only permitted:"));
+            throw e;
+        }
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testLeaf() {
+        Leaf_stmtContext ctx = YangModelValidationTest.mockStatement(
+                Leaf_stmtContext.class, "leaf1");
+        Default_stmtContext def = YangModelValidationTest.mockStatement(
+                Default_stmtContext.class, "default");
+        YangModelValidationTest.addChild(ctx, def);
+        Type_stmtContext typ = YangModelValidationTest.mockStatement(
+                Type_stmtContext.class, "type");
+        YangModelValidationTest.addChild(ctx, def);
+        YangModelValidationTest.addChild(ctx, typ);
+
+        Mandatory_stmtContext mand = YangModelValidationTest.mockStatement(
+                Mandatory_stmtContext.class, null);
+        YangModelValidationTest.addChild(ctx, mand);
+
+        try {
+            valid.enterLeaf_stmt(ctx);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Both Mandatory and Default statement present"));
+            throw e;
+        }
+    }
+
+}
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationModuleTest.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationModuleTest.java
new file mode 100644 (file)
index 0000000..2bdd3e5
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.yang.model.validator;
+
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_header_stmtsContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_stmtsContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Yang_version_stmtContext;
+import org.opendaylight.controller.yang.model.parser.util.YangValidationException;
+
+public class YangModelValidationModuleTest {
+
+    private YangModelBasicValidationListener valid;
+
+    @Before
+    public void setUp() {
+        valid = new YangModelBasicValidationListener();
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testRevisionInvalidDateFormat() {
+        Revision_stmtContext mockedRev = mockModuleWithRevision("badFormat",
+                "module1");
+
+        try {
+            valid.enterRevision_stmt(mockedRev);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Revision:badFormat, invalid date format expected date format is:"));
+            throw e;
+        }
+    }
+
+    @Test
+    public void testRevisionValidDateFormat() {
+        Revision_stmtContext mockedRev = mockModuleWithRevision(
+                YangModelValidationTest.getFormattedDate(), "module1");
+
+        valid.enterRevision_stmt(mockedRev);
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testNoHeaderStmts() {
+        Revision_stmtContext rev = mockModuleWithRevision("1999-4-5", "module1");
+
+        try {
+            valid.enterModule_stmt((Module_stmtContext) rev.getParent()
+                    .getParent());
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Missing Module-header statement in Module:module1"));
+            throw e;
+        }
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testMultipleModulesPerSession() {
+        Module_stmtContext module1 = (Module_stmtContext) mockModuleWithRevision(
+                "1999-09-10", "m1").getParent().getParent();
+        YangModelValidationTest.addChild(module1, YangModelValidationTest
+                .mockStatement(Namespace_stmtContext.class, ""));
+
+        Module_stmtContext module2 = (Module_stmtContext) mockModuleWithRevision(
+                "1999-09-10", "m2").getParent().getParent();
+        YangModelValidationTest.addChild(module1, YangModelValidationTest
+                .mockStatement(Namespace_stmtContext.class, ""));
+        valid.enterModule_stmt(module1);
+
+        try {
+            valid.enterModule_stmt(module2);
+        } catch (YangValidationException e) {
+            assertThat(e.getMessage(),
+                    containsString("Multiple (sub)modules per file"));
+            throw e;
+        }
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testNoNamespace() {
+        Module_header_stmtsContext header = YangModelValidationTest
+                .mockStatement(Module_header_stmtsContext.class, null);
+        Module_stmtContext mod = YangModelValidationTest.mockStatement(
+                Module_stmtContext.class, "module1");
+        YangModelValidationTest.addChild(mod, header);
+
+        try {
+            valid.enterModule_header_stmts(header);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Missing Namespace statement in Module-header:"));
+            throw e;
+        }
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testNoPrefix() {
+        Module_header_stmtsContext header = YangModelValidationTest
+                .mockStatement(Module_header_stmtsContext.class, null);
+        Namespace_stmtContext nmspc = YangModelValidationTest.mockStatement(
+                Namespace_stmtContext.class, "http://test");
+        Module_stmtContext mod = YangModelValidationTest.mockStatement(
+                Module_stmtContext.class, "module1");
+        YangModelValidationTest.addChild(mod, header);
+        YangModelValidationTest.addChild(header, nmspc);
+
+        try {
+            valid.enterModule_header_stmts(header);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Missing Prefix statement in Module-header:"));
+            throw e;
+        }
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testInvalidYangVersion() {
+
+        Yang_version_stmtContext yangVersion = YangModelValidationTest
+                .mockStatement(Yang_version_stmtContext.class, "55Unsup");
+
+        Module_stmtContext mod = YangModelValidationTest.mockStatement(
+                Module_stmtContext.class, "module1");
+        YangModelValidationTest.addChild(mod, yangVersion);
+
+        try {
+            valid.enterYang_version_stmt(yangVersion);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Unsupported yang version:55Unsup, supported version:"
+                            + BasicValidations.SUPPORTED_YANG_VERSION));
+            throw e;
+        }
+    }
+
+    @Test
+    public void testValidYangVersion() {
+
+        Yang_version_stmtContext ctx = mock(Yang_version_stmtContext.class);
+        doReturn(1).when(ctx).getChildCount();
+        YangModelValidationTest.mockName(ctx, "1");
+
+        valid.enterYang_version_stmt(ctx);
+    }
+
+    private static Revision_stmtContext mockModuleWithRevision(String date,
+            String moduleName) {
+        Revision_stmtContext mockedRev = YangModelValidationTest.mockStatement(
+                Revision_stmtContext.class, date);
+        Revision_stmtsContext revs = YangModelValidationTest.mockStatement(
+                Revision_stmtsContext.class, null);
+        Module_stmtContext mod = YangModelValidationTest.mockStatement(
+                Module_stmtContext.class, moduleName);
+
+        YangModelValidationTest.addChild(revs, mockedRev);
+        YangModelValidationTest.addChild(mod, revs);
+        return mockedRev;
+    }
+}
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationSubModuleTest.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationSubModuleTest.java
new file mode 100644 (file)
index 0000000..fe4f0a2
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.yang.model.validator;
+
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.*;
+import static org.mockito.Mockito.*;
+
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_header_stmtsContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Submodule_stmtContext;
+import org.opendaylight.controller.yang.model.parser.util.YangValidationException;
+
+public class YangModelValidationSubModuleTest {
+
+    private YangModelBasicValidationListener valid;
+
+    @Before
+    public void setUp() {
+        valid = new YangModelBasicValidationListener();
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testNoRevision() {
+
+        Submodule_stmtContext ctx = YangModelValidationTest.mockStatement(
+                Submodule_stmtContext.class, "submodule1");
+
+        try {
+            valid.enterSubmodule_stmt(ctx);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Missing Submodule-header statement in Submodule:submodule"));
+            throw e;
+        }
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testNoBelongsTo() {
+        Submodule_header_stmtsContext header = mock(Submodule_header_stmtsContext.class);
+        mockSubmoduleParent(header, "submodule");
+
+        try {
+            valid.enterSubmodule_header_stmts(header);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Missing Belongs-to statement in Submodule-header:"));
+            throw e;
+        }
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testBelongsToNoPrefix() {
+        Belongs_to_stmtContext belongsTo = YangModelValidationTest
+                .mockStatement(Belongs_to_stmtContext.class, "supermodule");
+
+        mockSubmoduleParent(belongsTo, "submodule");
+
+        try {
+            valid.enterBelongs_to_stmt(belongsTo);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Missing Prefix statement in Belongs-to:supermodule"));
+            throw e;
+        }
+    }
+
+    private Submodule_stmtContext mockSubmoduleParent(ParseTree child,
+            String moduleName) {
+        Submodule_stmtContext ctx = YangModelValidationTest.mockStatement(
+                Submodule_stmtContext.class, moduleName);
+        YangModelValidationTest.addChild(ctx, child);
+        return ctx;
+    }
+}
diff --git a/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationTest.java b/opendaylight/sal/yang-prototype/code-generator/yang-model-parser-impl/src/test/java/org/opendaylight/controller/yang/model/validator/YangModelValidationTest.java
new file mode 100644 (file)
index 0000000..ffcadf6
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.yang.model.validator;
+
+import static org.hamcrest.core.Is.*;
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Augment_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviate_add_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviate_delete_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Deviation_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Import_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Include_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Module_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Namespace_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Prefix_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.Status_argContext;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser.StringContext;
+import org.opendaylight.controller.yang.model.parser.impl.YangModelParserListenerImpl;
+import org.opendaylight.controller.yang.model.parser.util.YangValidationException;
+
+import com.google.common.collect.Sets;
+
+public class YangModelValidationTest {
+
+    private YangModelBasicValidationListener valid;
+
+    @Before
+    public void setUp() {
+
+        valid = new YangModelBasicValidationListener();
+    }
+
+    @Test
+    public void testPrefixes() {
+        Prefix_stmtContext pref = mockStatement(Prefix_stmtContext.class,
+                "unique1");
+        Module_stmtContext module = mockStatement(Module_stmtContext.class,
+                "module1");
+        addChild(module, pref);
+
+        valid.enterPrefix_stmt(pref);
+
+        pref = mockStatement(Prefix_stmtContext.class, "unique1");
+        module = mockStatement(Module_stmtContext.class, "module1");
+        addChild(module, pref);
+
+        try {
+            valid.enterPrefix_stmt(pref);
+        } catch (Exception e) {
+            return;
+        }
+
+        fail("Validation Exception should have occured");
+    }
+
+    @Test
+    public void testNamespace() {
+
+        Namespace_stmtContext namespace = mockStatement(
+                Namespace_stmtContext.class, "http://test.parsing.uri.com");
+        Module_stmtContext module = mockStatement(Module_stmtContext.class,
+                "module1");
+        addChild(module, namespace);
+
+        valid.enterNamespace_stmt(namespace);
+
+        namespace = mockStatement(Namespace_stmtContext.class, "invalid uri");
+        module = mockStatement(Module_stmtContext.class, "module1");
+        addChild(module, namespace);
+
+        try {
+            valid.enterNamespace_stmt(namespace);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Namespace:invalid uri cannot be parsed as URI"));
+            return;
+        }
+
+        fail("Validation Exception should have occured");
+    }
+
+    @Test
+    public void testImports() {
+        Import_stmtContext impor = mockImport("unique1", "p1");
+        Module_stmtContext mod = mockStatement(Module_stmtContext.class,
+                "module1");
+        addChild(mod, impor);
+
+        valid.enterImport_stmt(impor);
+
+        impor = mockImport("unique1", "p2");
+        mod = mockStatement(Module_stmtContext.class, "module1");
+        addChild(mod, impor);
+
+        try {
+            valid.enterImport_stmt(impor);
+        } catch (YangValidationException e) {
+            assertThat(e.getMessage(),
+                    containsString("Import:unique1 not unique"));
+            return;
+        }
+
+        fail("Validation Exception should have occured");
+    }
+
+    @Test
+    public void testIncludes() {
+        Include_stmtContext incl = mockInclude("unique1");
+        Module_stmtContext mod = mockStatement(Module_stmtContext.class,
+                "module1");
+        addChild(mod, incl);
+        valid.enterInclude_stmt(incl);
+
+        incl = mockInclude("unique1");
+        mod = mockStatement(Module_stmtContext.class, "module1");
+        addChild(mod, incl);
+
+        try {
+            valid.enterInclude_stmt(incl);
+        } catch (YangValidationException e) {
+            assertThat(e.getMessage(),
+                    containsString("Include:unique1 not unique in (sub)module"));
+            return;
+        }
+
+        fail("Validation Exception should have occured");
+    }
+
+    @Test
+    public void testIdentifierMatching() {
+        List<String> ids = new ArrayList<String>();
+        // valid
+        ids.add("_ok98-.87.-.8...88-asdAD");
+        ids.add("AA.bcd");
+        ids.add("a");
+        // invalid
+        ids.add("9aa");
+        ids.add("-");
+        ids.add(".");
+
+        int thrown = 0;
+        for (String id : ids) {
+            try {
+                BasicValidations.checkIdentifierInternal(
+                        mock(Module_stmtContext.class), id);
+            } catch (YangValidationException e) {
+                thrown++;
+            }
+        }
+
+        assertThat(thrown, is(3));
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testAugument() {
+        Augment_stmtContext augument = mockStatement(Augment_stmtContext.class,
+                "/a:*abc/a:augument1");
+        Module_stmtContext mod1 = mockStatement(Module_stmtContext.class,
+                "mod1");
+        addChild(mod1, augument);
+
+        try {
+            valid.enterAugment_stmt(augument);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Schema node id:/a:*abc/a:augument1 not in required format, details:Prefixed id:a:*abc not in required format"));
+            throw e;
+        }
+    }
+
+    @Test
+    public void testDeviate() {
+        Deviation_stmtContext ctx = mockStatement(Deviation_stmtContext.class,
+                "deviations");
+        Deviate_add_stmtContext add = mockStatement(
+                Deviate_add_stmtContext.class, "add");
+        Deviate_delete_stmtContext del = mockStatement(
+                Deviate_delete_stmtContext.class, "delete");
+
+        addChild(ctx, add);
+        addChild(ctx, del);
+
+        valid.enterDeviation_stmt(ctx);
+
+        HashSet<Class<? extends ParseTree>> types = Sets.newHashSet();
+        types.add(Deviate_add_stmtContext.class);
+        types.add(Deviate_delete_stmtContext.class);
+
+        int count = ValidationUtil.countPresentChildrenOfType(ctx, types);
+        assertThat(count, is(2));
+    }
+
+    @Test(expected = YangValidationException.class)
+    public void testStatus() throws Exception {
+        Status_argContext status = mockStatement(Status_argContext.class,
+                "unknown");
+        try {
+            valid.enterStatus_arg(status);
+        } catch (YangValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("illegal value for Status statement, only permitted:"));
+            throw e;
+        }
+    }
+
+    private Import_stmtContext mockImport(String name, String prefixName) {
+        Import_stmtContext impor = mockStatement(Import_stmtContext.class, name);
+
+        Prefix_stmtContext prefix = mockStatement(Prefix_stmtContext.class,
+                prefixName);
+        Revision_date_stmtContext revDate = mockStatement(
+                Revision_date_stmtContext.class, getFormattedDate());
+
+        addChild(impor, prefix);
+        addChild(impor, revDate);
+        return impor;
+    }
+
+    static String getFormattedDate() {
+        return YangModelParserListenerImpl.simpleDateFormat.format(new Date());
+    }
+
+    private Include_stmtContext mockInclude(String name) {
+        Include_stmtContext incl = mockStatement(Include_stmtContext.class,
+                name);
+
+        Revision_date_stmtContext revDate = mockStatement(
+                Revision_date_stmtContext.class, getFormattedDate());
+
+        addChild(incl, revDate);
+        return incl;
+    }
+
+    static void mockName(ParseTree stmt, String name) {
+        StringContext nameCtx = mock(StringContext.class);
+        ParseTree internalName = mock(ParseTree.class);
+        doReturn(1).when(stmt).getChildCount();
+        doReturn(name).when(internalName).getText();
+        doReturn(internalName).when(nameCtx).getChild(0);
+        doReturn(nameCtx).when(stmt).getChild(0);
+    }
+
+    static <T extends ParseTree> T mockStatement(Class<T> stmtType, String name) {
+        T stmt = stmtType.cast(mock(stmtType));
+
+        doReturn(0).when(stmt).getChildCount();
+
+        if (name != null)
+            mockName(stmt, name);
+        return stmt;
+    }
+
+    static void addChild(ParseTree parent, ParseTree child) {
+        int childCount = parent.getChildCount() + 1;
+        doReturn(childCount).when(parent).getChildCount();
+        doReturn(child).when(parent).getChild(childCount - 1);
+        doReturn(parent).when(child).getParent();
+    }
+
+}