Fix root choice targeting
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 28 May 2018 23:16:38 +0000 (01:16 +0200)
committerAnil Belur <abelur@linuxfoundation.org>
Wed, 19 Jun 2024 00:41:23 +0000 (10:41 +1000)
InstanceIdentifier codec infrastructure cannot deal with choices
which have only grouping children due to the inability to properly
find the choice schema.

Use the case-assisted addressing mode to allow these items to be
properly resolved.

JIRA: MDSAL-45
Change-Id: I61f88e0baa854abc8286ca5c57edd572638ca63b
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/InstanceIdentifierSerializeDeserializeTest.java
binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifier.java

index a1b75d97e4f9f33df014744449a13fd15d907dd8..8190bceee09cc8678d86264b253920ee85dce20b 100644 (file)
@@ -10,11 +10,15 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
+import com.google.common.base.Verify;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.util.concurrent.UncheckedExecutionException;
+import java.lang.reflect.Type;
+import java.util.List;
 import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -62,6 +66,14 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
                 }
             });
 
+    private final LoadingCache<Class<? extends DataObject>, ChoiceNodeCodecContext<?>> choicesByClass =
+            CacheBuilder.newBuilder().build(new CacheLoader<Class<? extends DataObject>, ChoiceNodeCodecContext<?>>() {
+                @Override
+                public ChoiceNodeCodecContext<?> load(final Class<? extends DataObject> key) {
+                    return createChoiceDataContext(key);
+                }
+            });
+
     private final LoadingCache<QName, DataContainerCodecContext<?,?>> childrenByQName = CacheBuilder.newBuilder().build(
             new CacheLoader<QName, DataContainerCodecContext<?,?>>() {
                 @Override
@@ -217,6 +229,19 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         return new NotificationCodecContext<>(notificationType, schema, factory());
     }
 
+    ChoiceNodeCodecContext<?> createChoiceDataContext(final Class<? extends DataObject> caseType) {
+        final Class<?> choiceClass = findCaseChoice(caseType);
+        Preconditions.checkArgument(choiceClass != null, "Class %s is not a valid case representation", caseType);
+        final DataSchemaNode schema = factory().getRuntimeContext().getSchemaDefinition(choiceClass);
+        Preconditions.checkArgument(schema instanceof ChoiceSchemaNode, "Class %s does not refer to a choice",
+            caseType);
+
+        final DataContainerCodecContext<?, ChoiceSchemaNode> choice = DataContainerCodecPrototype.from(choiceClass,
+            (ChoiceSchemaNode)schema, factory()).get();
+        Verify.verify(choice instanceof ChoiceNodeCodecContext);
+        return (ChoiceNodeCodecContext<?>) choice;
+    }
+
     @Override
     protected Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
         throw new UnsupportedOperationException("Unable to deserialize root");
@@ -234,6 +259,35 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         return null;
     }
 
+    @Override
+    public DataContainerCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
+            final List<PathArgument> builder) {
+        final java.util.Optional<? extends Class<? extends DataObject>> caseType = arg.getCaseType();
+        if (caseType.isPresent()) {
+            final Class<? extends DataObject> type = caseType.get();
+            final ChoiceNodeCodecContext<?> choice = choicesByClass.getUnchecked(type);
+            choice.addYangPathArgument(arg, builder);
+            final DataContainerCodecContext<?, ?> caze = choice.streamChild(type);
+            caze.addYangPathArgument(arg, builder);
+            return caze.bindingPathArgumentChild(arg, builder);
+        }
+
+        return super.bindingPathArgumentChild(arg, builder);
+    }
+
+    private static Class<?> findCaseChoice(final Class<? extends DataObject> caseClass) {
+        for (Type type : caseClass.getGenericInterfaces()) {
+            if (type instanceof Class) {
+                final Class<?> typeClass = (Class<?>) type;
+                if (ChoiceIn.class.isAssignableFrom(typeClass)) {
+                    return typeClass.asSubclass(ChoiceIn.class);
+                }
+            }
+        }
+
+        return null;
+    }
+
     private static <K,V> V getOrRethrow(final LoadingCache<K, V> cache, final K key) {
         try {
             return cache.getUnchecked(key);
index f233a971e9c7d15ac2779b57ade7906b0d67a6d0..fa05cfb1ba42aafc0a15e0cf719f64ec5dbc4966 100644 (file)
@@ -25,10 +25,13 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.te
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal45.aug.norev.cont.cont.choice.ContAug;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal45.aug.norev.root.RootAug;
 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal45.base.norev.Cont;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal45.base.norev.Root;
 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal45.base.norev.cont.ContChoice;
 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal45.base.norev.cont.cont.choice.ContBase;
 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal45.base.norev.grp.GrpCont;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal45.base.norev.root.RootBase;
 import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -155,13 +158,17 @@ public class InstanceIdentifierSerializeDeserializeTest extends AbstractBindingR
         assertEquals(1, ContBase.class.getCanonicalName().compareTo(ContAug.class.getCanonicalName()));
         final YangInstanceIdentifier contAugLegacy = registry.toYangInstanceIdentifier(
             InstanceIdentifier.builder(Cont.class).child((Class) GrpCont.class).build());
-        assertEquals(YangInstanceIdentifier.create(NodeIdentifier.create(Cont.QNAME),
-            NodeIdentifier.create(ContChoice.QNAME),
-            NodeIdentifier.create(GrpCont.QNAME.withModule(ContAug.QNAME.getModule()))), contAugLegacy);
+        assertEquals(contAug, contAugLegacy);
+
+        final YangInstanceIdentifier rootBase = registry.toYangInstanceIdentifier(
+            InstanceIdentifier.builder(RootBase.class, GrpCont.class).build());
+        assertEquals(YangInstanceIdentifier.create(NodeIdentifier.create(Root.QNAME),
+            NodeIdentifier.create(GrpCont.QNAME)), rootBase);
 
-        // FIXME: root choice handling is busted
-        //      final YangInstanceIdentifier rootAugLegacy = registry.toYangInstanceIdentifier(
-        //          InstanceIdentifier.create((Class) GrpCont.class));
+        final YangInstanceIdentifier rootAug = registry.toYangInstanceIdentifier(
+            InstanceIdentifier.builder(RootAug.class, GrpCont.class).build());
+        assertEquals(YangInstanceIdentifier.create(NodeIdentifier.create(Root.QNAME),
+            NodeIdentifier.create(GrpCont.QNAME.withModule(RootAug.QNAME.getModule()))), rootAug);
     }
 
     @Test
@@ -175,5 +182,14 @@ public class InstanceIdentifierSerializeDeserializeTest extends AbstractBindingR
             YangInstanceIdentifier.create(NodeIdentifier.create(Cont.QNAME), NodeIdentifier.create(ContChoice.QNAME),
                 NodeIdentifier.create(GrpCont.QNAME.withModule(ContAug.QNAME.getModule()))));
         assertEquals(InstanceIdentifier.builder(Cont.class).child(ContAug.class, GrpCont.class).build(), contAug);
+
+        final InstanceIdentifier<?> rootBase = registry.fromYangInstanceIdentifier(
+            YangInstanceIdentifier.create(NodeIdentifier.create(Root.QNAME), NodeIdentifier.create(GrpCont.QNAME)));
+        assertEquals(InstanceIdentifier.builder(RootBase.class, GrpCont.class).build(), rootBase);
+
+        final InstanceIdentifier<?> rootAug = registry.fromYangInstanceIdentifier(
+            YangInstanceIdentifier.create(NodeIdentifier.create(Root.QNAME),
+                NodeIdentifier.create(GrpCont.QNAME.withModule(RootAug.QNAME.getModule()))));
+        assertEquals(InstanceIdentifier.builder(RootAug.class, GrpCont.class).build(), rootAug);
     }
 }
index 909305ec1bdc22a94f15c1d4a9cae6fc1d6715ed..6b71a31122aee22cedbb238b8e72c5c5882bb047 100644 (file)
@@ -464,6 +464,22 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
         return new InstanceIdentifierBuilderImpl<T>().addWildNode(Item.of(container));
     }
 
+    /**
+     * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container in
+     * a {@code grouping} used in the {@code case} statement.
+     *
+     * @param caze Choice case class
+     * @param container Base container
+     * @param <C> Case type
+     * @param <T> Type of the container
+     * @return A new {@link InstanceIdentifierBuilder}
+     * @throws NullPointerException if any argument is null
+     */
+    public static <C extends ChoiceIn<? extends DataRoot> & DataObject, T extends ChildOf<? super C>>
+            InstanceIdentifierBuilder<T> builder(final Class<C> caze, final Class<T> container) {
+        return new InstanceIdentifierBuilderImpl<T>().addWildNode(Item.of(caze, container));
+    }
+
     /**
      * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an
      * {@link IdentifiableItem}.
@@ -480,6 +496,25 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
         return new InstanceIdentifierBuilderImpl<N>().addNode(IdentifiableItem.of(listItem, listKey));
     }
 
+    /**
+     * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an
+     * {@link IdentifiableItem} in a {@code grouping} used in the {@code case} statement.
+     *
+     * @param caze Choice case class
+     * @param listItem list item class
+     * @param listKey key value
+     * @param <C> Case type
+     * @param <N> List type
+     * @param <K> List key
+     * @return A new {@link InstanceIdentifierBuilder}
+     * @throws NullPointerException if any argument is null
+     */
+    public static <C extends ChoiceIn<? extends DataRoot> & DataObject,
+            N extends Identifiable<K> & ChildOf<? super C>, K extends Identifier<N>>
+            InstanceIdentifierBuilder<N> builder(final Class<C> caze, final Class<N> listItem, final K listKey) {
+        return new InstanceIdentifierBuilderImpl<N>().addNode(IdentifiableItem.of(caze, listItem, listKey));
+    }
+
     /**
      * Create an instance identifier for a very specific object type. This method implements {@link #create(Iterable)}
      * semantics, except it is used by internal callers, which have assured that the argument is an immutable Iterable.