b78fb046526674a8f0e09c1f374aec4ba2a10c33
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / 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.parser.impl;
9
10 import static java.lang.String.format;
11
12 import com.google.common.collect.Sets;
13 import java.text.ParseException;
14 import java.util.Date;
15 import java.util.List;
16 import java.util.Set;
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
19 import org.antlr.v4.runtime.ParserRuleContext;
20 import org.antlr.v4.runtime.tree.ParseTree;
21 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yang_version_stmtContext;
22 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
23 import org.opendaylight.yangtools.yang.parser.util.YangValidationException;
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 checkIsModuleIdNull(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 final 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     private 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         // validation 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 = format("(In (sub)module:%s) %s", root, message);
191                 return message;
192             }
193         };
194
195         Set<Class<? extends ParseTree>> expectedChildTypeSet = Sets.newHashSet();
196         expectedChildTypeSet.add(expectedChildType);
197
198         checkPresentChildOfTypeInternal(parent, expectedChildTypeSet, message, atMostOne);
199     }
200
201     /**
202      *
203      * Implementation of interface <code>MessageProvider</code> for method
204      * {@link BasicValidations#checkPresentChildOfTypeSafe(ParseTree, Set, boolean)
205      * checkPresentChildOfTypeSafe(ParseTree, Set, boolean) * }
206      */
207     private static class MessageProviderForSetOfChildTypes implements MessageProvider {
208
209         private final Set<Class<? extends ParseTree>> expectedChildTypes;
210         private final ParseTree parent;
211
212         public MessageProviderForSetOfChildTypes(final Set<Class<? extends ParseTree>> expectedChildTypes, final ParseTree parent) {
213             this.expectedChildTypes = expectedChildTypes;
214             this.parent = parent;
215         }
216
217         @Override
218         public String getMessage() {
219             StringBuilder childTypes = new StringBuilder();
220             String orStr = " OR ";
221             for (Class<? extends ParseTree> type : expectedChildTypes) {
222                 childTypes.append(ValidationUtil.getSimpleStatementName(type));
223                 childTypes.append(orStr);
224             }
225             String message = ValidationUtil.f("Missing %s statement in %s:%s", childTypes.toString(),
226                     ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent));
227
228             String root = ValidationUtil.getRootParentName(parent);
229             message = format("(In (sub)module:%s) %s", root, message);
230             return message;
231         }
232     }
233
234     static void checkPresentChildOfTypes(final ParseTree parent,
235             final Set<Class<? extends ParseTree>> expectedChildTypes, final boolean atMostOne) {
236
237         // Construct message in checkPresentChildOfTypeInternal only if
238         // validation fails, not in advance
239         MessageProvider message = new MessageProviderForSetOfChildTypes(expectedChildTypes, parent);
240         checkPresentChildOfTypeInternal(parent, expectedChildTypes, message, atMostOne);
241     }
242
243     private static boolean checkPresentChildOfTypeSafe(final ParseTree parent, final Set<Class<? extends ParseTree>> expectedChildType,
244                                                        final boolean atMostOne) {
245
246         int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType);
247
248         return atMostOne ? foundChildrenOfType == 1 : foundChildrenOfType != 0;
249     }
250
251     private static boolean checkPresentChildOfTypeSafe(final ParseTree parent, final Class<? extends ParseTree> expectedChildType,
252                                                        final boolean atMostOne) {
253
254         int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType);
255
256         return atMostOne ? foundChildrenOfType == 1 : foundChildrenOfType != 0;
257     }
258
259     static List<String> getAndCheckUniqueKeys(final ParseTree ctx) {
260         String key = ValidationUtil.getName(ctx);
261         ParseTree parent = ctx.getParent();
262         String rootParentName = ValidationUtil.getRootParentName(ctx);
263
264         List<String> keyList = ValidationUtil.listKeysFromId(key);
265         Set<String> duplicates = ValidationUtil.getDuplicates(keyList);
266
267         if (duplicates.size() != 0) {
268             ValidationUtil.ex(ValidationUtil.f("(In (sub)module:%s) %s:%s, %s:%s contains duplicates:%s",
269                     rootParentName, ValidationUtil.getSimpleStatementName(parent.getClass()),
270                     ValidationUtil.getName(parent), ValidationUtil.getSimpleStatementName(ctx.getClass()), key,
271                     duplicates));
272         }
273         return keyList;
274     }
275 }