Disconnect StmtNamespaceContext from statement 14/90814/3
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 29 Jun 2020 21:24:15 +0000 (23:24 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 30 Jun 2020 09:18:16 +0000 (11:18 +0200)
Retaining a reference to any StmtContext is not good, as we end
up retaining the entire build context from each parsed XPath. Make
sure we maintain a simple disconnected YangNamespaceContext
implementation at each root.

JIRA: YANGTOOLS-1116
Change-Id: Ic327647b758c83b94bde05a740c51384f70c4d03
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/StmtNamespaceContext.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/YangNamespaceContextNamespace.java [new file with mode: 0644]
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/StmtNamespaceContext.java [deleted file]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/XPathSupport.java

diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/StmtNamespaceContext.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/StmtNamespaceContext.java
new file mode 100644 (file)
index 0000000..38bd6c1
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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.rfc7950.namespace;
+
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import javax.xml.namespace.NamespaceContext;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.BelongsToPrefixToModuleName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleQName;
+import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToModuleQName;
+
+/**
+ * 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 YangNamespaceContext {
+    private static final long serialVersionUID = 1L;
+
+    private final ImmutableBiMap<QNameModule, String> moduleToPrefix;
+    private final ImmutableMap<String, QNameModule> prefixToModule;
+
+    StmtNamespaceContext(final StmtContext<?, ?, ?> ctx) {
+        // QNameModule -> prefix mappings
+        final Map<QNameModule, String> qnameToPrefix = ctx.getAllFromNamespace(ModuleQNameToPrefix.class);
+        this.moduleToPrefix = qnameToPrefix == null ? ImmutableBiMap.of() : ImmutableBiMap.copyOf(qnameToPrefix);
+
+        // Additional mappings
+        final Map<String, StmtContext<?, ?, ?>> imports = ctx.getAllFromNamespace(ImportPrefixToModuleCtx.class);
+        if (imports != null) {
+            final Map<String, QNameModule> additional = new HashMap<>();
+            for (Entry<String, StmtContext<?, ?, ?>> entry : imports.entrySet()) {
+                if (!moduleToPrefix.containsValue(entry.getKey())) {
+                    QNameModule qnameModule = ctx.getFromNamespace(ModuleCtxToModuleQName.class, entry.getValue());
+                    if (qnameModule == null && ctx.producesDeclared(SubmoduleStatement.class)) {
+                        final String moduleName = ctx.getFromNamespace(BelongsToPrefixToModuleName.class,
+                            entry.getKey());
+                        qnameModule = ctx.getFromNamespace(ModuleNameToModuleQName.class, moduleName);
+                    }
+
+                    if (qnameModule != null) {
+                        additional.put(entry.getKey(), qnameModule);
+                    }
+                }
+            }
+            this.prefixToModule = ImmutableMap.copyOf(additional);
+        } else {
+            this.prefixToModule = ImmutableMap.of();
+        }
+    }
+
+    @Override
+    public Optional<String> findPrefixForNamespace(final QNameModule namespace) {
+        return Optional.ofNullable(moduleToPrefix.get(namespace));
+    }
+
+    @Override
+    public Optional<QNameModule> findNamespaceForPrefix(final String prefix) {
+        final QNameModule normal = moduleToPrefix.inverse().get(prefix);
+        return normal != null ? Optional.of(normal) : Optional.ofNullable(prefixToModule.get(prefix));
+    }
+}
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/YangNamespaceContextNamespace.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/namespace/YangNamespaceContextNamespace.java
new file mode 100644 (file)
index 0000000..4334479
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 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 static com.google.common.base.Verify.verify;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+
+@Beta
+public interface YangNamespaceContextNamespace extends IdentifierNamespace<Empty, YangNamespaceContext> {
+    NamespaceBehaviour<Empty, YangNamespaceContext, @NonNull YangNamespaceContextNamespace> BEHAVIOUR =
+            NamespaceBehaviour.rootStatementLocal(YangNamespaceContextNamespace.class);
+
+    static @NonNull YangNamespaceContext computeIfAbsent(final StmtContext<?, ?, ?> ctx) {
+        final StmtContext<?, ?, ?> root = ctx.getRoot();
+        YangNamespaceContext ret = root.getFromNamespace(YangNamespaceContextNamespace.class, Empty.getInstance());
+        if (ret == null) {
+            verify(ctx instanceof Mutable, "Cannot populate namespace context to %s", ctx);
+            ret = new StmtNamespaceContext(ctx);
+            ((Mutable<?, ?, ?>)ctx).addToNs(YangNamespaceContextNamespace.class, Empty.getInstance(), ret);
+        }
+        return ret;
+    }
+}
index 5a61ebef02b51abafcfe6479d15890bdf2cbea4d..0f3afed61cbbd579f7e150bbc196cb6a0be4c071 100644 (file)
@@ -19,6 +19,7 @@ import org.opendaylight.yangtools.yang.parser.openconfig.stmt.OpenConfigVersionS
 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.namespace.YangNamespaceContextNamespace;
 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.XPathSupport;
 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.action.ActionStatementSupport;
 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.anydata.AnydataStatementSupport;
@@ -391,6 +392,7 @@ public final class RFC7950Reactors {
             .addSupport(FeatureStatementSupport.getInstance())
             .addSupport(PositionStatementSupport.getInstance())
             .addSupport(ValueStatementSupport.getInstance())
+            .addSupport(YangNamespaceContextNamespace.BEHAVIOUR)
             .build();
     }
 }
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/StmtNamespaceContext.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/StmtNamespaceContext.java
deleted file mode 100644 (file)
index bdff19e..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.rfc7950.stmt;
-
-import static java.util.Objects.requireNonNull;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.util.Optional;
-import javax.xml.namespace.NamespaceContext;
-import org.opendaylight.yangtools.yang.common.QNameModule;
-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;
-
-/**
- * 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,
-//       but that requires dealing with serialization below.
-final class StmtNamespaceContext implements YangNamespaceContext {
-    private static final long serialVersionUID = 1L;
-
-    // FIXME: deal with serialization by serializing the underlying namespace Map
-    @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Not passed to serialization")
-    private final StmtContext<?, ?, ?> ctx;
-
-    StmtNamespaceContext(final StmtContext<?, ?, ?> ctx) {
-        this.ctx = requireNonNull(ctx);
-    }
-
-    @Override
-    public Optional<String> findPrefixForNamespace(final QNameModule namespace) {
-        return Optional.ofNullable(ctx.getFromNamespace(ModuleQNameToPrefix.class, namespace));
-    }
-
-    @Override
-    public Optional<QNameModule> findNamespaceForPrefix(final String prefix) {
-        // TODO: perform caching?
-        return Optional.ofNullable(StmtContextUtils.getModuleQNameByPrefix(ctx, prefix));
-    }
-}
index a5bc3f462e57e690badf854b3e319633a8c0d178..6043d0e86d1cb1cfbf70e3c28c5a740c01c1862a 100644 (file)
@@ -14,6 +14,7 @@ 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.rfc7950.namespace.YangNamespaceContextNamespace;
 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;
@@ -34,7 +35,8 @@ public final class XPathSupport {
 
     public RevisionAwareXPath parseXPath(final StmtContext<?, ?, ?> ctx, final String xpath) {
         final boolean isAbsolute = ArgumentUtils.isAbsoluteXPath(xpath);
-        final YangXPathParser.QualifiedBound parser = factory.newParser(new StmtNamespaceContext(ctx));
+        final YangXPathParser.QualifiedBound parser = factory.newParser(
+            YangNamespaceContextNamespace.computeIfAbsent(ctx));
         final QualifiedBound parsed;
         try {
             parsed = parser.parseExpression(xpath);