Merge "BUG-582: pre-cache Pattern instances"
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / validator / BasicValidations.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.validator;
9
10 import java.text.ParseException;
11 import java.util.Date;
12 import java.util.List;
13 import java.util.Set;
14 import java.util.regex.Matcher;
15 import java.util.regex.Pattern;
16
17 import org.antlr.v4.runtime.ParserRuleContext;
18 import org.antlr.v4.runtime.tree.ParseTree;
19 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yang_version_stmtContext;
20 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
21 import org.opendaylight.yangtools.yang.parser.util.YangValidationException;
22
23 import com.google.common.collect.Sets;
24
25 /**
26  * Reusable checks of basic constraints on yang statements
27  */
28 final class BasicValidations {
29
30     static final String SUPPORTED_YANG_VERSION = "1";
31
32     /**
33      * It isn't desirable to create instance of this class.
34      */
35     private BasicValidations() {
36     }
37
38     static void checkNotPresentBoth(final ParseTree parent, final Class<? extends ParseTree> childType1,
39             final Class<? extends ParseTree> childType2) {
40         if (BasicValidations.checkPresentChildOfTypeSafe(parent, childType1, true)
41                 && BasicValidations.checkPresentChildOfTypeSafe(parent, childType2, false)) {
42             ValidationUtil.ex(ValidationUtil.f("(In (sub)module:%s) Both %s and %s statement present in %s:%s",
43                     ValidationUtil.getRootParentName(parent), ValidationUtil.getSimpleStatementName(childType1),
44                     ValidationUtil.getSimpleStatementName(childType2),
45                     ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent)));
46         }
47     }
48
49     static void checkOnlyPermittedValues(final ParseTree ctx, final Set<String> permittedValues) {
50         String mandatory = ValidationUtil.getName(ctx);
51         String rootParentName = ValidationUtil.getRootParentName(ctx);
52
53         if (!permittedValues.contains(mandatory)) {
54             ValidationUtil.ex(ValidationUtil.f(
55                     "(In (sub)module:%s) %s:%s, illegal value for %s statement, only permitted:%s", rootParentName,
56                     ValidationUtil.getSimpleStatementName(ctx.getClass()), mandatory,
57                     ValidationUtil.getSimpleStatementName(ctx.getClass()), permittedValues));
58         }
59     }
60
61     static void checkUniquenessInNamespace(final ParseTree stmt, final Set<String> uniques) {
62         String name = ValidationUtil.getName(stmt);
63         String rootParentName = ValidationUtil.getRootParentName(stmt);
64
65         if (uniques.contains(name)) {
66             ValidationUtil.ex(ValidationUtil.f("(In (sub)module:%s) %s:%s not unique in (sub)module", rootParentName,
67                     ValidationUtil.getSimpleStatementName(stmt.getClass()), name));
68         }
69         uniques.add(name);
70     }
71
72     /**
73      * Check if only one module or submodule is present in session(one yang
74      * file)
75      */
76     static void checkOnlyOneModulePresent(final String moduleName, final String globalId) {
77         if (globalId != null) {
78             ValidationUtil.ex(ValidationUtil.f("Multiple (sub)modules per file"));
79         }
80     }
81
82     static void checkPresentYangVersion(final ParseTree ctx, final String moduleName) {
83         if (!checkPresentChildOfTypeSafe(ctx, Yang_version_stmtContext.class, true)) {
84             ValidationUtil.ex(ValidationUtil.f(
85                     "Yang version statement not present in module:%s, Validating as yang version:%s", moduleName,
86                     SUPPORTED_YANG_VERSION));
87         }
88     }
89
90     static void checkDateFormat(final ParseTree stmt) {
91         try {
92             SimpleDateFormatUtil.getRevisionFormat().parse(ValidationUtil.getName(stmt));
93         } catch (ParseException e) {
94             String exceptionMessage = ValidationUtil.f(
95                     "(In (sub)module:%s) %s:%s, invalid date format expected date format is:%s",
96                     ValidationUtil.getRootParentName(stmt), ValidationUtil.getSimpleStatementName(stmt.getClass()),
97                     ValidationUtil.getName(stmt), SimpleDateFormatUtil.getRevisionFormat().format(new Date()));
98             ValidationUtil.ex(exceptionMessage);
99         }
100     }
101
102     private static final Pattern identifierPattern = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_.-]*");
103
104     static void checkIdentifier(final ParseTree statement) {
105         checkIdentifierInternal(statement, ValidationUtil.getName(statement));
106     }
107
108     static void checkIdentifierInternal(final ParseTree statement, final String name) {
109         if (!identifierPattern.matcher(name).matches()) {
110
111             String message = ValidationUtil.f("%s statement identifier:%s is not in required format:%s",
112                     ValidationUtil.getSimpleStatementName(statement.getClass()), name, identifierPattern.toString());
113             String parent = ValidationUtil.getRootParentName(statement);
114             message = parent.equals(name) ? message : ValidationUtil.f("(In (sub)module:%s) %s", parent, message);
115
116             if (statement instanceof ParserRuleContext) {
117                 message = "Error on line " + ((ParserRuleContext) statement).getStart().getLine() + ": " + message;
118             }
119
120             ValidationUtil.ex(message);
121         }
122     }
123
124     private static Pattern prefixedIdentifierPattern = Pattern.compile("(.+):(.+)");
125
126     static void checkPrefixedIdentifier(final ParseTree statement) {
127         checkPrefixedIdentifierInternal(statement, ValidationUtil.getName(statement));
128     }
129
130     private static void checkPrefixedIdentifierInternal(final ParseTree statement, final String id) {
131         Matcher matcher = prefixedIdentifierPattern.matcher(id);
132
133         if (matcher.matches()) {
134             try {
135                 // check prefix
136                 checkIdentifierInternal(statement, matcher.group(1));
137                 // check ID
138                 checkIdentifierInternal(statement, matcher.group(2));
139             } catch (YangValidationException e) {
140                 ValidationUtil.ex(ValidationUtil.f("Prefixed id:%s not in required format, details:%s", id,
141                         e.getMessage()));
142             }
143         } else {
144             checkIdentifierInternal(statement, id);
145         }
146     }
147
148     static void checkSchemaNodeIdentifier(final ParseTree statement) {
149         String id = ValidationUtil.getName(statement);
150
151         try {
152             for (String oneOfId : id.split("/")) {
153                 if (oneOfId.isEmpty()) {
154                     continue;
155                 }
156                 checkPrefixedIdentifierInternal(statement, oneOfId);
157             }
158         } catch (YangValidationException e) {
159             ValidationUtil.ex(ValidationUtil.f("Schema node id:%s not in required format, details:%s", id,
160                     e.getMessage()));
161         }
162     }
163
164     private interface MessageProvider {
165         String getMessage();
166     }
167
168     static void checkPresentChildOfTypeInternal(final ParseTree parent, final Set<Class<? extends ParseTree>> expectedChildType,
169             final MessageProvider message, final boolean atMostOne) {
170         if (!checkPresentChildOfTypeSafe(parent, expectedChildType, atMostOne)) {
171             String str = atMostOne ? "(Expected exactly one statement) " + message.getMessage() : message.getMessage();
172             ValidationUtil.ex(str);
173         }
174     }
175
176     static void checkPresentChildOfType(final ParseTree parent, final Class<? extends ParseTree> expectedChildType,
177             final boolean atMostOne) {
178
179         // Construct message in checkPresentChildOfTypeInternal only if
180         // validaiton fails, not in advance
181         MessageProvider message = new MessageProvider() {
182
183             @Override
184             public String getMessage() {
185                 String message = ValidationUtil.f("Missing %s statement in %s:%s",
186                         ValidationUtil.getSimpleStatementName(expectedChildType),
187                         ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent));
188
189                 String root = ValidationUtil.getRootParentName(parent);
190                 message = parent.equals(ValidationUtil.getRootParentName(parent)) ? message : ValidationUtil.f(
191                         "(In (sub)module:%s) %s", root, message);
192                 return message;
193             }
194         };
195
196         Set<Class<? extends ParseTree>> expectedChildTypeSet = Sets.newHashSet();
197         expectedChildTypeSet.add(expectedChildType);
198
199         checkPresentChildOfTypeInternal(parent, expectedChildTypeSet, message, atMostOne);
200     }
201
202     /**
203      *
204      * Implementation of interface <code>MessageProvider</code> for method
205      * {@link BasicValidations#checkPresentChildOfTypeSafe(ParseTree, Set, boolean)
206      * checkPresentChildOfTypeSafe(ParseTree, Set, boolean) * }
207      */
208     private static class MessageProviderForSetOfChildTypes implements MessageProvider {
209
210         private final Set<Class<? extends ParseTree>> expectedChildTypes;
211         private final ParseTree parent;
212
213         public MessageProviderForSetOfChildTypes(final Set<Class<? extends ParseTree>> expectedChildTypes, final ParseTree parent) {
214             this.expectedChildTypes = expectedChildTypes;
215             this.parent = parent;
216         }
217
218         @Override
219         public String getMessage() {
220             StringBuilder childTypes = new StringBuilder();
221             String orStr = " OR ";
222             for (Class<? extends ParseTree> type : expectedChildTypes) {
223                 childTypes.append(ValidationUtil.getSimpleStatementName(type));
224                 childTypes.append(orStr);
225             }
226             String message = ValidationUtil.f("Missing %s statement in %s:%s", childTypes.toString(),
227                     ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent));
228
229             String root = ValidationUtil.getRootParentName(parent);
230             message = parent.equals(ValidationUtil.getRootParentName(parent)) ? message : ValidationUtil.f(
231                     "(In (sub)module:%s) %s", root, message);
232             return message;
233         }
234     };
235
236     static void checkPresentChildOfTypes(final ParseTree parent,
237             final Set<Class<? extends ParseTree>> expectedChildTypes, final boolean atMostOne) {
238
239         // Construct message in checkPresentChildOfTypeInternal only if
240         // validaiton fails, not in advance
241         MessageProvider message = new MessageProviderForSetOfChildTypes(expectedChildTypes, parent);
242         checkPresentChildOfTypeInternal(parent, expectedChildTypes, message, atMostOne);
243     }
244
245     static boolean checkPresentChildOfTypeSafe(final ParseTree parent, final Set<Class<? extends ParseTree>> expectedChildType,
246             final boolean atMostOne) {
247
248         int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType);
249
250         return atMostOne ? foundChildrenOfType == 1 ? true : false : foundChildrenOfType != 0 ? true : false;
251     }
252
253     static boolean checkPresentChildOfTypeSafe(final ParseTree parent, final Class<? extends ParseTree> expectedChildType,
254             final boolean atMostOne) {
255
256         int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType);
257
258         return atMostOne ? foundChildrenOfType == 1 ? true : false : foundChildrenOfType != 0 ? true : false;
259     }
260
261     static List<String> getAndCheckUniqueKeys(final ParseTree ctx) {
262         String key = ValidationUtil.getName(ctx);
263         ParseTree parent = ctx.getParent();
264         String rootParentName = ValidationUtil.getRootParentName(ctx);
265
266         List<String> keyList = ValidationUtil.listKeysFromId(key);
267         Set<String> duplicates = ValidationUtil.getDuplicates(keyList);
268
269         if (duplicates.size() != 0) {
270             ValidationUtil.ex(ValidationUtil.f("(In (sub)module:%s) %s:%s, %s:%s contains duplicates:%s",
271                     rootParentName, ValidationUtil.getSimpleStatementName(parent.getClass()),
272                     ValidationUtil.getName(parent), ValidationUtil.getSimpleStatementName(ctx.getClass()), key,
273                     duplicates));
274         }
275         return keyList;
276     }
277 }