2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/eplv10.html
8 package org.opendaylight.yangtools.yang.validator;
10 import java.text.DateFormat;
11 import java.text.ParseException;
12 import java.util.Date;
13 import java.util.List;
15 import java.util.regex.Matcher;
16 import java.util.regex.Pattern;
18 import org.antlr.v4.runtime.ParserRuleContext;
19 import org.antlr.v4.runtime.tree.ParseTree;
20 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yang_version_stmtContext;
21 import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
22 import org.opendaylight.yangtools.yang.parser.util.YangValidationException;
24 import com.google.common.collect.Sets;
27 * Reusable checks of basic constraints on yang statements
29 final class BasicValidations {
31 static final String SUPPORTED_YANG_VERSION = "1";
34 * It isn't desirable to create instance of this class.
36 private BasicValidations() {
39 static void checkNotPresentBoth(ParseTree parent, Class<? extends ParseTree> childType1,
40 Class<? extends ParseTree> childType2) {
41 if (BasicValidations.checkPresentChildOfTypeSafe(parent, childType1, true)
42 && BasicValidations.checkPresentChildOfTypeSafe(parent, childType2, false)) {
43 ValidationUtil.ex(ValidationUtil.f("(In (sub)module:%s) Both %s and %s statement present in %s:%s",
44 ValidationUtil.getRootParentName(parent), ValidationUtil.getSimpleStatementName(childType1),
45 ValidationUtil.getSimpleStatementName(childType2),
46 ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent)));
50 static void checkOnlyPermittedValues(ParseTree ctx, Set<String> permittedValues) {
51 String mandatory = ValidationUtil.getName(ctx);
52 String rootParentName = ValidationUtil.getRootParentName(ctx);
54 if (!permittedValues.contains(mandatory)) {
55 ValidationUtil.ex(ValidationUtil.f(
56 "(In (sub)module:%s) %s:%s, illegal value for %s statement, only permitted:%s", rootParentName,
57 ValidationUtil.getSimpleStatementName(ctx.getClass()), mandatory,
58 ValidationUtil.getSimpleStatementName(ctx.getClass()), permittedValues));
62 static void checkUniquenessInNamespace(ParseTree stmt, Set<String> uniques) {
63 String name = ValidationUtil.getName(stmt);
64 String rootParentName = ValidationUtil.getRootParentName(stmt);
66 if (uniques.contains(name)) {
67 ValidationUtil.ex(ValidationUtil.f("(In (sub)module:%s) %s:%s not unique in (sub)module", rootParentName,
68 ValidationUtil.getSimpleStatementName(stmt.getClass()), name));
74 * Check if only one module or submodule is present in session(one yang
77 static void checkOnlyOneModulePresent(String moduleName, String globalId) {
78 if (globalId != null) {
79 ValidationUtil.ex(ValidationUtil.f("Multiple (sub)modules per file"));
83 static void checkPresentYangVersion(ParseTree ctx, String moduleName) {
84 if (!checkPresentChildOfTypeSafe(ctx, Yang_version_stmtContext.class, true)) {
85 ValidationUtil.ex(ValidationUtil.f(
86 "Yang version statement not present in module:%s, Validating as yang version:%s", moduleName,
87 SUPPORTED_YANG_VERSION));
91 static void checkDateFormat(ParseTree stmt, DateFormat format) {
93 format.parse(ValidationUtil.getName(stmt));
94 } catch (ParseException e) {
95 String exceptionMessage = ValidationUtil.f(
96 "(In (sub)module:%s) %s:%s, invalid date format expected date format is:%s",
97 ValidationUtil.getRootParentName(stmt), ValidationUtil.getSimpleStatementName(stmt.getClass()),
98 ValidationUtil.getName(stmt), YangParserListenerImpl.simpleDateFormat.format(new Date()));
99 ValidationUtil.ex(exceptionMessage);
103 static private Pattern identifierPattern = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_.-]*");
105 static void checkIdentifier(ParseTree statement) {
106 checkIdentifierInternal(statement, ValidationUtil.getName(statement));
109 static void checkIdentifierInternal(ParseTree statement, String name) {
110 if (!identifierPattern.matcher(name).matches()) {
112 String message = ValidationUtil.f("%s statement identifier:%s is not in required format:%s",
113 ValidationUtil.getSimpleStatementName(statement.getClass()), name, identifierPattern.toString());
114 String parent = ValidationUtil.getRootParentName(statement);
115 message = parent.equals(name) ? message : ValidationUtil.f("(In (sub)module:%s) %s", parent, message);
117 if (statement instanceof ParserRuleContext) {
118 message = "Error on line " + ((ParserRuleContext) statement).getStart().getLine() + ": " + message;
121 ValidationUtil.ex(message);
125 static private Pattern prefixedIdentifierPattern = Pattern.compile("(.+):(.+)");
127 static void checkPrefixedIdentifier(ParseTree statement) {
128 checkPrefixedIdentifierInternal(statement, ValidationUtil.getName(statement));
131 private static void checkPrefixedIdentifierInternal(ParseTree statement, String id) {
132 Matcher matcher = prefixedIdentifierPattern.matcher(id);
134 if (matcher.matches()) {
137 checkIdentifierInternal(statement, matcher.group(1));
139 checkIdentifierInternal(statement, matcher.group(2));
140 } catch (YangValidationException e) {
141 ValidationUtil.ex(ValidationUtil.f("Prefixed id:%s not in required format, details:%s", id,
145 checkIdentifierInternal(statement, id);
149 static void checkSchemaNodeIdentifier(ParseTree statement) {
150 String id = ValidationUtil.getName(statement);
153 for (String oneOfId : id.split("/")) {
154 if (oneOfId.isEmpty()) {
157 checkPrefixedIdentifierInternal(statement, oneOfId);
159 } catch (YangValidationException e) {
160 ValidationUtil.ex(ValidationUtil.f("Schema node id:%s not in required format, details:%s", id,
165 private interface MessageProvider {
169 static void checkPresentChildOfTypeInternal(ParseTree parent, Set<Class<? extends ParseTree>> expectedChildType,
170 MessageProvider message, boolean atMostOne) {
171 if (!checkPresentChildOfTypeSafe(parent, expectedChildType, atMostOne)) {
172 String str = atMostOne ? "(Expected exactly one statement) " + message.getMessage() : message.getMessage();
173 ValidationUtil.ex(str);
177 static void checkPresentChildOfType(final ParseTree parent, final Class<? extends ParseTree> expectedChildType,
180 // Construct message in checkPresentChildOfTypeInternal only if
181 // validaiton fails, not in advance
182 MessageProvider message = new MessageProvider() {
185 public String getMessage() {
186 String message = ValidationUtil.f("Missing %s statement in %s:%s",
187 ValidationUtil.getSimpleStatementName(expectedChildType),
188 ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent));
190 String root = ValidationUtil.getRootParentName(parent);
191 message = parent.equals(ValidationUtil.getRootParentName(parent)) ? message : ValidationUtil.f(
192 "(In (sub)module:%s) %s", root, message);
197 Set<Class<? extends ParseTree>> expectedChildTypeSet = Sets.newHashSet();
198 expectedChildTypeSet.add(expectedChildType);
200 checkPresentChildOfTypeInternal(parent, expectedChildTypeSet, message, atMostOne);
205 * Implementation of interface <code>MessageProvider</code> for method
206 * {@link BasicValidations#checkPresentChildOfTypeSafe(ParseTree, Set, boolean)
207 * checkPresentChildOfTypeSafe(ParseTree, Set, boolean) * }
209 private static class MessageProviderForSetOfChildTypes implements MessageProvider {
211 private Set<Class<? extends ParseTree>> expectedChildTypes;
212 private ParseTree parent;
214 public MessageProviderForSetOfChildTypes(Set<Class<? extends ParseTree>> expectedChildTypes, ParseTree parent) {
215 this.expectedChildTypes = expectedChildTypes;
216 this.parent = parent;
220 public String getMessage() {
221 StringBuilder childTypes = new StringBuilder();
222 String orStr = " OR ";
223 for (Class<? extends ParseTree> type : expectedChildTypes) {
224 childTypes.append(ValidationUtil.getSimpleStatementName(type));
225 childTypes.append(orStr);
227 String message = ValidationUtil.f("Missing %s statement in %s:%s", childTypes.toString(),
228 ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent));
230 String root = ValidationUtil.getRootParentName(parent);
231 message = parent.equals(ValidationUtil.getRootParentName(parent)) ? message : ValidationUtil.f(
232 "(In (sub)module:%s) %s", root, message);
237 static void checkPresentChildOfTypes(final ParseTree parent,
238 final Set<Class<? extends ParseTree>> expectedChildTypes, boolean atMostOne) {
240 // Construct message in checkPresentChildOfTypeInternal only if
241 // validaiton fails, not in advance
242 MessageProvider message = new MessageProviderForSetOfChildTypes(expectedChildTypes, parent);
243 checkPresentChildOfTypeInternal(parent, expectedChildTypes, message, atMostOne);
246 static boolean checkPresentChildOfTypeSafe(ParseTree parent, Set<Class<? extends ParseTree>> expectedChildType,
249 int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType);
251 return atMostOne ? foundChildrenOfType == 1 ? true : false : foundChildrenOfType != 0 ? true : false;
254 static boolean checkPresentChildOfTypeSafe(ParseTree parent, Class<? extends ParseTree> expectedChildType,
257 int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType);
259 return atMostOne ? foundChildrenOfType == 1 ? true : false : foundChildrenOfType != 0 ? true : false;
262 static List<String> getAndCheckUniqueKeys(ParseTree ctx) {
263 String key = ValidationUtil.getName(ctx);
264 ParseTree parent = ctx.getParent();
265 String rootParentName = ValidationUtil.getRootParentName(ctx);
267 List<String> keyList = ValidationUtil.listKeysFromId(key);
268 Set<String> duplicates = ValidationUtil.getDuplicates(keyList);
270 if (duplicates.size() != 0) {
271 ValidationUtil.ex(ValidationUtil.f("(In (sub)module:%s) %s:%s, %s:%s contains duplicates:%s",
272 rootParentName, ValidationUtil.getSimpleStatementName(parent.getClass()),
273 ValidationUtil.getName(parent), ValidationUtil.getSimpleStatementName(ctx.getClass()), key,