X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-parser-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fparser%2Fstmt%2Frfc6020%2FUtils.java;h=cbeae98ac6ae4762c4d54260ae8b8a04b6f0fa2f;hb=6436cd3443272f3c574f65546fd26dc63f3b01c6;hp=101038d94fcb377ae3cb1ebc4bbc136f2e57a0e2;hpb=b5c32f072f61abfe730a386f8ffe3862491785cd;p=yangtools.git diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/Utils.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/Utils.java index 101038d94f..cbeae98ac6 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/Utils.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/Utils.java @@ -7,20 +7,433 @@ */ package org.opendaylight.yangtools.yang.parser.stmt.rfc6020; +import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf; +import com.google.common.base.CharMatcher; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import javax.annotation.Nullable; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; +import org.opendaylight.yangtools.yang.common.YangConstants; +import org.opendaylight.yangtools.yang.model.api.Deviation; +import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.Status; +import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.BelongsToStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative; +import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; +import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName; +import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier; +import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName; +import org.opendaylight.yangtools.yang.parser.spi.source.ModuleIdentifierToModuleQName; +import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName; import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule; +import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition; +import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace; +import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.RootStatementContext; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class Utils { +public final class Utils { + private static final Logger LOG = LoggerFactory.getLogger(Utils.class); + private static final CharMatcher DOUBLE_QUOTE_MATCHER = CharMatcher.is('"'); + private static final CharMatcher SINGLE_QUOTE_MATCHER = CharMatcher.is('\''); - public static QName qNameFromArgument(StmtContext ctx, String value) { - // TODO: Implement real parsing - String prefix = ""; - ctx.getFromNamespace(PrefixToModule.class, prefix); + private static final char SEPARATOR_NODENAME = '/'; - return QName.create(value); + private static final String REGEX_PATH_ABS = "/[^/].*"; + + public static final char SEPARATOR = ' '; + + private Utils() { + } + + public static Collection transformKeysStringToKeyNodes(final StmtContext ctx, + final String value) { + Splitter keySplitter = Splitter.on(SEPARATOR).omitEmptyStrings().trimResults(); + List keyTokens = keySplitter.splitToList(value); + + // to detect if key contains duplicates + if ((new HashSet<>(keyTokens)).size() < keyTokens.size()) { + throw new IllegalArgumentException(); + } + + Set keyNodes = new HashSet<>(); + + for (String keyToken : keyTokens) { + + SchemaNodeIdentifier.Relative keyNode = (Relative) SchemaNodeIdentifier.Relative.create(false, + Utils.qNameFromArgument(ctx, keyToken)); + keyNodes.add(keyNode); + } + + return keyNodes; + } + + public static List splitPathToNodeNames(final String path) { + + Splitter keySplitter = Splitter.on(SEPARATOR_NODENAME).omitEmptyStrings().trimResults(); + return keySplitter.splitToList(path); + } + + public static void validateXPath(final StmtContext ctx, final String path) { + + final XPath xPath = XPathFactory.newInstance().newXPath(); + + try { + xPath.compile(path); + } catch (XPathExpressionException e) { + throw new IllegalArgumentException(String.format("Argument %s is not valid XPath string at %s", path, ctx + .getStatementSourceReference().toString()), e); + } + } + + private static String trimSingleLastSlashFromXPath(final String path) { + return path.replaceAll("/$", ""); + } + + public static boolean isXPathAbsolute(final StmtContext ctx, final String path) { + + validateXPath(ctx, trimSingleLastSlashFromXPath(path)); + + return path.matches(REGEX_PATH_ABS); } + public static QName trimPrefix(final QName identifier) { + String prefixedLocalName = identifier.getLocalName(); + String[] namesParts = prefixedLocalName.split(":"); + + if (namesParts.length == 2) { + String localName = namesParts[1]; + return QName.create(identifier.getModule(), localName); + } + + return identifier; + } + + public static String getPrefixFromArgument(final String prefixedLocalName) { + String[] namesParts = prefixedLocalName.split(":"); + if (namesParts.length == 2) { + return namesParts[0]; + } + return null; + } + + public static boolean isValidStatementDefinition(final PrefixToModule prefixes, final QNameToStatementDefinition stmtDef, + final QName identifier) { + if (stmtDef.get(identifier) != null) { + return true; + } else { + String prefixedLocalName = identifier.getLocalName(); + String[] namesParts = prefixedLocalName.split(":"); + + if (namesParts.length == 2) { + String prefix = namesParts[0]; + String localName = namesParts[1]; + if (prefixes != null && prefixes.get(prefix) != null + && stmtDef.get(QName.create(YangConstants.RFC6020_YIN_MODULE, localName)) != null) { + return true; + } else { + if (stmtDef.get(QName.create(YangConstants.RFC6020_YIN_MODULE, localName)) != null) { + return true; + } + } + } + } + return false; + } + + public static Iterable parseXPath(final StmtContext ctx, final String path) { + + String trimmedPath = trimSingleLastSlashFromXPath(path); + + validateXPath(ctx, trimmedPath); + + List nodeNames = splitPathToNodeNames(trimmedPath); + List qNames = new ArrayList<>(); + + for (String nodeName : nodeNames) { + try { + final QName qName = Utils.qNameFromArgument(ctx, nodeName); + qNames.add(qName); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + + return qNames; + } + + public static String stringFromStringContext(final YangStatementParser.ArgumentContext context) { + StringBuilder sb = new StringBuilder(); + List strings = context.STRING(); + if (strings.isEmpty()) { + strings = Arrays.asList(context.IDENTIFIER()); + } + for (TerminalNode stringNode : strings) { + final String str = stringNode.getText(); + char firstChar = str.charAt(0); + final CharMatcher quoteMatcher; + if (SINGLE_QUOTE_MATCHER.matches(firstChar)) { + quoteMatcher = SINGLE_QUOTE_MATCHER; + } else if (DOUBLE_QUOTE_MATCHER.matches(firstChar)) { + quoteMatcher = DOUBLE_QUOTE_MATCHER; + } else { + sb.append(str); + continue; + } + sb.append(quoteMatcher.removeFrom(str.substring(1, str.length() - 1))); + } + return sb.toString(); + } + + public static QName qNameFromArgument(StmtContext ctx, final String value) { + + if (value == null || value.equals("")) { + return ctx.getPublicDefinition().getStatementName(); + } + + String prefix; + QNameModule qNameModule = null; + String localName = null; + + String[] namesParts = value.split(":"); + switch (namesParts.length) { + case 1: + localName = namesParts[0]; + qNameModule = getRootModuleQName(ctx); + break; + default: + prefix = namesParts[0]; + localName = namesParts[1]; + qNameModule = getModuleQNameByPrefix(ctx, prefix); + // in case of unknown statement argument, we're not going to parse it + if (qNameModule == null + && ctx.getPublicDefinition().getDeclaredRepresentationClass() + .isAssignableFrom(UnknownStatementImpl.class)) { + localName = value; + qNameModule = getRootModuleQName(ctx); + } + if (qNameModule == null + && Iterables.getLast(ctx.getCopyHistory()) == StmtContext.TypeOfCopy.ADDED_BY_AUGMENTATION) { + ctx = ctx.getOriginalCtx(); + qNameModule = getModuleQNameByPrefix(ctx, prefix); + } + break; + } + if (qNameModule == null) { + throw new IllegalArgumentException("Error in module '" + ctx.getRoot().rawStatementArgument() + + "': can not resolve QNameModule for '" + value + "'."); + } + + QNameModule resultQNameModule = qNameModule.getRevision() == null ? QNameModule.create( + qNameModule.getNamespace(), SimpleDateFormatUtil.DEFAULT_DATE_REV) : qNameModule; + + return QName.create(resultQNameModule, localName); + } + + public static QNameModule getModuleQNameByPrefix(final StmtContext ctx, final String prefix) { + QNameModule qNameModule; + ModuleIdentifier impModIdentifier = ctx.getRoot().getFromNamespace(ImpPrefixToModuleIdentifier.class, prefix); + qNameModule = ctx.getFromNamespace(ModuleIdentifierToModuleQName.class, impModIdentifier); + + if (qNameModule == null && StmtContextUtils.producesDeclared(ctx.getRoot(), SubmoduleStatement.class)) { + String moduleName = ctx.getRoot().getFromNamespace(BelongsToPrefixToModuleName.class, prefix); + qNameModule = ctx.getFromNamespace(ModuleNameToModuleQName.class, moduleName); + } + return qNameModule; + } + + public static QNameModule getRootModuleQName(final StmtContext ctx) { + + if (ctx == null) { + return null; + } + + StmtContext rootCtx = ctx.getRoot(); + QNameModule qNameModule = null; + + if (StmtContextUtils.producesDeclared(rootCtx, ModuleStatement.class)) { + qNameModule = rootCtx.getFromNamespace(ModuleCtxToModuleQName.class, rootCtx); + } else if (StmtContextUtils.producesDeclared(rootCtx, SubmoduleStatement.class)) { + String belongsToModuleName = firstAttributeOf(rootCtx.substatements(), + BelongsToStatement.class); + qNameModule = rootCtx.getFromNamespace(ModuleNameToModuleQName.class, belongsToModuleName); + } + + return qNameModule.getRevision() == null ? QNameModule.create(qNameModule.getNamespace(), + SimpleDateFormatUtil.DEFAULT_DATE_REV) : qNameModule; + } + + @Nullable + public static StatementContextBase findNode(final StatementContextBase rootStmtCtx, + final SchemaNodeIdentifier node) { + StatementContextBase current = rootStmtCtx; + Iterator arguments = node.getPathFromRoot().iterator(); + while(current != null && arguments.hasNext()) { + current = (StatementContextBase) current.getFromNamespace(ChildSchemaNodes.class, arguments.next()); + } + return current; + } + + public static SchemaPath getSchemaPath(final StmtContext ctx) { + + if (ctx == null) { + return null; + } + + final Iterator> iteratorFromRoot = ctx.getStmtContextsFromRoot().iterator(); + // skip root argument + if (iteratorFromRoot.hasNext()) { + iteratorFromRoot.next(); + } + + List qNamesFromRoot = new LinkedList<>(); + while (iteratorFromRoot.hasNext()) { + StmtContext nextStmtCtx = iteratorFromRoot.next(); + Object nextStmtArgument = nextStmtCtx.getStatementArgument(); + if (nextStmtArgument instanceof QName) { + QName qname = (QName) nextStmtArgument; + if (StmtContextUtils.producesDeclared(nextStmtCtx, UsesStatement.class)) { + continue; + } + if (StmtContextUtils.producesDeclared(nextStmtCtx.getParentContext(), ChoiceStatement.class) + && isSupportedAsShorthandCase(nextStmtCtx)) { + qNamesFromRoot.add(qname); + } + qNamesFromRoot.add(qname); + } else if (nextStmtArgument instanceof String) { + // FIXME: This may yield illegal argument exceptions + StatementContextBase originalCtx = ctx + .getOriginalCtx(); + final QName qName = (originalCtx != null) ? qNameFromArgument( + originalCtx, (String) nextStmtArgument) + : qNameFromArgument(ctx, (String) nextStmtArgument); + qNamesFromRoot.add(qName); + } else if ((StmtContextUtils.producesDeclared(nextStmtCtx, AugmentStatement.class) + || StmtContextUtils.producesDeclared(nextStmtCtx, RefineStatement.class)) + && nextStmtArgument instanceof SchemaNodeIdentifier) { + addQNamesFromSchemaNodeIdentifierToList(qNamesFromRoot, (SchemaNodeIdentifier) nextStmtArgument); + } else if (isUnknownNode(nextStmtCtx)) { + qNamesFromRoot.add(nextStmtCtx.getPublicDefinition().getStatementName()); + } else { + return SchemaPath.SAME; + } + } + + final SchemaPath schemaPath = SchemaPath.create(qNamesFromRoot, true); + return schemaPath; + } + + public static boolean isUnknownNode(final StmtContext stmtCtx) { + return stmtCtx.getPublicDefinition().getDeclaredRepresentationClass() + .isAssignableFrom(UnknownStatementImpl.class); + } + + private static boolean isSupportedAsShorthandCase(final StmtContext statementCtx) { + + Collection supportedCaseShorthands = statementCtx.getFromNamespace(ValidationBundlesNamespace.class, + ValidationBundleType.SUPPORTED_CASE_SHORTHANDS); + + return supportedCaseShorthands == null || supportedCaseShorthands.contains(statementCtx.getPublicDefinition()); + } + + private static void addQNamesFromSchemaNodeIdentifierToList(final List qNamesFromRoot, + final SchemaNodeIdentifier augmentTargetPath) { + for (QName qname : augmentTargetPath.getPathFromRoot()) { + qNamesFromRoot.add(qname); + } + } + + public static Deviation.Deviate parseDeviateFromString(final String deviate) { + + // Yang constants should be lowercase so we have throw if value does not + // suit this + String deviateUpper = deviate.toUpperCase(); + Preconditions.checkArgument(!Objects.equals(deviate, deviateUpper), + "String %s is not valid deviate argument", deviate); + + // but Java enum is uppercase so we cannot use lowercase here + try { + return Deviation.Deviate.valueOf(deviateUpper); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(String.format("String %s is not valid deviate argument", deviate), e); + } + } + + public static Status parseStatus(final String value) { + + Status status = null; + switch (value) { + case "current": + status = Status.CURRENT; + break; + case "deprecated": + status = Status.DEPRECATED; + break; + case "obsolete": + status = Status.OBSOLETE; + break; + default: + LOG.warn("Invalid 'status' statement: " + value); + } + + return status; + } + + public static Date getLatestRevision(final RootStatementContext root) { + return getLatestRevision(root.declaredSubstatements()); + } + + public static Date getLatestRevision(final Iterable> subStmts) { + Date revision = null; + for (StmtContext subStmt : subStmts) { + if (subStmt.getPublicDefinition().getDeclaredRepresentationClass().isAssignableFrom(RevisionStatement + .class)) { + if (revision == null && subStmt.getStatementArgument() != null) { + revision = (Date) subStmt.getStatementArgument(); + } else if (subStmt.getStatementArgument() != null && ((Date) subStmt.getStatementArgument()).compareTo + (revision) > 0) { + revision = (Date) subStmt.getStatementArgument(); + } + } + } + return revision; + } + + public static boolean isModuleIdentifierWithoutSpecifiedRevision(final Object o) { + return (o instanceof ModuleIdentifier) + && (((ModuleIdentifier) o).getRevision() == SimpleDateFormatUtil.DEFAULT_DATE_IMP || + ((ModuleIdentifier) o).getRevision() == SimpleDateFormatUtil.DEFAULT_BELONGS_TO_DATE); + } }