e91ffeadb2e7034b4da98e0da2c8b136ef28b281
[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.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.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;
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     /**
34      * It isn't desirable to create instance of this class.
35      */
36     private BasicValidations() {
37     }
38
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)));
47         }
48     }
49
50     static void checkOnlyPermittedValues(ParseTree ctx, Set<String> permittedValues) {
51         String mandatory = ValidationUtil.getName(ctx);
52         String rootParentName = ValidationUtil.getRootParentName(ctx);
53
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));
59         }
60     }
61
62     static void checkUniquenessInNamespace(ParseTree stmt, Set<String> uniques) {
63         String name = ValidationUtil.getName(stmt);
64         String rootParentName = ValidationUtil.getRootParentName(stmt);
65
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));
69         }
70         uniques.add(name);
71     }
72
73     /**
74      * Check if only one module or submodule is present in session(one yang
75      * file)
76      */
77     static void checkOnlyOneModulePresent(String moduleName, String globalId) {
78         if (globalId != null) {
79             ValidationUtil.ex(ValidationUtil.f("Multiple (sub)modules per file"));
80         }
81     }
82
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));
88         }
89     }
90
91     static void checkDateFormat(ParseTree stmt, DateFormat format) {
92         try {
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);
100         }
101     }
102
103     static private Pattern identifierPattern = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_.-]*");
104
105     static void checkIdentifier(ParseTree statement) {
106         checkIdentifierInternal(statement, ValidationUtil.getName(statement));
107     }
108
109     static void checkIdentifierInternal(ParseTree statement, String name) {
110         if (!identifierPattern.matcher(name).matches()) {
111
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);
116
117             if (statement instanceof ParserRuleContext) {
118                 message = "Error on line " + ((ParserRuleContext) statement).getStart().getLine() + ": " + message;
119             }
120
121             ValidationUtil.ex(message);
122         }
123     }
124
125     static private Pattern prefixedIdentifierPattern = Pattern.compile("(.+):(.+)");
126
127     static void checkPrefixedIdentifier(ParseTree statement) {
128         checkPrefixedIdentifierInternal(statement, ValidationUtil.getName(statement));
129     }
130
131     private static void checkPrefixedIdentifierInternal(ParseTree statement, String id) {
132         Matcher matcher = prefixedIdentifierPattern.matcher(id);
133
134         if (matcher.matches()) {
135             try {
136                 // check prefix
137                 checkIdentifierInternal(statement, matcher.group(1));
138                 // check ID
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,
142                         e.getMessage()));
143             }
144         } else {
145             checkIdentifierInternal(statement, id);
146         }
147     }
148
149     static void checkSchemaNodeIdentifier(ParseTree statement) {
150         String id = ValidationUtil.getName(statement);
151
152         try {
153             for (String oneOfId : id.split("/")) {
154                 if (oneOfId.isEmpty()) {
155                     continue;
156                 }
157                 checkPrefixedIdentifierInternal(statement, oneOfId);
158             }
159         } catch (YangValidationException e) {
160             ValidationUtil.ex(ValidationUtil.f("Schema node id:%s not in required format, details:%s", id,
161                     e.getMessage()));
162         }
163     }
164
165     private interface MessageProvider {
166         String getMessage();
167     }
168
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);
174         }
175     }
176
177     static void checkPresentChildOfType(final ParseTree parent, final Class<? extends ParseTree> expectedChildType,
178             boolean atMostOne) {
179
180         // Construct message in checkPresentChildOfTypeInternal only if
181         // validaiton fails, not in advance
182         MessageProvider message = new MessageProvider() {
183
184             @Override
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));
189
190                 String root = ValidationUtil.getRootParentName(parent);
191                 message = parent.equals(ValidationUtil.getRootParentName(parent)) ? message : ValidationUtil.f(
192                         "(In (sub)module:%s) %s", root, message);
193                 return message;
194             }
195         };
196
197         Set<Class<? extends ParseTree>> expectedChildTypeSet = Sets.newHashSet();
198         expectedChildTypeSet.add(expectedChildType);
199
200         checkPresentChildOfTypeInternal(parent, expectedChildTypeSet, message, atMostOne);
201     }
202
203     /**
204      * 
205      * Implementation of interface <code>MessageProvider</code> for method
206      * {@link BasicValidations#checkPresentChildOfTypeSafe(ParseTree, Set, boolean)
207      * checkPresentChildOfTypeSafe(ParseTree, Set, boolean) * }
208      */
209     private static class MessageProviderForSetOfChildTypes implements MessageProvider {
210
211         private Set<Class<? extends ParseTree>> expectedChildTypes;
212         private ParseTree parent;
213
214         public MessageProviderForSetOfChildTypes(Set<Class<? extends ParseTree>> expectedChildTypes, ParseTree parent) {
215             this.expectedChildTypes = expectedChildTypes;
216             this.parent = parent;
217         }
218
219         @Override
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);
226             }
227             String message = ValidationUtil.f("Missing %s statement in %s:%s", childTypes.toString(),
228                     ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent));
229
230             String root = ValidationUtil.getRootParentName(parent);
231             message = parent.equals(ValidationUtil.getRootParentName(parent)) ? message : ValidationUtil.f(
232                     "(In (sub)module:%s) %s", root, message);
233             return message;
234         }
235     };
236
237     static void checkPresentChildOfTypes(final ParseTree parent,
238             final Set<Class<? extends ParseTree>> expectedChildTypes, boolean atMostOne) {
239
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);
244     }
245
246     static boolean checkPresentChildOfTypeSafe(ParseTree parent, Set<Class<? extends ParseTree>> expectedChildType,
247             boolean atMostOne) {
248
249         int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType);
250
251         return atMostOne ? foundChildrenOfType == 1 ? true : false : foundChildrenOfType != 0 ? true : false;
252     }
253
254     static boolean checkPresentChildOfTypeSafe(ParseTree parent, Class<? extends ParseTree> expectedChildType,
255             boolean atMostOne) {
256
257         int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType);
258
259         return atMostOne ? foundChildrenOfType == 1 ? true : false : foundChildrenOfType != 0 ? true : false;
260     }
261
262     static List<String> getAndCheckUniqueKeys(ParseTree ctx) {
263         String key = ValidationUtil.getName(ctx);
264         ParseTree parent = ctx.getParent();
265         String rootParentName = ValidationUtil.getRootParentName(ctx);
266
267         List<String> keyList = ValidationUtil.listKeysFromId(key);
268         Set<String> duplicates = ValidationUtil.getDuplicates(keyList);
269
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,
274                     duplicates));
275         }
276         return keyList;
277     }
278 }