Reparent ChoiceCodecContext 49/109749/6
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 11 Jan 2024 22:37:43 +0000 (23:37 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 12 Jan 2024 11:08:23 +0000 (12:08 +0100)
Choice as itself does not map to a DataObject, but rather to a ChoiceIn
-- it is the individual cases that also map to DataObject.

In this patch we reparent ChoiceCodecContext to be a subclass of
DataContainerCodecContext, which allows us to ditch the idea that a
there is a InstanceIdentifier.PathArgument corresponding to this
context.

A nice bonus is that we end up with exactly one serialization method,
which is delegating to the individual case.

Another nice thing is we have a natural place to host the case lookup
logic -- which fits squarely with
ChoiceCodecContext.bindingPathArgumentChild() contract.

JIRA: MDSAL-815
Change-Id: I12a80c849f6405e0e5723afc3f31704a2ad604a6
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
17 files changed:
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDataObjectModification.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingChoiceCodecTreeNode.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataContainerCodecTreeNode.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeParent.java
binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationCodecPrototype.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecPrototype.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecPrototype.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerAnalysis.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerPrototype.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecPrototype.java

index 57ab7c906ece3620e00d1c69d0fc09224df80d92..71d306df30e36b1eb528098ce8a8a40f40fb8a48 100644 (file)
@@ -28,6 +28,8 @@ import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.api.DataObjectModification;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingChoiceCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataContainerCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
@@ -321,7 +323,7 @@ abstract sealed class AbstractDataObjectModification<T extends DataObject, N ext
     }
 
     private static void populateList(final ImmutableList.Builder<AbstractDataObjectModification<?, ?>> result,
-            final CommonDataObjectCodecTreeNode<?> parentCodec, final DataTreeCandidateNode parent,
+            final BindingDataContainerCodecTreeNode<?> parentCodec, final DataTreeCandidateNode parent,
             final Collection<DataTreeCandidateNode> children) {
         final var augmentChildren =
             ArrayListMultimap.<BindingAugmentationCodecTreeNode<?>, DataTreeCandidateNode>create();
@@ -341,6 +343,8 @@ abstract sealed class AbstractDataObjectModification<T extends DataObject, N ext
                         } else if (childCodec instanceof BindingAugmentationCodecTreeNode<?> childAugmentationCodec) {
                             // Defer creation once we have collected all modified children
                             augmentChildren.put(childAugmentationCodec, domChildNode);
+                        } else if (childCodec instanceof BindingChoiceCodecTreeNode<?> childChoiceCodec) {
+                            populateList(result, childChoiceCodec, domChildNode, domChildNode.childNodes());
                         } else {
                             throw new VerifyException("Unhandled codec %s for type %s".formatted(childCodec, type));
                         }
diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingChoiceCodecTreeNode.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingChoiceCodecTreeNode.java
new file mode 100644 (file)
index 0000000..b9c72d2
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2024 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.mdsal.binding.dom.codec.api;
+
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
+
+/**
+ * A {@link BindingDataContainerCodecTreeNode} corresponding to a base {@link ChoiceIn}.
+ *
+ * @param <C> ChoiceIn type
+ */
+public interface BindingChoiceCodecTreeNode<C extends ChoiceIn<?>> extends BindingDataContainerCodecTreeNode<C> {
+
+}
index 7a6321d66c231af75eea1c5178b5ff41c602ee46..c2ef310ca47b3f3162781a2a2cf7d3d501e4e19d 100644 (file)
@@ -47,7 +47,7 @@ public non-sealed interface BindingDataContainerCodecTreeNode<T extends DataCont
      * @return Context of child or {@code null} is supplied class is not applicable in context.
      * @throws NullPointerException if {@code childClass} is {@code null}
      */
-    <E extends DataObject> @Nullable CommonDataObjectCodecTreeNode<E> streamChild(@NonNull Class<E> childClass);
+    <E extends DataObject> @Nullable BindingDataContainerCodecTreeNode<E> streamChild(@NonNull Class<E> childClass);
 
     default <A extends Augmentation<?>> @Nullable BindingAugmentationCodecTreeNode<A> streamAugmentation(
             final @NonNull Class<A> childClass) {
index bfaf2aba4adbc611001e7d2da196be976fdc0526..8ac63661a863d3e5144db729938de5aa9733cb27 100644 (file)
@@ -46,7 +46,7 @@ public interface BindingDataObjectCodecTreeParent<T> {
      * @throws NullPointerException if {@code childClass} is {@code null}
      * @throws IllegalArgumentException If supplied child class is not valid in specified context.
      */
-    <E extends DataObject> @NonNull CommonDataObjectCodecTreeNode<E> getStreamChild(@NonNull Class<E> childClass);
+    <E extends DataObject> @NonNull BindingDataContainerCodecTreeNode<E> getStreamChild(@NonNull Class<E> childClass);
 
     default <A extends Augmentation<?>> @NonNull BindingAugmentationCodecTreeNode<A> getStreamAugmentation(
             final @NonNull Class<A> childClass) {
index f24235daec0adad7e265c249d0aa4cc6da4d5004..3a1a8b3814b1363866ef18125cd46666e73fc95d 100644 (file)
@@ -14,6 +14,7 @@ import java.util.Map.Entry;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataContainerCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingIdentityCodec;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCodec;
@@ -251,7 +252,7 @@ public abstract class ForwardingBindingDOMCodecServices extends ForwardingObject
     }
 
     @Override
-    public <E extends DataObject> CommonDataObjectCodecTreeNode<E> getStreamChild(final Class<E> childClass) {
+    public <E extends DataObject> BindingDataContainerCodecTreeNode<E> getStreamChild(final Class<E> childClass) {
         return delegate().getStreamChild(childClass);
     }
 }
index d0e2f04db38233ca2e7b478cf789fffd46db72ab..65394235dd76559feeb04737c84ac930ce0d81c9 100644 (file)
@@ -43,8 +43,8 @@ import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
 public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
         extends CommonDataObjectCodecContext<D, T>
         permits AugmentationCodecContext, DataObjectCodecContext {
-    private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byBindingArgClass;
-    private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byStreamClass;
+    private final ImmutableMap<Class<?>, DataContainerPrototype<?, ?>> byBindingArgClass;
+    private final ImmutableMap<Class<?>, DataContainerPrototype<?, ?>> byStreamClass;
     private final ImmutableMap<NodeIdentifier, CodecContextSupplier> byYang;
     private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
 
@@ -64,7 +64,7 @@ public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject
     }
 
     @Override
-    CommonDataObjectCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
+    DataContainerPrototype<?, ?> streamChildPrototype(final Class<?> childClass) {
         return byStreamClass.get(childClass);
     }
 
@@ -75,27 +75,17 @@ public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject
         final var context = childNonNull(pathChildPrototype(argType), argType,
             "Class %s is not valid child of %s", argType, getBindingClass())
             .getCodecContext();
-        if (context instanceof ChoiceCodecContext<?> choice) {
-            choice.addYangPathArgument(arg, builder);
-
-            final var caseType = arg.getCaseType();
-            final var type = arg.getType();
-            final DataContainerCodecContext<?, ?, ?> caze;
-            if (caseType.isPresent()) {
-                // Non-ambiguous addressing this should not pose any problems
-                caze = choice.getStreamChild(caseType.orElseThrow());
-            } else {
-                caze = choice.getCaseByChildClass(type);
-            }
-
-            caze.addYangPathArgument(arg, builder);
-            return caze.bindingPathArgumentChild(arg, builder);
-        }
         context.addYangPathArgument(arg, builder);
-        return context;
+        if (context instanceof CommonDataObjectCodecContext<?, ?> dataObject) {
+            return dataObject;
+        } else if (context instanceof ChoiceCodecContext<?> choice) {
+            return choice.bindingPathArgumentChild(arg, builder);
+        } else {
+            throw new IllegalStateException("Unhandled context " + context);
+        }
     }
 
-    @Nullable CommonDataObjectCodecPrototype<?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
+    @Nullable DataContainerPrototype<?, ?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
         return byBindingArgClass.get(argType);
     }
 
index 70b450387f2b3ad64f2c29a82434b3d1d3b61ac2..dccf09ae3ca6f02174d0c9020c80e7639a2a35cd 100644 (file)
@@ -27,7 +27,7 @@ final class AugmentationCodecPrototype extends CommonDataObjectCodecPrototype<Au
     }
 
     @Override
-    NodeIdentifier getYangArg() {
+    NodeIdentifier yangArg() {
         throw new UnsupportedOperationException("Augmentation does not have PathArgument address");
     }
 
index 1547e7d0a9654c430d69b7f8c3abc5851ad3fd68..2b351405b471bef7c1619eda01406cfe9c6e10ef 100644 (file)
@@ -167,7 +167,7 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
                     return list.keyType() == null ? new ListCodecContext<>(key, list, BindingCodecContext.this)
                         : MapCodecContext.of(key, list, BindingCodecContext.this);
                 } else if (childSchema instanceof ChoiceRuntimeType choice) {
-                    return new ChoiceCodecContext<>(key, choice, BindingCodecContext.this);
+                    return new ChoiceCodecContext<>(key.asSubclass(ChoiceIn.class), choice, BindingCodecContext.this);
                 } else if (childSchema == null) {
                     throw DataContainerCodecContext.childNullException(context, key, "%s is not top-level item.", key);
                 } else {
@@ -753,10 +753,10 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
 
     @Override
     @SuppressWarnings("unchecked")
-    public <E extends DataObject> CommonDataObjectCodecContext<E, ?> getStreamChild(final Class<E> childClass) {
+    public <E extends DataObject> DataContainerCodecContext<E, ?, ?> getStreamChild(final Class<E> childClass) {
         final var result = Notification.class.isAssignableFrom(childClass) ? getNotificationContext(childClass)
             : getOrRethrow(childrenByClass, childClass);
-        return (CommonDataObjectCodecContext<E, ?>) result;
+        return (DataContainerCodecContext<E, ?, ?>) result;
     }
 
     @Override
index 8e0adc74b7e6bb314a16b24288cb9330a61f0e78..8454de4f25a283c49e07ded78f501b23ad5e5429 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
-import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -17,25 +16,23 @@ import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.MultimapBuilder.SetMultimapBuilder;
 import com.google.common.collect.Multimaps;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingChoiceCodecTreeNode;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType;
-import org.opendaylight.yangtools.yang.binding.BindingObject;
+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.Item;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.binding.contract.Naming;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -94,8 +91,9 @@ import org.slf4j.LoggerFactory;
  * ambiguous reference and issue warn once when they are encountered -- tracking warning information in
  * {@link #ambiguousByCaseChildWarnings}.
  */
-final class ChoiceCodecContext<D extends DataObject> extends CommonDataObjectCodecContext<D, ChoiceRuntimeType>
-        implements BindingDataObjectCodecTreeNode<D> {
+final class ChoiceCodecContext<T extends ChoiceIn<?>>
+        extends DataContainerCodecContext<T, ChoiceRuntimeType, ChoiceCodecPrototype<T>>
+        implements BindingChoiceCodecTreeNode<T> {
     private static final Logger LOG = LoggerFactory.getLogger(ChoiceCodecContext.class);
 
     private final ImmutableListMultimap<Class<?>, CommonDataObjectCodecPrototype<?>> ambiguousByCaseChildClass;
@@ -104,11 +102,12 @@ final class ChoiceCodecContext<D extends DataObject> extends CommonDataObjectCod
     private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byClass;
     private final Set<Class<?>> ambiguousByCaseChildWarnings;
 
-    ChoiceCodecContext(final Class<D> cls, final ChoiceRuntimeType type, final CodecContextFactory factory) {
-        this(new ChoiceCodecPrototype(Item.of(cls), type, factory));
+    ChoiceCodecContext(final Class<T> javaClass, final ChoiceRuntimeType runtimeType,
+            final CodecContextFactory contextFactory) {
+        this(new ChoiceCodecPrototype<>(contextFactory, runtimeType, javaClass));
     }
 
-    ChoiceCodecContext(final ChoiceCodecPrototype prototype) {
+    ChoiceCodecContext(final ChoiceCodecPrototype<T> prototype) {
         super(prototype);
         final var byYangCaseChildBuilder = new HashMap<NodeIdentifier, CaseCodecPrototype>();
         final var byClassBuilder = new HashMap<Class<?>, CommonDataObjectCodecPrototype<?>>();
@@ -222,54 +221,43 @@ final class ChoiceCodecContext<D extends DataObject> extends CommonDataObjectCod
     }
 
     @Override
-    CaseCodecPrototype yangChildSupplier(final NodeIdentifier arg) {
+    CodecContextSupplier yangChildSupplier(final NodeIdentifier arg) {
         return byYangCaseChild.get(arg);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
-    @SuppressFBWarnings(value = "NP_NONNULL_RETURN_VIOLATION", justification = "See FIXME below")
-    public D deserialize(final NormalizedNode data) {
-        final var casted = checkDataArgument(ChoiceNode.class, data);
-        final var first = Iterables.getFirst(casted.body(), null);
-
-        if (first == null) {
-            // FIXME: this needs to be sorted out
+    protected T deserializeObject(final NormalizedNode normalizedNode) {
+        final var casted = checkDataArgument(ChoiceNode.class, normalizedNode);
+        final var it = casted.body().iterator();
+        if (!it.hasNext()) {
+            // FIXME: can this reasonably happen? Empty choice nodes do not have semantics, or do they?
             return null;
         }
-        final var caze = byYangCaseChild.get(first.name());
-        return ((CaseCodecContext<D>) caze.getCodecContext()).deserialize(data);
-    }
 
-    @Override
-    public NormalizedNode serialize(final D data) {
-        return serializeImpl(data);
-    }
-
-    @Override
-    protected Object deserializeObject(final NormalizedNode normalizedNode) {
-        return deserialize(normalizedNode);
+        final var childName = it.next().name();
+        final var caze = childNonNull(byYangCaseChild.get(childName), childName, "%s is not a valid case child of %s",
+            childName, this);
+        return (T) caze.getCodecContext().deserializeObject(casted);
     }
 
     @Override
-    public PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
-        checkArgument(getDomPathArgument().equals(arg));
-        return null;
-    }
-
-    @Override
-    public YangInstanceIdentifier.PathArgument serializePathArgument(final PathArgument arg) {
-        // FIXME: check for null, since binding container is null.
-        return getDomPathArgument();
-    }
+    public CommonDataObjectCodecContext<?, ?> bindingPathArgumentChild(final PathArgument arg,
+            final List<YangInstanceIdentifier.PathArgument> builder) {
+        final var caseType = arg.getCaseType();
+        final var type = arg.getType();
+        final DataContainerCodecContext<?, ?, ?> caze;
+        if (caseType.isPresent()) {
+            // Non-ambiguous addressing this should not pose any problems
+            caze = getStreamChild(caseType.orElseThrow());
+        } else {
+            caze = getCaseByChildClass(type);
+        }
 
-    @Override
-    public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
-            final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
-        return createCachingCodec(this, cacheSpecifier);
+        caze.addYangPathArgument(arg, builder);
+        return caze.bindingPathArgumentChild(arg, builder);
     }
 
-    DataContainerCodecContext<?, ?, ?> getCaseByChildClass(final @NonNull Class<? extends DataObject> type) {
+    private DataContainerCodecContext<?, ?, ?> getCaseByChildClass(final @NonNull Class<? extends DataObject> type) {
         var result = byCaseChildClass.get(type);
         if (result == null) {
             // We have not found an unambiguous result, try ambiguous ones
@@ -282,13 +270,13 @@ final class ChoiceCodecContext<D extends DataObject> extends CommonDataObjectCod
                         Ambiguous reference {} to child of {} resolved to {}, the first case in {} This mapping is \
                         not guaranteed to be stable and is subject to variations based on runtime circumstances. \
                         Please see the stack trace for hints about the source of ambiguity.""",
-                        type, bindingArg(), result.javaClass(),
+                        type, getBindingClass(), result.javaClass(),
                         Lists.transform(inexact, CommonDataObjectCodecPrototype::javaClass), new Throwable());
                 }
             }
         }
 
-        return childNonNull(result, type, "Class %s is not child of any cases for %s", type, bindingArg())
+        return childNonNull(result, type, "Class %s is not child of any cases for %s", type, getBindingClass())
             .getCodecContext();
     }
 
index 7fc1f3cca996e21e7bc85f71bdb89645f8a39944..f5ac3d670281375ecb2f900ec64b2109d906162f 100644 (file)
@@ -7,20 +7,40 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 
 /**
  * A prototype for {@link ChoiceCodecContext}.
  */
-final class ChoiceCodecPrototype extends DataObjectCodecPrototype<ChoiceRuntimeType> {
-    ChoiceCodecPrototype(final Item<?> item, final ChoiceRuntimeType type, final CodecContextFactory factory) {
-        super(item, NodeIdentifier.create(type.statement().argument()), type, factory);
+final class ChoiceCodecPrototype<T extends ChoiceIn<?>>
+        extends DataContainerPrototype<ChoiceCodecContext<T>, ChoiceRuntimeType> {
+    private final @NonNull NodeIdentifier yangArg;
+    private final @NonNull Class<T> javaClass;
+
+    ChoiceCodecPrototype(final CodecContextFactory contextFactory, final ChoiceRuntimeType runtimeType,
+            final Class<T> javaClass) {
+        super(contextFactory, runtimeType);
+        this.javaClass = requireNonNull(javaClass);
+        yangArg = NodeIdentifier.create(runtimeType.statement().argument());
+    }
+
+    @Override
+    Class<T> javaClass() {
+        return javaClass;
+    }
+
+    @Override
+    NodeIdentifier yangArg() {
+        return yangArg;
     }
 
     @Override
-    ChoiceCodecContext<?> createInstance() {
+    ChoiceCodecContext<T> createInstance() {
         return new ChoiceCodecContext<>(this);
     }
 }
index f5416d24f54fbebefb5ef001f3cd5238d2c195f7..219ef7ca84654ec37ea48455658b159c2158c3f7 100644 (file)
@@ -12,7 +12,6 @@ import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 
 /**
  * Base implementation of {@link CommonDataObjectCodecTreeNode}.
@@ -20,22 +19,11 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 abstract sealed class CommonDataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
         extends DataContainerCodecContext<D, T, CommonDataObjectCodecPrototype<T>>
         implements CommonDataObjectCodecTreeNode<D>
-        permits AbstractDataObjectCodecContext, ChoiceCodecContext {
+        permits AbstractDataObjectCodecContext {
     CommonDataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype) {
         super(prototype);
     }
 
-    @SuppressWarnings("unchecked")
-    @Override
-    public final Class<D> getBindingClass() {
-        return Class.class.cast(prototype().javaClass());
-    }
-
-    @Override
-    protected NodeIdentifier getDomPathArgument() {
-        return prototype().getYangArg();
-    }
-
     /**
      * Returns deserialized Binding Path Argument from YANG instance identifier.
      */
index 49b863b0061b43ca258ba98311c0a6cb065ccaf0..0962fea4adc133c33f9fa920b9e141c80a3a82cc 100644 (file)
@@ -13,7 +13,6 @@ import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 
 /**
  * Common superclass for {@link DataObjectCodecPrototype} and {@link AugmentationCodecPrototype}.
@@ -38,6 +37,4 @@ abstract sealed class CommonDataObjectCodecPrototype<R extends CompositeRuntimeT
     final @NonNull Item<?> getBindingArg() {
         return bindingArg;
     }
-
-    abstract @NonNull NodeIdentifier getYangArg();
 }
index cbb3a974196eb3e9289d3ab719c19eb36dfb7a1c..c799bbbbac0b49285d0a27a09020b66927a3e834 100644 (file)
@@ -26,6 +26,7 @@ import org.opendaylight.mdsal.binding.runtime.api.ContainerLikeRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.ContainerRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.ListRuntimeType;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
 import org.opendaylight.yangtools.yang.binding.contract.Naming;
@@ -42,8 +43,8 @@ final class DataContainerAnalysis<R extends CompositeRuntimeType> {
     private static final Logger LOG = LoggerFactory.getLogger(DataContainerAnalysis.class);
 
     // Needed for DataContainerCodecContext
-    final @NonNull ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byStreamClass;
-    final @NonNull ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byBindingArgClass;
+    final @NonNull ImmutableMap<Class<?>, DataContainerPrototype<?, ?>> byStreamClass;
+    final @NonNull ImmutableMap<Class<?>, DataContainerPrototype<?, ?>> byBindingArgClass;
     final @NonNull ImmutableMap<NodeIdentifier, CodecContextSupplier> byYang;
     final @NonNull ImmutableMap<String, ValueNodeCodecContext> leafNodes;
 
@@ -73,8 +74,8 @@ final class DataContainerAnalysis<R extends CompositeRuntimeType> {
         }
         leafNodes = leafBuilder.build();
 
-        final var byBindingArgClassBuilder = new HashMap<Class<?>, CommonDataObjectCodecPrototype<?>>();
-        final var byStreamClassBuilder = new HashMap<Class<?>, CommonDataObjectCodecPrototype<?>>();
+        final var byBindingArgClassBuilder = new HashMap<Class<?>, DataContainerPrototype<?, ?>>();
+        final var byStreamClassBuilder = new HashMap<Class<?>, DataContainerPrototype<?, ?>>();
         final var daoPropertiesBuilder = new HashMap<Class<?>, PropertyInfo>();
         for (var childDataObj : clsToMethod.entrySet()) {
             final var method = childDataObj.getValue();
@@ -91,14 +92,11 @@ final class DataContainerAnalysis<R extends CompositeRuntimeType> {
 
             final var childProto = getChildPrototype(runtimeType, factory, itemFactory, retClass);
             byStreamClassBuilder.put(childProto.javaClass(), childProto);
-            byYangBuilder.put(childProto.getYangArg(), childProto);
-
-            // FIXME: It really feels like we should be specializing DataContainerCodecPrototype so as to ditch
-            //        createInstance() and then we could do an instanceof check instead.
-            if (childProto.runtimeType() instanceof ChoiceRuntimeType) {
-                final var choice = (ChoiceCodecContext<?>) childProto.getCodecContext();
-                for (var cazeChild : choice.getCaseChildrenClasses()) {
-                    byBindingArgClassBuilder.put(cazeChild, childProto);
+            byYangBuilder.put(childProto.yangArg(), childProto);
+
+            if (childProto instanceof ChoiceCodecPrototype<?> choiceProto) {
+                for (var cazeChild : choiceProto.getCodecContext().getCaseChildrenClasses()) {
+                    byBindingArgClassBuilder.put(cazeChild, choiceProto);
                 }
             }
         }
@@ -127,7 +125,7 @@ final class DataContainerAnalysis<R extends CompositeRuntimeType> {
         daoProperties = ImmutableMap.copyOf(daoPropertiesBuilder);
     }
 
-    private static @NonNull CommonDataObjectCodecPrototype<?> getChildPrototype(final CompositeRuntimeType type,
+    private static @NonNull DataContainerPrototype<?, ?> getChildPrototype(final CompositeRuntimeType type,
             final CodecContextFactory factory, final CodecItemFactory itemFactory,
             final Class<? extends DataContainer> childClass) {
         final var child = type.bindingChild(JavaTypeName.create(childClass));
@@ -135,6 +133,11 @@ final class DataContainerAnalysis<R extends CompositeRuntimeType> {
             throw DataContainerCodecContext.childNullException(factory.getRuntimeContext(), childClass,
                 "Node %s does not have child named %s", type, childClass);
         }
+
+        if (child instanceof ChoiceRuntimeType choice) {
+            return new ChoiceCodecPrototype<>(factory, choice, childClass.asSubclass(ChoiceIn.class));
+        }
+
         final var item = itemFactory.createItem(childClass, child.statement());
         if (child instanceof ContainerLikeRuntimeType containerLike) {
             if (child instanceof ContainerRuntimeType container
@@ -145,14 +148,11 @@ final class DataContainerAnalysis<R extends CompositeRuntimeType> {
         } else if (child instanceof ListRuntimeType list) {
             return list.keyType() != null ? new MapCodecPrototype(item, list, factory)
                 : new ListCodecPrototype(item, list, factory);
-        } else if (child instanceof ChoiceRuntimeType choice) {
-            return new ChoiceCodecPrototype(item, choice, factory);
         } else {
             throw new UnsupportedOperationException("Unhandled type " + child);
         }
     }
 
-
     // FIXME: MDSAL-780: these methods perform analytics using java.lang.reflect to acquire the basic shape of the
     //                   class. This is not exactly AOT friendly, as most of the information should be provided by
     //                   CompositeRuntimeType.
index a1fd1bfc29658831fe40508ca921dbf54ba0b701..ebd628c0ff1c5c4fcdb41cf482a2c4d1df95a573 100644 (file)
@@ -65,7 +65,7 @@ import org.slf4j.LoggerFactory;
 abstract sealed class DataContainerCodecContext<D extends DataContainer, R extends CompositeRuntimeType,
         P extends DataContainerPrototype<?, R>>
         extends CodecContext implements BindingDataContainerCodecTreeNode<D>
-        permits CommonDataObjectCodecContext {
+        permits ChoiceCodecContext, CommonDataObjectCodecContext {
     private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecContext.class);
     private static final VarHandle EVENT_STREAM_SERIALIZER;
 
@@ -95,6 +95,18 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, R exten
         return prototype;
     }
 
+    @Override
+    @SuppressWarnings("unchecked")
+    public final Class<D> getBindingClass() {
+        return (Class<D>) prototype().javaClass();
+    }
+
+    // overridden in AugmentationCodecContext
+    @Override
+    protected NodeIdentifier getDomPathArgument() {
+        return prototype.yangArg();
+    }
+
     @Override
     public final ChildAddressabilitySummary getChildAddressabilitySummary() {
         return childAddressabilitySummary;
@@ -117,12 +129,8 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, R exten
     abstract @Nullable CodecContextSupplier yangChildSupplier(@NonNull NodeIdentifier arg);
 
     @Override
-    public CommonDataObjectCodecContext<?, ?> bindingPathArgumentChild(final PathArgument arg,
-            final List<YangInstanceIdentifier.PathArgument> builder) {
-        final var child = getStreamChild(arg.getType());
-        child.addYangPathArgument(arg, builder);
-        return child;
-    }
+    public abstract CommonDataObjectCodecContext<?, ?> bindingPathArgumentChild(PathArgument arg,
+        List<YangInstanceIdentifier.PathArgument> builder);
 
     /**
      * Serializes supplied Binding Path Argument and adds all necessary YANG instance identifiers to supplied list.
@@ -144,19 +152,19 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, R exten
     }
 
     @Override
-    public final <C extends DataObject> CommonDataObjectCodecContext<C, ?> getStreamChild(final Class<C> childClass) {
+    public final <C extends DataObject> DataContainerCodecContext<C, ?, ?> getStreamChild(final Class<C> childClass) {
         return childNonNull(streamChild(childClass), childClass,
             "Child %s is not valid child of %s", getBindingClass(), childClass);
     }
 
     @SuppressWarnings("unchecked")
     @Override
-    public final <C extends DataObject> CommonDataObjectCodecContext<C, ?> streamChild(final Class<C> childClass) {
+    public final <C extends DataObject> DataContainerCodecContext<C, ?, ?> streamChild(final Class<C> childClass) {
         final var childProto = streamChildPrototype(requireNonNull(childClass));
-        return childProto == null ? null : (CommonDataObjectCodecContext<C, ?>) childProto.getCodecContext();
+        return childProto == null ? null : (DataContainerCodecContext<C, ?, ?>) childProto.getCodecContext();
     }
 
-    abstract @Nullable CommonDataObjectCodecPrototype<?> streamChildPrototype(@NonNull Class<?> childClass);
+    abstract @Nullable DataContainerPrototype<?, ?> streamChildPrototype(@NonNull Class<?> childClass);
 
     @Override
     public String toString() {
index 9fbc08ec44586ae1582565155a41d3ab57eab828..e8393140a91f0e30b56bdd9c1dc1f5a67da52ce3 100644 (file)
@@ -12,6 +12,7 @@ import static java.util.Objects.requireNonNull;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 
 /**
  * A prototype for codecs dealing with {@link DataContainer}s.
@@ -19,8 +20,9 @@ import org.opendaylight.yangtools.yang.binding.DataContainer;
  * @param <C> {@link CodecContext} type
  * @param <R> {@link CompositeRuntimeType} type
  */
-abstract sealed class DataContainerPrototype<C extends CodecContext, R extends CompositeRuntimeType>
-        extends LazyCodecContextSupplier<C> permits CommonDataObjectCodecPrototype {
+abstract sealed class DataContainerPrototype<C extends DataContainerCodecContext<?, R, ?>,
+        R extends CompositeRuntimeType>
+        extends LazyCodecContextSupplier<C> permits ChoiceCodecPrototype, CommonDataObjectCodecPrototype {
     private final @NonNull CodecContextFactory contextFactory;
     private final @NonNull R runtimeType;
 
@@ -53,4 +55,6 @@ abstract sealed class DataContainerPrototype<C extends CodecContext, R extends C
      * @return the generated binding class this prototype corresponds to
      */
     abstract @NonNull Class<? extends DataContainer> javaClass();
+
+    abstract @NonNull NodeIdentifier yangArg();
 }
index 5ba4e8fd9609a5c6c6ab3c4be97993ce728eae47..8a45102c3b03cb5f08f6bafe6bc6d9cea82c0702 100644 (file)
@@ -150,13 +150,13 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
     }
 
     @Override
-    final CommonDataObjectCodecPrototype<?> pathChildPrototype(final Class<? extends DataObject> argType) {
+    final DataContainerPrototype<?, ?> pathChildPrototype(final Class<? extends DataObject> argType) {
         final var child = super.pathChildPrototype(argType);
         return child != null ? child : augmentToPrototype.get(argType);
     }
 
     @Override
-    final CommonDataObjectCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
+    final DataContainerPrototype<?, ?> streamChildPrototype(final Class<?> childClass) {
         final var child = super.streamChildPrototype(childClass);
         if (child == null && Augmentation.class.isAssignableFrom(childClass)) {
             return getAugmentationProtoByClass(childClass);
index a414c2972cd27e2bd6a65db661404cd0462decf7..e53354f09d2618902efa030cd997742c3ea8c9b3 100644 (file)
@@ -16,7 +16,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 
 abstract sealed class DataObjectCodecPrototype<T extends CompositeRuntimeType> extends CommonDataObjectCodecPrototype<T>
-        permits CaseCodecPrototype, ChoiceCodecPrototype, ContainerLikeCodecPrototype, ListCodecPrototype,
+        permits CaseCodecPrototype, ContainerLikeCodecPrototype, ListCodecPrototype,
                 NotificationCodecContext.Prototype {
     private final @NonNull NodeIdentifier yangArg;
 
@@ -34,7 +34,7 @@ abstract sealed class DataObjectCodecPrototype<T extends CompositeRuntimeType> e
     }
 
     @Override
-    final NodeIdentifier getYangArg() {
+    final NodeIdentifier yangArg() {
         return yangArg;
     }
 }