Add static ArgumentUtils XPath integration 25/85425/12
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 29 Oct 2019 21:44:25 +0000 (22:44 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 31 Oct 2019 06:53:26 +0000 (07:53 +0100)
YANG parser needs to interpret when/must expressions so that they
can be used by downstreams in their effective view.

We add static binding of rfc7950.stmt to an implementation of
YangXPathParserFactory. This makes it clear we do not need to
actually store URIs to make things useful, hence we deprecate
URIStringToImportPrefix namespace.

JIRA: YANGTOOLS-966
Change-Id: Iaf771ad5e0bcdd058fe537ed67d5c9e4e6556e81
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
15 files changed:
features/odl-yangtools-parser/pom.xml
yang/yang-parser-impl/pom.xml
yang/yang-parser-rfc7950/pom.xml
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/ModuleQNameToPrefix.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/URIStringToImportPrefix.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/RFC7950Reactors.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ArgumentUtils.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/StmtNamespaceContext.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/WithExpressionImpl.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/XPathSupport.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/RevisionImport.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/import_/SemanticVersionImport.java
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/Bug6878Test.java
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5481Test.java
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5942Test.java

index 740988e2ab104fbc91cfa58e68cf38bc3a9fc29d..c6bdeb671aa34baaac9cc4714c74aab457901959 100644 (file)
             <type>xml</type>
             <classifier>features</classifier>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>odl-yangtools-xpath</artifactId>
+            <type>xml</type>
+            <classifier>features</classifier>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>odlext-parser-support</artifactId>
index 0bb3eaf8777ec09fdb65614dc70558d82d4c0d22..d52934dcd818b741d960e60f6102a42c742b6f9d 100644 (file)
             <artifactId>rfc8528-parser-support</artifactId>
         </dependency>
 
+        <!-- XPath reference implementation -->
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-impl</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
index e6dc53abf8601c2d2baeb0554c285181d2995be4..bc097452565ebb36f86273129707f2cd7344debc 100644 (file)
             <artifactId>logback-classic</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-xpath-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/ModuleQNameToPrefix.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/ModuleQNameToPrefix.java
new file mode 100644 (file)
index 0000000..9118076
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 org.opendaylight.yangtools.yang.parser.rfc7950.namespace;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+
+/**
+ * Implementation-internal cache for looking up URI to import prefix. URIs are taken in as Strings to save ourselves
+ * some quality parsing time.
+ */
+@Beta
+public interface ModuleQNameToPrefix extends IdentifierNamespace<QNameModule, String> {
+    NamespaceBehaviour<QNameModule, String, @NonNull ModuleQNameToPrefix> BEHAVIOUR =
+            NamespaceBehaviour.rootStatementLocal(ModuleQNameToPrefix.class);
+}
index d685b1924e3c3f81f6195ba45152f712126562a2..a22906b1d38129b04ae182393316b869445b0d1f 100644 (file)
@@ -15,7 +15,10 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
 /**
  * Implementation-internal cache for looking up URI to import prefix. URIs are taken in as Strings to save ourselves
  * some quality parsing time.
+ *
+ * @deprecated Use {@link ModuleQNameToPrefix} instead.
  */
+@Deprecated
 @Beta
 public interface URIStringToImportPrefix extends IdentifierNamespace<String, String> {
     NamespaceBehaviour<String, String, @NonNull URIStringToImportPrefix> BEHAVIOUR =
index ed174200856129acad73db57b4801fd7f8a9f677..a0394e486a455dda489642bf78a085a4c7cebd74 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.yangtools.yang.common.YangVersion;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 import org.opendaylight.yangtools.yang.parser.openconfig.stmt.OpenConfigVersionSupport;
 import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ChildSchemaNodeNamespace;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ModuleQNameToPrefix;
 import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.URIStringToImportPrefix;
 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.action.ActionStatementSupport;
 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.anydata.AnydataStatementSupport;
@@ -208,6 +209,7 @@ public final class RFC7950Reactors {
             .addSupport(IncludedSubmoduleNameToModuleCtx.BEHAVIOUR)
             .addSupport(ImportPrefixToModuleCtx.BEHAVIOUR)
             .addSupport(BelongsToPrefixToModuleCtx.BEHAVIOUR)
+            .addSupport(ModuleQNameToPrefix.BEHAVIOUR)
             .addSupport(URIStringToImportPrefix.BEHAVIOUR)
             .addSupport(BelongsToModuleContext.BEHAVIOUR)
             .addSupport(QNameToStatementDefinition.BEHAVIOUR)
index 38baa4301626c30492c1c02fe263e91ea7ab0a27..138822dc067ef408b1d12fc216ff18d09fd0fe40 100644 (file)
@@ -7,29 +7,19 @@
  */
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
 
-import static org.opendaylight.yangtools.yang.common.YangConstants.RFC6020_YANG_NAMESPACE_STRING;
-import static org.opendaylight.yangtools.yang.common.YangConstants.YANG_XPATH_FUNCTIONS_PREFIX;
-
 import com.google.common.annotations.Beta;
 import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableBiMap;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
 import org.checkerframework.checker.regex.qual.Regex;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.YangVersion;
 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
-import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
 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.SourceException;
@@ -37,9 +27,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Utility class for dealing with arguments encountered by StatementSupport classes. Note that using this class may
- * result in thread-local state getting attached. To clean up this state, please invoke
- * {@link #detachFromCurrentThread()} when appropriate.
+ * Utility class for dealing with arguments encountered by StatementSupport classes.
  */
 @Beta
 public final class ArgumentUtils {
@@ -48,24 +36,11 @@ public final class ArgumentUtils {
 
     private static final Logger LOG = LoggerFactory.getLogger(ArgumentUtils.class);
 
-    @Regex
-    private static final String YANG_XPATH_FUNCTIONS_STRING =
-            "(re-match|deref|derived-from(-or-self)?|enum-value|bit-is-set)([ \t\r\n]*)(\\()";
-    private static final Pattern YANG_XPATH_FUNCTIONS_PATTERN = Pattern.compile(YANG_XPATH_FUNCTIONS_STRING);
-
     @Regex
     private static final String PATH_ABS_STR = "/[^/].*";
     private static final Pattern PATH_ABS = Pattern.compile(PATH_ABS_STR);
     private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings().trimResults();
 
-    // XPathFactory is not thread-safe, rather than locking around a shared instance, we use a thread-local one.
-    private static final ThreadLocal<XPathFactory> XPATH_FACTORY = new ThreadLocal<>() {
-        @Override
-        protected XPathFactory initialValue() {
-            return XPathFactory.newInstance();
-        }
-    };
-
     // these objects are to compare whether range has MAX or MIN value
     // none of these values should appear as Yang number according to spec so they are safe to use
     private static final BigDecimal YANG_MIN_NUM = BigDecimal.valueOf(-Double.MAX_VALUE);
@@ -105,24 +80,7 @@ public final class ArgumentUtils {
     }
 
     public static RevisionAwareXPath parseXPath(final StmtContext<?, ?, ?> ctx, final String path) {
-        final XPath xPath = XPATH_FACTORY.get().newXPath();
-        xPath.setNamespaceContext(StmtNamespaceContext.create(ctx,
-                ImmutableBiMap.of(RFC6020_YANG_NAMESPACE_STRING, YANG_XPATH_FUNCTIONS_PREFIX)));
-
-        final String trimmed = trimSingleLastSlashFromXPath(path);
-        try {
-            // XPath extension functions have to be prefixed
-            // yang-specific XPath functions are in fact extended functions, therefore we have to add
-            // "yang" prefix to them so that they can be properly validated with the XPath.compile() method
-            // the "yang" prefix is bound to RFC6020 YANG namespace
-            final String prefixedXPath = addPrefixToYangXPathFunctions(trimmed, ctx);
-            // TODO: we could capture the result and expose its 'evaluate' method
-            xPath.compile(prefixedXPath);
-        } catch (final XPathExpressionException e) {
-            LOG.warn("Argument \"{}\" is not valid XPath string at \"{}\"", path, ctx.getStatementSourceReference(), e);
-        }
-
-        return new RevisionAwareXPathImpl(path, isAbsoluteXPath(path));
+        return XPathSupport.parseXPath(ctx, path);
     }
 
     public static boolean isAbsoluteXPath(final String path) {
@@ -145,30 +103,6 @@ public final class ArgumentUtils {
         return SchemaNodeIdentifier.create(qNames, PATH_ABS.matcher(path).matches());
     }
 
-    /**
-     * Cleanup any resources attached to the current thread. Threads interacting with this class can cause thread-local
-     * caches to them. Invoke this method if you want to detach those resources.
-     */
-    public static void detachFromCurrentThread() {
-        XPATH_FACTORY.remove();
-    }
-
-    private static String addPrefixToYangXPathFunctions(final String path, final StmtContext<?, ?, ?> ctx) {
-        if (ctx.getRootVersion() == YangVersion.VERSION_1_1) {
-            final StringBuilder result = new StringBuilder();
-            final String prefix = YANG_XPATH_FUNCTIONS_PREFIX + ":";
-            final Matcher matcher = YANG_XPATH_FUNCTIONS_PATTERN.matcher(path);
-            while (matcher.find()) {
-                matcher.appendReplacement(result, prefix + matcher.group());
-            }
-
-            matcher.appendTail(result);
-            return result.toString();
-        }
-
-        return path;
-    }
-
     private static String trimSingleLastSlashFromXPath(final String path) {
         return path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
     }
index a325f48ab333886c2e8e25d0629becfcd3cc3ab8..ae97fc11d680f754be02acec324ab26df8b8e354 100644 (file)
@@ -7,17 +7,13 @@
  */
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.Iterators;
-import java.util.Iterator;
+import java.util.Optional;
 import javax.xml.namespace.NamespaceContext;
 import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.URIStringToImportPrefix;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ModuleQNameToPrefix;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
 
@@ -25,76 +21,24 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
  * A {@link NamespaceContext} implementation based on the set of imports and local module namespace.
  */
 // TODO: this is a useful utility, so it may be useful to expose it either in this package, or yang.parser.spi.source.
-final class StmtNamespaceContext implements NamespaceContext {
-    private final StmtContext<?, ?, ?> ctx;
-    private final ImmutableBiMap<String, String> uriToPrefix;
-
-    private String localNamespaceURI;
+final class StmtNamespaceContext implements YangNamespaceContext {
+    // FIXME: add serialization barrier
+    private static final long serialVersionUID = 1L;
 
-    private StmtNamespaceContext(final StmtContext<?, ?, ?> ctx) {
-        this(ctx, ImmutableBiMap.of());
-    }
+    private final StmtContext<?, ?, ?> ctx;
 
-    private StmtNamespaceContext(final StmtContext<?, ?, ?> ctx, final BiMap<String, String> uriToPrefix) {
+    StmtNamespaceContext(final StmtContext<?, ?, ?> ctx) {
         this.ctx = requireNonNull(ctx);
-        this.uriToPrefix = ImmutableBiMap.copyOf(uriToPrefix);
-    }
-
-    public static NamespaceContext create(final StmtContext<?, ?, ?> ctx) {
-        return new StmtNamespaceContext(ctx);
-    }
-
-    public static NamespaceContext create(final StmtContext<?, ?, ?> ctx, final BiMap<String, String> uriToPrefix) {
-        return new StmtNamespaceContext(ctx, uriToPrefix);
-    }
-
-    private String localNamespaceURI() {
-        if (localNamespaceURI == null) {
-            localNamespaceURI = verifyNotNull(ctx.getPublicDefinition().getStatementName().getNamespace().toString(),
-                "Local namespace URI not found in %s", ctx);
-        }
-        return localNamespaceURI;
     }
 
     @Override
-    public String getNamespaceURI(final String prefix) {
-        // API-mandated by NamespaceContext
-        checkArgument(prefix != null);
-
-        final String uri = uriToPrefix.inverse().get(prefix);
-        if (uri != null) {
-            return uri;
-        }
-
-        if (prefix.isEmpty()) {
-            return localNamespaceURI();
-        }
-
-        final QNameModule module = StmtContextUtils.getModuleQNameByPrefix(ctx, prefix);
-        return module == null ? null : module.getNamespace().toString();
-    }
-
-    @Override
-    public String getPrefix(final String namespaceURI) {
-        // API-mandated by NamespaceContext
-        checkArgument(namespaceURI != null);
-
-        final String prefix = uriToPrefix.get(namespaceURI);
-        if (prefix != null) {
-            return prefix;
-        }
-
-        if (localNamespaceURI().equals(namespaceURI)) {
-            return "";
-        }
-        return ctx.getFromNamespace(URIStringToImportPrefix.class, namespaceURI);
+    public Optional<String> findPrefixForNamespace(final QNameModule namespace) {
+        return Optional.ofNullable(ctx.getFromNamespace(ModuleQNameToPrefix.class, namespace));
     }
 
     @Override
-    public Iterator<String> getPrefixes(final String namespaceURI) {
-        // Ensures underlying map remains constant
-        return Iterators.unmodifiableIterator(Iterators.concat(
-                ctx.getAllFromNamespace(URIStringToImportPrefix.class).values().iterator(),
-                uriToPrefix.values().iterator()));
+    public Optional<QNameModule> findNamespaceForPrefix(final String prefix) {
+        // TODO: perform caching?
+        return Optional.ofNullable(StmtContextUtils.getModuleQNameByPrefix(ctx, prefix));
     }
 }
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/WithExpressionImpl.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/WithExpressionImpl.java
new file mode 100644 (file)
index 0000000..34c81da
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  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 org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath.WithExpression;
+import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
+
+// TODO: disconnect from RevisionAwareXPathImpl
+// FIXME: absolute should not be needed: since the expression is qualified, we known how to interpret it
+// FIXME: originalString() is an escape hatch: everybody should operate of QualifiedBound
+@NonNullByDefault
+final class WithExpressionImpl extends RevisionAwareXPathImpl implements WithExpression {
+    private final QualifiedBound xpathExpression;
+
+    WithExpressionImpl(final String xpath, final boolean absolute, final QualifiedBound xpathExpression) {
+        super(xpath, absolute);
+        this.xpathExpression = requireNonNull(xpathExpression);
+    }
+
+    @Override
+    public QualifiedBound getXPathExpression() {
+        return xpathExpression;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).add("xpath", getOriginalString()).toString();
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/XPathSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/XPathSupport.java
new file mode 100644 (file)
index 0000000..c9f6027
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 org.opendaylight.yangtools.yang.parser.rfc7950.stmt;
+
+import java.util.Iterator;
+import java.util.ServiceLoader;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParserFactory;
+import org.slf4j.Logger;
+
+@NonNullByDefault
+abstract class XPathSupport {
+    private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(XPathSupport.class);
+
+    private static final XPathSupport INSTANCE;
+
+    static {
+        final Iterator<YangXPathParserFactory> it = ServiceLoader.load(YangXPathParserFactory.class).iterator();
+        if (!it.hasNext()) {
+            LOG.warn("Failed to find XPath parser factory, no XPath validation will be performed");
+            INSTANCE = new Noop();
+        } else {
+            INSTANCE = new XPathImpl(it.next());
+        }
+    }
+
+    static RevisionAwareXPath parseXPath(final StmtContext<?, ?, ?> ctx, final String xpath) {
+        return INSTANCE.parseXPath(xpath, ctx);
+    }
+
+    abstract RevisionAwareXPath parseXPath(String xpath, StmtContext<?, ?, ?> ctx);
+
+    private static final class Noop extends XPathSupport {
+        @Override
+        RevisionAwareXPath parseXPath(final String xpath, final StmtContext<?, ?, ?> ctx) {
+            return new RevisionAwareXPathImpl(xpath, ArgumentUtils.isAbsoluteXPath(xpath));
+        }
+    }
+
+    private static final class XPathImpl extends XPathSupport {
+        private final YangXPathParserFactory factory;
+
+        XPathImpl(final YangXPathParserFactory factory) {
+            this.factory = factory;
+        }
+
+        @Override
+        RevisionAwareXPath parseXPath(final String xpath, final StmtContext<?, ?, ?> ctx) {
+            final boolean isAbsolute = ArgumentUtils.isAbsoluteXPath(xpath);
+            final YangXPathParser.QualifiedBound parser = factory.newParser(new StmtNamespaceContext(ctx));
+            final QualifiedBound parsed;
+            try {
+                parsed = parser.parseExpression(xpath);
+            } catch (XPathExpressionException e) {
+                LOG.warn("Argument \"{}\" is not valid XPath string at \"{}\"", xpath,
+                    ctx.getStatementSourceReference(), e);
+                return new RevisionAwareXPathImpl(xpath, isAbsolute);
+            }
+
+            if (ctx.getRootVersion().compareTo(parsed.getYangVersion()) < 0) {
+                LOG.warn("{} features required in {} context to parse expression '{}' [at {}]",
+                    parsed.getYangVersion().getReference(), ctx.getRootVersion().getReference(), xpath,
+                    ctx.getStatementSourceReference());
+            }
+            return new WithExpressionImpl(xpath, isAbsolute, parsed);
+        }
+    }
+}
index 12f0d128586be4c25ef2475900642faaf9055767..8c52e0ba0323d49bad62957a4bfb7f6a8e1967c6 100644 (file)
@@ -14,6 +14,7 @@ import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.f
 import java.net.URI;
 import java.util.Collection;
 import java.util.Optional;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
@@ -22,6 +23,7 @@ import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ModuleQNameToPrefix;
 import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.URIStringToImportPrefix;
 import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
@@ -34,6 +36,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
 import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
 import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
 
 final class RevisionImport {
@@ -64,12 +67,17 @@ final class RevisionImport {
             public void apply(final InferenceContext ctx) {
                 final StmtContext<?, ?, ?> importedModule = imported.resolve(ctx);
 
+                final QNameModule mod = InferenceException.throwIfNull(stmt.getFromNamespace(
+                    ModuleCtxToModuleQName.class, importedModule), stmt.getStatementSourceReference(),
+                    "Failed to find module of %s", importedModule);
+
                 linkageTarget.resolve(ctx).addToNs(ImportedModuleContext.class,
                     stmt.getFromNamespace(ModuleCtxToSourceIdentifier.class, importedModule), importedModule);
                 final String impPrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
                 final URI modNs = firstAttributeOf(importedModule.declaredSubstatements(),
                     NamespaceStatement.class);
                 stmt.addToNs(ImportPrefixToModuleCtx.class, impPrefix, importedModule);
+                stmt.addToNs(ModuleQNameToPrefix.class, mod, impPrefix);
                 stmt.addToNs(URIStringToImportPrefix.class, modNs.toString(), impPrefix);
             }
 
index 4ad6d005e29029f1f551fc9fd6a31fabac8f422c..17ea58a2e20e2a5b6b3488ae662df767e3f87358 100644 (file)
@@ -16,12 +16,14 @@ import java.net.URI;
 import java.util.Collection;
 import java.util.Optional;
 import org.opendaylight.yangtools.concepts.SemVer;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
 import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.ModuleQNameToPrefix;
 import org.opendaylight.yangtools.yang.parser.rfc7950.namespace.URIStringToImportPrefix;
 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
@@ -36,6 +38,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
 import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
 import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToSemVerSourceIdentifier;
 import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
 
 final class SemanticVersionImport {
@@ -147,8 +150,13 @@ final class SemanticVersionImport {
                 stmt.addToNs(ImportPrefixToModuleCtx.class, impPrefix, importedModule);
                 stmt.addToNs(ImportPrefixToSemVerSourceIdentifier.class, impPrefix, semVerModuleIdentifier);
 
+                final QNameModule mod = InferenceException.throwIfNull(stmt.getFromNamespace(
+                    ModuleCtxToModuleQName.class, importedModule), stmt.getStatementSourceReference(),
+                    "Failed to find module of %s", importedModule);
+
                 final URI modNs = firstAttributeOf(importedModule.declaredSubstatements(),
                     NamespaceStatement.class);
+                stmt.addToNs(ModuleQNameToPrefix.class, mod, impPrefix);
                 stmt.addToNs(URIStringToImportPrefix.class, modNs.toString(), impPrefix);
             }
 
index 92a214fc7cf98b3cd2a88810d422c2101192b856..7f7140b39b7037f02d145713b6686252770b5415 100644 (file)
@@ -5,67 +5,49 @@
  * 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 org.opendaylight.yangtools.yang.parser.stmt.rfc7950;
 
+import static org.hamcrest.Matchers.containsString;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertThat;
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import org.junit.Test;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.stmt.StmtTestUtils;
 
 public class Bug6878Test {
 
     @Test
-    @SuppressWarnings("checkstyle:regexpSinglelineJava")
     public void testParsingXPathWithYang11Functions() throws Exception {
-        final PrintStream stdout = System.out;
-        final ByteArrayOutputStream output = new ByteArrayOutputStream();
-        final String testLog;
-
-        System.setOut(new PrintStream(output, true, "UTF-8"));
-
-        final SchemaContext schemaContext = StmtTestUtils.parseYangSource("/rfc7950/bug6878/foo.yang");
-        assertNotNull(schemaContext);
-
-        testLog = output.toString();
+        final String testLog = parseAndcaptureLog("/rfc7950/bug6878/foo.yang");
         assertFalse(testLog.contains("Could not find function: "));
-        System.setOut(stdout);
     }
 
     @Test
-    @SuppressWarnings("checkstyle:regexpSinglelineJava")
     public void shouldLogInvalidYang10XPath() throws Exception {
-        final PrintStream stdout = System.out;
-        final ByteArrayOutputStream output = new ByteArrayOutputStream();
-        final String testLog;
-
-        System.setOut(new PrintStream(output, true, "UTF-8"));
-
-        StmtTestUtils.parseYangSource("/rfc7950/bug6878/foo10-invalid.yang");
-
-        testLog = output.toString();
-        assertTrue(testLog.contains("Could not find function: re-match"));
-        System.setOut(stdout);
+        final String testLog = parseAndcaptureLog("/rfc7950/bug6878/foo10-invalid.yang");
+        assertThat(testLog, containsString("RFC7950 features required in RFC6020 context to parse expression "));
     }
 
     @Test
-    @SuppressWarnings("checkstyle:regexpSinglelineJava")
     public void shouldLogInvalidYang10XPath2() throws Exception {
+        final String testLog = parseAndcaptureLog("/rfc7950/bug6878/foo10-invalid-2.yang");
+        assertThat(testLog, containsString("RFC7950 features required in RFC6020 context to parse expression "));
+    }
+
+    @SuppressWarnings("checkstyle:regexpSinglelineJava")
+    private static String parseAndcaptureLog(final String yangFile) throws Exception {
         final PrintStream stdout = System.out;
         final ByteArrayOutputStream output = new ByteArrayOutputStream();
-        final String testLog;
-
-        System.setOut(new PrintStream(output, true, "UTF-8"));
 
-        StmtTestUtils.parseYangSource("/rfc7950/bug6878/foo10-invalid-2.yang");
+        try (PrintStream out = new PrintStream(output, true, "UTF-8")) {
+            System.setOut(out);
+            StmtTestUtils.parseYangSource(yangFile);
+        } finally {
+            System.setOut(stdout);
+        }
 
-        testLog = output.toString();
-        assertTrue(testLog.contains("Could not find function: deref"));
-        System.setOut(stdout);
+        return output.toString();
     }
 }
index 321ae473bbdadae8e92ec85ba335b3287be1c3c8..44b2cf1ab3a24d6e2c5d168b64ee434d4aea9ddf 100644 (file)
@@ -8,9 +8,11 @@
 
 package org.opendaylight.yangtools.yang.stmt;
 
+import static org.hamcrest.Matchers.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Optional;
@@ -20,9 +22,9 @@ import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath.WithExpression;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.Status;
-import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
 
 public class Bug5481Test {
     @Test
@@ -54,11 +56,13 @@ public class Bug5481Test {
         assertTrue(dataChildByName2 instanceof LeafSchemaNode);
 
         LeafSchemaNode extendedLeaf = (LeafSchemaNode) dataChildByName2;
-        RevisionAwareXPath whenConditionExtendedLeaf = extendedLeaf.getWhenCondition().get();
-
-        assertEquals(new RevisionAwareXPathImpl("module1:top = 'extended'", false), whenConditionExtendedLeaf);
         assertEquals(Status.DEPRECATED, extendedLeaf.getStatus());
         assertEquals(Optional.of("text"), extendedLeaf.getDescription());
         assertEquals(Optional.of("ref"), extendedLeaf.getReference());
+
+        RevisionAwareXPath whenConditionExtendedLeaf = extendedLeaf.getWhenCondition().get();
+        assertFalse(whenConditionExtendedLeaf.isAbsolute());
+        assertThat(whenConditionExtendedLeaf, instanceOf(WithExpression.class));
+        assertEquals("module1:top = 'extended'", whenConditionExtendedLeaf.getOriginalString());
     }
 }
index 5c7b541529cb3ae782a4875810a41d987c565c8e..6e219a8c3ee4c572a54805d991d7ce0b39ecb46b 100644 (file)
@@ -5,11 +5,13 @@
  * 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 org.opendaylight.yangtools.yang.stmt;
 
+import static org.hamcrest.Matchers.instanceOf;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import java.util.List;
@@ -19,11 +21,12 @@ import org.junit.Test;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath.WithExpression;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.Status;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UsesNode;
-import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
 
 public class Bug5942Test {
     @Test
@@ -41,7 +44,11 @@ public class Bug5942Test {
         assertEquals(Optional.of("uses description"), usesNode.getDescription());
         assertEquals(Optional.of("uses reference"), usesNode.getReference());
         assertEquals(Status.DEPRECATED, usesNode.getStatus());
-        assertEquals(new RevisionAwareXPathImpl("0!=1", false), usesNode.getWhenCondition().get());
+
+        final RevisionAwareXPath when = usesNode.getWhenCondition().get();
+        assertFalse(when.isAbsolute());
+        assertThat(when, instanceOf(WithExpression.class));
+        assertEquals("0!=1", when.getOriginalString());
 
         final List<UnknownSchemaNode> unknownSchemaNodes = usesNode.getUnknownSchemaNodes();
         assertEquals(1, unknownSchemaNodes.size());