Use a proper NamespaceContext for XPaths 58/27858/6
authorRobert Varga <rovarga@cisco.com>
Fri, 2 Oct 2015 22:43:19 +0000 (00:43 +0200)
committerRobert Varga <rovarga@cisco.com>
Sat, 3 Oct 2015 14:06:46 +0000 (16:06 +0200)
Parsing an XPath which contains namespace prefixes requires a
NamespaceContext. Create a StmtContext-backed implementation and pass it
to XPath before we attempt to parse an XPath string.

Change-Id: Ib28cac2525ffcb6b8e3026e19e431cb9dc86c14b
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/ImportStatementDefinition.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/StmtNamespaceContext.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/URIStringToImpPrefix.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/Utils.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/YangInferencePipeline.java

index dd73f6c484f5c000bd733e89b28b9945ab6fd3e2..0dbd2c3ee3d66ff261a7543dff40c4aea39f58a1 100644 (file)
@@ -15,13 +15,13 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
 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.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleIdentifierImpl;
@@ -34,12 +34,10 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prereq
 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.ImpPrefixToModuleIdentifier;
-import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ImportEffectiveStatementImpl;
 
 public class ImportStatementDefinition
-        extends
-        AbstractStatementSupport<String, ImportStatement, EffectiveStatement<String, ImportStatement>> {
+        extends AbstractStatementSupport<String, ImportStatement, EffectiveStatement<String, ImportStatement>> {
 
     public ImportStatementDefinition() {
         super(Rfc6020Mapping.IMPORT);
@@ -64,44 +62,38 @@ public class ImportStatementDefinition
 
     @Override
     public void onLinkageDeclared(
-            final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt)
-            throws SourceException {
+            final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) {
         final ModuleIdentifier impIdentifier = getImportedModuleIdentifier(stmt);
-        ModelActionBuilder importAction = stmt
-                .newInferenceAction(SOURCE_LINKAGE);
-        final Prerequisite<StmtContext<?, ?, ?>> imported;
-        final Prerequisite<Mutable<?, ?, ?>> linkageTarget;
-        imported = importAction.requiresCtx(stmt, ModuleNamespace.class,
-                impIdentifier, SOURCE_LINKAGE);
-        linkageTarget = importAction.mutatesCtx(stmt.getRoot(), SOURCE_LINKAGE);
+        final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_LINKAGE);
+        final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt, ModuleNamespace.class,
+            impIdentifier, SOURCE_LINKAGE);
+        final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction.mutatesCtx(stmt.getRoot(), SOURCE_LINKAGE);
 
         importAction.apply(new InferenceAction() {
-
             @Override
             public void apply() throws InferenceException {
                 StmtContext<?, ?, ?> importedModule = null;
                 ModuleIdentifier importedModuleIdentifier = null;
                 if (impIdentifier.getRevision() == SimpleDateFormatUtil.DEFAULT_DATE_IMP) {
                     Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> recentModuleEntry = findRecentModule(
-                            impIdentifier,
-                            stmt.getAllFromNamespace(ModuleNamespace.class));
+                            impIdentifier, stmt.getAllFromNamespace(ModuleNamespace.class));
                     if (recentModuleEntry != null) {
                         importedModuleIdentifier = recentModuleEntry.getKey();
                         importedModule = recentModuleEntry.getValue();
                     }
                 }
 
-                if(importedModule == null || importedModuleIdentifier == null) {
+                if (importedModule == null || importedModuleIdentifier == null) {
                     importedModule = imported.get();
                     importedModuleIdentifier = impIdentifier;
                 }
 
-                linkageTarget.get().addToNs(ImportedModuleContext.class,
-                        importedModuleIdentifier, importedModule);
-                String impPrefix = firstAttributeOf(stmt.declaredSubstatements(),
-                        PrefixStatement.class);
-                stmt.addToNs(ImpPrefixToModuleIdentifier.class, impPrefix,
-                        importedModuleIdentifier);
+                linkageTarget.get().addToNs(ImportedModuleContext.class, importedModuleIdentifier, importedModule);
+                String impPrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
+                stmt.addToNs(ImpPrefixToModuleIdentifier.class, impPrefix, importedModuleIdentifier);
+
+                final URI modNs = firstAttributeOf(importedModule.declaredSubstatements(), NamespaceStatement.class);
+                stmt.addToNs(URIStringToImpPrefix.class, modNs.toString(), impPrefix);
             }
 
             private Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> findRecentModule(
@@ -111,17 +103,9 @@ public class ImportStatementDefinition
                 ModuleIdentifier recentModuleIdentifier = impIdentifier;
                 Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> recentModuleEntry = null;
 
-                Set<Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>>> moduleEntrySet = allModules
-                        .entrySet();
-                for (Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> moduleEntry : moduleEntrySet) {
-                    if (moduleEntry.getKey().getName()
-                            .equals(impIdentifier.getName())
-                            && moduleEntry
-                                    .getKey()
-                                    .getRevision()
-                                    .compareTo(
-                                            recentModuleIdentifier
-                                                    .getRevision()) > 0) {
+                for (Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> moduleEntry : allModules.entrySet()) {
+                    if (moduleEntry.getKey().getName().equals(impIdentifier.getName())
+                            && moduleEntry.getKey().getRevision().compareTo(recentModuleIdentifier.getRevision()) > 0) {
                         recentModuleIdentifier = moduleEntry.getKey();
                         recentModuleEntry = moduleEntry;
                     }
@@ -131,29 +115,22 @@ public class ImportStatementDefinition
             }
 
             @Override
-            public void prerequisiteFailed(
-                    final Collection<? extends Prerequisite<?>> failed)
-                    throws InferenceException {
+            public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed)  {
                 if (failed.contains(imported)) {
-                    throw new InferenceException(String.format(
-                            "Imported module [%s] was not found.",
-                            impIdentifier), stmt.getStatementSourceReference());
+                    throw new InferenceException(String.format("Imported module [%s] was not found.", impIdentifier),
+                        stmt.getStatementSourceReference());
                 }
             }
         });
     }
 
-    private static ModuleIdentifier getImportedModuleIdentifier(
-            final Mutable<String, ImportStatement, ?> stmt) throws SourceException {
-
-        String moduleName = stmt.getStatementArgument();
-        Date revision = firstAttributeOf(stmt.declaredSubstatements(),
-                RevisionDateStatement.class);
+    private static ModuleIdentifier getImportedModuleIdentifier(final Mutable<String, ImportStatement, ?> stmt) {
+        Date revision = firstAttributeOf(stmt.declaredSubstatements(), RevisionDateStatement.class);
         if (revision == null) {
             revision = SimpleDateFormatUtil.DEFAULT_DATE_IMP;
         }
 
-        return new ModuleIdentifierImpl(moduleName, Optional.<URI> absent(),
+        return new ModuleIdentifierImpl(stmt.getStatementArgument(), Optional.<URI> absent(),
                 Optional.<Date> of(revision));
     }
 
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/StmtNamespaceContext.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/StmtNamespaceContext.java
new file mode 100644 (file)
index 0000000..8a54ec2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 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 org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
+import com.google.common.collect.Iterators;
+import java.util.Iterator;
+import javax.xml.namespace.NamespaceContext;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+
+/**
+ * A {@link NamespaceContext} implementation based on the set of imports and local module namespace.
+ *
+ * TODO: this is a useful utility, so should probably move to yang.parser.spi.meta.
+ */
+final class StmtNamespaceContext implements NamespaceContext {
+    private final StmtContext<?, ?, ?> ctx;
+    private String localNamespaceURI;
+
+    private StmtNamespaceContext(final StmtContext<?, ?, ?> ctx) {
+        this.ctx = Preconditions.checkNotNull(ctx);
+    }
+
+    public static NamespaceContext create(final StmtContext<?, ?, ?> ctx) {
+        return new StmtNamespaceContext(ctx);
+    }
+
+    private String localNamespaceURI() {
+        if (localNamespaceURI == null) {
+            localNamespaceURI = Verify.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
+        Preconditions.checkArgument(prefix != null);
+
+        if (prefix.isEmpty()) {
+            return localNamespaceURI();
+        }
+
+        final QNameModule module = Utils.getModuleQNameByPrefix(ctx, prefix);
+        return module == null ? null : module.getNamespace().toString();
+    }
+
+    @Override
+    public String getPrefix(final String namespaceURI) {
+        // API-mandated by NamespaceContext
+        Preconditions.checkArgument(namespaceURI != null);
+
+        if (localNamespaceURI().equals(namespaceURI)) {
+            return "";
+        }
+        return ctx.getFromNamespace(URIStringToImpPrefix.class, namespaceURI);
+    }
+
+    @Override
+    public Iterator<String> getPrefixes(final String namespaceURI) {
+        // Ensures underlying map remains constant
+        return Iterators.unmodifiableIterator(
+            ctx.getAllFromNamespace(URIStringToImpPrefix.class).values().iterator());
+    }
+}
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/URIStringToImpPrefix.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/URIStringToImpPrefix.java
new file mode 100644 (file)
index 0000000..35a958b
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 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 org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
+
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+
+/**
+ * Implementation-internal cache for looking up URI -> import prefix. URIs are taken in as Strings to save ourselves
+ * some quality parsing time.
+ */
+interface URIStringToImpPrefix extends IdentifierNamespace<String, String> {
+
+}
index 32ab655db707207ccc0a8b47b581168557edb85e..806857fabfa3e04637cbe79ff997bbd9cb14695a 100644 (file)
@@ -122,6 +122,7 @@ public final class Utils {
 
     static RevisionAwareXPath parseXPath(final StmtContext<?, ?, ?> ctx, final String path) {
         final XPath xPath = XPATH_FACTORY.get().newXPath();
+        xPath.setNamespaceContext(StmtNamespaceContext.create(ctx));
 
         final String trimmed = trimSingleLastSlashFromXPath(path);
         try {
index 6dd2595d3ed8a70e0df8a41c2b3562eab4c5c9b6..e424c9a235413e30bc066f7448c65c779067c317 100644 (file)
@@ -73,6 +73,7 @@ public final class YangInferencePipeline {
             .addSupport(sourceLocal(IncludedModuleContext.class))
             .addSupport(sourceLocal(IncludedSubmoduleNameToIdentifier.class))
             .addSupport(sourceLocal(ImpPrefixToModuleIdentifier.class))
+            .addSupport(sourceLocal(URIStringToImpPrefix.class))
             .addSupport(sourceLocal(BelongsToModuleContext.class))
             .addSupport(sourceLocal(QNameToStatementDefinition.class))
             .addSupport(sourceLocal(BelongsToPrefixToModuleName.class)).build();