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