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