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>
<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
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;
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++) {
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 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>();
+++ /dev/null
-/*
- * 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;
- }
-}
* 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
- * {@link YangModelValidationListener}
+ * {@link YangModelBasicValidationListener}
*/
-public class YangValidationException extends RuntimeException {
+public final class YangValidationException extends RuntimeException {
private static final long serialVersionUID = 7414330400390825381L;
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+
+}
+++ /dev/null
-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);
- }
-}
+++ /dev/null
-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();
- }
-}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}