/******************************************************************************* * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package com.cisco.yangide.core.parser; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Yang_version_stmtContext; /** * Reusable checks of basic constraints on yang statements * * @author Konstantin Zaitsev * @date Jul 9, 2014 */ public class BasicValidations { static final String SUPPORTED_YANG_VERSION = "1"; /** * It isn't desirable to create instance of this class. */ private BasicValidations() { } static void checkNotPresentBoth(ParseTree parent, Class<? extends ParseTree> childType1, Class<? extends ParseTree> childType2) { if (BasicValidations.checkPresentChildOfTypeSafe(parent, childType1, true) && BasicValidations.checkPresentChildOfTypeSafe(parent, childType2, false)) { ValidationUtil.ex(parent, ValidationUtil.f("(In (sub)module:%s) Both %s and %s statement present in %s:%s", ValidationUtil.getRootParentName(parent), ValidationUtil.getSimpleStatementName(childType1), ValidationUtil.getSimpleStatementName(childType2), ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent))); } } static void checkOnlyPermittedValues(ParseTree ctx, Set<String> permittedValues) { String mandatory = ValidationUtil.getName(ctx); String rootParentName = ValidationUtil.getRootParentName(ctx); if (!permittedValues.contains(mandatory)) { ValidationUtil.ex(ctx, ValidationUtil.f( "(In (sub)module:%s) %s:%s, illegal value for %s statement, only permitted:%s", rootParentName, ValidationUtil.getSimpleStatementName(ctx.getClass()), mandatory, ValidationUtil.getSimpleStatementName(ctx.getClass()), permittedValues)); } } static void checkUniquenessInNamespace(ParseTree stmt, Set<String> uniques) { String name = ValidationUtil.getName(stmt); String rootParentName = ValidationUtil.getRootParentName(stmt); if (uniques.contains(name)) { ValidationUtil.ex(stmt, ValidationUtil.f("(In (sub)module:%s) %s:%s not unique in (sub)module", rootParentName, ValidationUtil.getSimpleStatementName(stmt.getClass()), name)); } uniques.add(name); } /** * Check if only one module or submodule is present in session(one yang file) */ static void checkOnlyOneModulePresent(ParseTree ctx, String moduleName, String globalId) { if (globalId != null) { ValidationUtil.ex(ctx, ValidationUtil.f("Multiple (sub)modules per file")); } } static void checkPresentYangVersion(ParseTree ctx, String moduleName) { if (!checkPresentChildOfTypeSafe(ctx, Yang_version_stmtContext.class, true)) { ValidationUtil.ex(ctx, ValidationUtil.f( "Yang version statement not present in module:%s, Validating as yang version:%s", moduleName, SUPPORTED_YANG_VERSION)); } } static void checkDateFormat(ParseTree stmt, DateFormat format) { try { format.parse(ValidationUtil.getName(stmt)); } catch (ParseException e) { String exceptionMessage = ValidationUtil.f( "(In (sub)module:%s) %s:%s, invalid date format expected date format is:%s", ValidationUtil.getRootParentName(stmt), ValidationUtil.getSimpleStatementName(stmt.getClass()), ValidationUtil.getName(stmt), new SimpleDateFormat("yyyy-MM-dd").format(new Date())); ValidationUtil.ex(stmt, exceptionMessage); } } private static Pattern identifierPattern = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_.-]*"); static void checkIdentifier(ParseTree statement) { checkIdentifierInternal(statement, ValidationUtil.getName(statement)); } static void checkIdentifierInternal(ParseTree statement, String name) { if (!identifierPattern.matcher(name).matches()) { String message = ValidationUtil.f("%s statement identifier:%s is not in required format:%s", ValidationUtil.getSimpleStatementName(statement.getClass()), name, identifierPattern.toString()); String parent = ValidationUtil.getRootParentName(statement); message = parent.equals(name) ? message : ValidationUtil.f("(In (sub)module:%s) %s", parent, message); if (statement instanceof ParserRuleContext) { message = "Error on line " + ((ParserRuleContext) statement).getStart().getLine() + ": " + message; } ValidationUtil.ex(statement, message); } } private static Pattern prefixedIdentifierPattern = Pattern.compile("(.+):(.+)"); static void checkPrefixedIdentifier(ParseTree statement) { checkPrefixedIdentifierInternal(statement, ValidationUtil.getName(statement)); } private static void checkPrefixedIdentifierInternal(ParseTree statement, String id) { Matcher matcher = prefixedIdentifierPattern.matcher(id); if (matcher.matches()) { try { // check prefix checkIdentifierInternal(statement, matcher.group(1)); // check ID checkIdentifierInternal(statement, matcher.group(2)); } catch (YangValidationException e) { ValidationUtil.ex(statement, ValidationUtil.f("Prefixed id:%s not in required format, details:%s", id, e.getMessage())); } } else { checkIdentifierInternal(statement, id); } } static void checkSchemaNodeIdentifier(ParseTree statement) { String id = ValidationUtil.getName(statement); try { for (String oneOfId : id.split("/")) { if (oneOfId.isEmpty()) { continue; } checkPrefixedIdentifierInternal(statement, oneOfId); } } catch (YangValidationException e) { ValidationUtil.ex(statement, ValidationUtil.f("Schema node id:%s not in required format, details:%s", id, e.getMessage())); } } private interface MessageProvider { String getMessage(); } static void checkPresentChildOfTypeInternal(ParseTree parent, Set<Class<? extends ParseTree>> expectedChildType, MessageProvider message, boolean atMostOne) { if (!checkPresentChildOfTypeSafe(parent, expectedChildType, atMostOne)) { String str = atMostOne ? "(Expected exactly one statement) " + message.getMessage() : message.getMessage(); ValidationUtil.ex(parent, str); } } static void checkPresentChildOfType(final ParseTree parent, final Class<? extends ParseTree> expectedChildType, boolean atMostOne) { // Construct message in checkPresentChildOfTypeInternal only if // validaiton fails, not in advance MessageProvider message = new MessageProvider() { @Override public String getMessage() { String message = ValidationUtil.f("Missing %s statement in %s:%s", ValidationUtil.getSimpleStatementName(expectedChildType), ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent)); String root = ValidationUtil.getRootParentName(parent); message = ValidationUtil.getName(parent).equals(root) ? message : ValidationUtil.f( "(In (sub)module:%s) %s", root, message); return message; } }; Set<Class<? extends ParseTree>> expectedChildTypeSet = new HashSet<>(); expectedChildTypeSet.add(expectedChildType); checkPresentChildOfTypeInternal(parent, expectedChildTypeSet, message, atMostOne); } /** * Implementation of interface <code>MessageProvider</code> for method * {@link BasicValidations#checkPresentChildOfTypeSafe(ParseTree, Set, boolean) * checkPresentChildOfTypeSafe(ParseTree, Set, boolean) * } */ private static class MessageProviderForSetOfChildTypes implements MessageProvider { private Set<Class<? extends ParseTree>> expectedChildTypes; private ParseTree parent; public MessageProviderForSetOfChildTypes(Set<Class<? extends ParseTree>> expectedChildTypes, ParseTree parent) { this.expectedChildTypes = expectedChildTypes; this.parent = parent; } @Override public String getMessage() { StringBuilder childTypes = new StringBuilder(); String orStr = " OR "; for (Class<? extends ParseTree> type : expectedChildTypes) { childTypes.append(ValidationUtil.getSimpleStatementName(type)); childTypes.append(orStr); } String message = ValidationUtil.f("Missing %s statement in %s:%s", childTypes.toString(), ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent)); String root = ValidationUtil.getRootParentName(parent); message = ValidationUtil.getName(parent).equals(root) ? message : ValidationUtil.f( "(In (sub)module:%s) %s", root, message); return message; } }; static void checkPresentChildOfTypes(final ParseTree parent, final Set<Class<? extends ParseTree>> expectedChildTypes, boolean atMostOne) { // Construct message in checkPresentChildOfTypeInternal only if // validaiton fails, not in advance MessageProvider message = new MessageProviderForSetOfChildTypes(expectedChildTypes, parent); checkPresentChildOfTypeInternal(parent, expectedChildTypes, message, atMostOne); } static boolean checkPresentChildOfTypeSafe(ParseTree parent, Set<Class<? extends ParseTree>> expectedChildType, boolean atMostOne) { int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType); return atMostOne ? foundChildrenOfType == 1 ? true : false : foundChildrenOfType != 0 ? true : false; } static boolean checkPresentChildOfTypeSafe(ParseTree parent, Class<? extends ParseTree> expectedChildType, boolean atMostOne) { int foundChildrenOfType = ValidationUtil.countPresentChildrenOfType(parent, expectedChildType); return atMostOne ? foundChildrenOfType == 1 ? true : false : foundChildrenOfType != 0 ? true : false; } static List<String> getAndCheckUniqueKeys(ParseTree ctx) { String key = ValidationUtil.getName(ctx); ParseTree parent = ctx.getParent(); String rootParentName = ValidationUtil.getRootParentName(ctx); List<String> keyList = ValidationUtil.listKeysFromId(key); Set<String> duplicates = ValidationUtil.getDuplicates(keyList); if (duplicates.size() != 0) { ValidationUtil.ex(ctx, ValidationUtil.f("(In (sub)module:%s) %s:%s, %s:%s contains duplicates:%s", rootParentName, ValidationUtil.getSimpleStatementName(parent.getClass()), ValidationUtil.getName(parent), ValidationUtil.getSimpleStatementName(ctx.getClass()), key, duplicates)); } return keyList; } }