Add ModuleNameNamespaceContext 22/80622/2
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 27 Feb 2019 11:17:48 +0000 (12:17 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 27 Feb 2019 11:31:03 +0000 (12:31 +0100)
This adds a utility YangNamespaceContext, which maps QNameModules
to their corresponding module name as contained in a SchemaContext.

Change-Id: I3be6316d0e7c1f9f98439e3bca3b9b0d385f63bb
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/BiMapYangNamespaceContext.java
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/BiMapYangNamespaceContextTest.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/ModuleNameNamespaceContext.java [new file with mode: 0644]

index 96edd486be0814d029b4ef403c1b8833be5d4b30..9cc9e0e2e6fb13098f25d7793262de8940b8d10c 100644 (file)
@@ -24,9 +24,9 @@ import java.util.Optional;
 import org.opendaylight.yangtools.concepts.WritableObject;
 
 /**
- * A BiMap-based implementation of {@link YangNamespaceContext}. This implementation requires the default namespace
- * to be present.
+ * A BiMap-based implementation of {@link YangNamespaceContext}.
  *
+ * @author Robert Varga
  */
 @Beta
 public final class BiMapYangNamespaceContext implements YangNamespaceContext, WritableObject {
@@ -35,16 +35,23 @@ public final class BiMapYangNamespaceContext implements YangNamespaceContext, Wr
     private final ImmutableBiMap<String, QNameModule> mapping;
     private final QNameModule defaultNamespace;
 
-    public BiMapYangNamespaceContext(final QNameModule defaultNamespace, final BiMap<String, QNameModule> mapping) {
-        this.defaultNamespace = requireNonNull(defaultNamespace);
-        checkArgument(mapping.containsValue(defaultNamespace), "Mapping %s does not contain default namespace %s",
-            mapping, defaultNamespace);
+    public BiMapYangNamespaceContext(final BiMap<String, QNameModule> mapping) {
+        this(mapping, null);
+    }
+
+    public BiMapYangNamespaceContext(final BiMap<String, QNameModule> mapping,
+            final QNameModule defaultNamespace) {
+        this.defaultNamespace = defaultNamespace;
         this.mapping = ImmutableBiMap.copyOf(mapping);
+        if (defaultNamespace != null) {
+            checkArgument(this.mapping.containsValue(defaultNamespace),
+                "Mapping %s does not contain default namespace %s", this.mapping, defaultNamespace);
+        }
     }
 
     @Override
     public Optional<QNameModule> getDefaultNamespace() {
-        return Optional.of(defaultNamespace);
+        return Optional.ofNullable(defaultNamespace);
     }
 
     @Override
@@ -59,7 +66,13 @@ public final class BiMapYangNamespaceContext implements YangNamespaceContext, Wr
 
     @Override
     public void writeTo(final DataOutput out) throws IOException {
-        defaultNamespace.writeTo(out);
+        if (defaultNamespace != null) {
+            out.writeBoolean(true);
+            defaultNamespace.writeTo(out);
+        } else {
+            out.writeBoolean(false);
+        }
+
         out.writeInt(mapping.size());
         for (Entry<String, QNameModule> entry : mapping.entrySet()) {
             out.writeUTF(entry.getKey());
@@ -68,7 +81,8 @@ public final class BiMapYangNamespaceContext implements YangNamespaceContext, Wr
     }
 
     public static BiMapYangNamespaceContext readFrom(final DataInput in) throws IOException {
-        final QNameModule defaultNamespace = QNameModule.readFrom(in);
+        final boolean haveDefault = in.readBoolean();
+        final QNameModule defaultNamespace = haveDefault ? QNameModule.readFrom(in) : null;
         final int size = in.readInt();
         final Builder<String, QNameModule> builder = ImmutableBiMap.builder();
         for (int i = 0; i < size; ++i) {
@@ -77,7 +91,7 @@ public final class BiMapYangNamespaceContext implements YangNamespaceContext, Wr
             builder.put(prefix, namespace);
         }
 
-        return new BiMapYangNamespaceContext(defaultNamespace, builder.build());
+        return new BiMapYangNamespaceContext(builder.build(), defaultNamespace);
     }
 
     @Override
index a4a697d324ce40d75724e2aeb7a66dedef1a339b..0a15d92b5c05f75f71f45fa7ad19e777ce86b844 100644 (file)
@@ -26,12 +26,12 @@ public class BiMapYangNamespaceContextTest {
     private static final QNameModule BAR = QNameModule.create(URI.create("bar"));
     private static final QNameModule BAZ = QNameModule.create(URI.create("baz"));
 
-    private final BiMapYangNamespaceContext context = new BiMapYangNamespaceContext(FOO,
-        ImmutableBiMap.of("foo", FOO, "bar", BAR));
+    private final BiMapYangNamespaceContext context = new BiMapYangNamespaceContext(
+        ImmutableBiMap.of("foo", FOO, "bar", BAR), FOO);
 
     @Test(expected = IllegalArgumentException.class)
     public void testUnmappedDefault() {
-        new BiMapYangNamespaceContext(FOO, ImmutableBiMap.of());
+        new BiMapYangNamespaceContext(ImmutableBiMap.of(), FOO);
     }
 
     @Test
@@ -42,10 +42,10 @@ public class BiMapYangNamespaceContextTest {
     @Test
     public void testEquals() {
         assertTrue(context.equals(context));
-        assertTrue(context.equals(new BiMapYangNamespaceContext(FOO, ImmutableBiMap.of("foo", FOO, "bar", BAR))));
+        assertTrue(context.equals(new BiMapYangNamespaceContext(ImmutableBiMap.of("foo", FOO, "bar", BAR), FOO)));
         assertFalse(context.equals(null));
-        assertFalse(context.equals(new BiMapYangNamespaceContext(FOO, ImmutableBiMap.of("foo", FOO))));
-        assertFalse(context.equals(new BiMapYangNamespaceContext(BAR, ImmutableBiMap.of("bar", BAR))));
+        assertFalse(context.equals(new BiMapYangNamespaceContext(ImmutableBiMap.of("foo", FOO), FOO)));
+        assertFalse(context.equals(new BiMapYangNamespaceContext(ImmutableBiMap.of("bar", BAR), BAR)));
     }
 
     @Test
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/ModuleNameNamespaceContext.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/ModuleNameNamespaceContext.java
new file mode 100644 (file)
index 0000000..7336b34
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019 Pantheon Technologies, 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.model.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.opendaylight.yangtools.yang.common.BiMapYangNamespaceContext;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+/**
+ * Utility {@link YangNamespaceContext} backed by a SchemaContext, resolving namespaces to their module names. This
+ * is useful for implementing namespace resolution according to
+ * <a href="https://tools.ietf.org/html/rfc7951#section-4">RFC7951 Section 4</a>.
+ *
+ * <p>
+ * When multiple revisions of a particular namespace are present in the backing SchemaContext, this ambiguity is
+ * resolved by using the latest revision available.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class ModuleNameNamespaceContext implements YangNamespaceContext, SchemaContextProvider {
+    private static final long serialVersionUID = 1L;
+
+    @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Handled through writeReplace()")
+    private final SchemaContext schemaContext;
+
+    public ModuleNameNamespaceContext(final SchemaContext schemaContext) {
+        this.schemaContext = requireNonNull(schemaContext);
+    }
+
+    /**
+     * Convert this object to an equivalent {@link BiMapYangNamespaceContext}.
+     *
+     * @return A BiMapYangNamespaceContext.
+     */
+    public BiMapYangNamespaceContext toBiMap() {
+        final Builder<String, QNameModule> builder = ImmutableBiMap.builder();
+        for (String name : schemaContext.getModules().stream().map(Module::getName).collect(Collectors.toSet())) {
+            builder.put(name, findNamespaceForPrefix(name).get());
+        }
+        return new BiMapYangNamespaceContext(builder.build());
+    }
+
+    @Override
+    public SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    @Override
+    public Optional<QNameModule> getDefaultNamespace() {
+        throw new IllegalStateException("No default namespace is available");
+    }
+
+    @Override
+    public Optional<QNameModule> findNamespaceForPrefix(final String prefix) {
+        return schemaContext.findModules(prefix).stream().findFirst().map(Module::getQNameModule);
+    }
+
+    @Override
+    public Optional<String> findPrefixForNamespace(final QNameModule namespace) {
+        return schemaContext.findModule(namespace).map(Module::getName);
+    }
+
+    private Object writeReplace() {
+        return toBiMap();
+    }
+}