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