Split out BindingDataContainerCodecTreeNode 27/106727/16
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 27 Jun 2023 16:23:02 +0000 (18:23 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 29 Jun 2023 12:08:17 +0000 (14:08 +0200)
DataContainer contract is well-known and used all over the place. Split
it out so it can be implemented separately. This also reworks codec's
internal type hierarchy to make place for
BindingDataContainerCodecTreeNodes which are not DataObjects.

JIRA: MDSAL-805
Change-Id: I225bb02f2c2a1d9caa6e435a4c2ef8f8f62574bd
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
14 files changed:
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataContainerCodecTreeNode.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/CommonDataObjectCodecTreeNode.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/CodecDataObjectAnalysis.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecContext.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecPrototype.java [new file with mode: 0644]
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/DataContainerCodecPrototype.java [deleted file]
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
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/RootCodecContext.java

diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataContainerCodecTreeNode.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataContainerCodecTreeNode.java
new file mode 100644 (file)
index 0000000..9d48250
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2023 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 java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BindingObject;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * A {@link BindingObjectCodecTreeNode} which corresponds to a {@link DataContainer} construct.
+ *
+ * @param <T> DataContainer type
+ */
+public interface BindingDataContainerCodecTreeNode<T extends BindingObject & DataContainer>
+        extends BindingObjectCodecTreeNode<T>, BindingDataObjectCodecTreeParent<Empty> {
+    /**
+     * Returns binding class of interface which represents API of current schema node. The result is same as invoking
+     * {@link DataContainer#implementedInterface()} on instance of data.
+     *
+     * @return interface which defines API of binding representation of data.
+     */
+    @Override
+    Class<T> getBindingClass();
+
+    /**
+     * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case,
+     * one must issue getChild(ChoiceClass).getChild(CaseClass).
+     *
+     * <p>
+     * This method differs from {@link #getStreamChild(Class)}, that is less strict for interfaces representing
+     * augmentation and cases, that may return {@link BindingCodecTreeNode} even if augmentation interface containing
+     * same data was supplied and does not represent augmentation of this node.
+     *
+     * @param childClass Child class by Binding Stream navigation
+     * @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);
+
+    default <A extends Augmentation<?>> @Nullable BindingAugmentationCodecTreeNode<A> streamAugmentation(
+            final @NonNull Class<A> childClass) {
+        final var result = streamChild(childClass);
+        if (result instanceof BindingAugmentationCodecTreeNode) {
+            return (BindingAugmentationCodecTreeNode<A>) result;
+        } else if (result == null) {
+            return null;
+        } else {
+            throw new IllegalArgumentException(
+                "Child " + childClass.getName() + " results in non-Augmentation " + result);
+        }
+    }
+
+    default <E extends DataObject> @Nullable BindingDataObjectCodecTreeNode<E> streamDataObject(
+            final @NonNull Class<E> childClass) {
+        final var result = streamChild(childClass);
+        if (result instanceof BindingDataObjectCodecTreeNode) {
+            return (BindingDataObjectCodecTreeNode<E>) result;
+        } else if (result == null) {
+            return null;
+        } else {
+            throw new IllegalArgumentException(
+                "Child " + childClass.getName() + " results in non-DataObject " + result);
+        }
+    }
+
+    /**
+     * Returns nested node context using supplied YANG Instance Identifier.
+     *
+     * @param child
+     *            Yang Instance Identifier Argument
+     * @return Context of child
+     * @throws IllegalArgumentException
+     *             If supplied argument does not represent valid child.
+     */
+    @NonNull BindingCodecTreeNode yangPathArgumentChild(YangInstanceIdentifier.@NonNull PathArgument child);
+
+    /**
+     * Returns nested node context using supplied Binding Instance Identifier and adds YANG instance identifiers to
+     * the supplied list.
+     *
+     * @param arg
+     *            Binding Instance Identifier Argument
+     * @param builder
+     *            Mutable instance of list, which is appended by YangInstanceIdentifiers
+     *            as tree is walked. Use null if such side-product is not needed.
+     * @return Context of child
+     * @throws IllegalArgumentException
+     *             If supplied argument does not represent valid child.
+     */
+    @NonNull CommonDataObjectCodecTreeNode<?> bindingPathArgumentChild(InstanceIdentifier.@NonNull PathArgument arg,
+            @Nullable List<YangInstanceIdentifier.PathArgument> builder);
+
+    /**
+     * Return a summary of addressability of potential children. Binding specification does not allow all DOM tree
+     * elements to be directly addressed, which means some recursive tree operations, like data tree changes do not
+     * have a one-to-one mapping from DOM to binding in all cases. This method provides an optimization hint to guide
+     * translation of data structures, allowing for fast paths when all children are known to either be addressable
+     * or non-addressable.
+     *
+     * @return Summary children addressability.
+     */
+    @NonNull ChildAddressabilitySummary getChildAddressabilitySummary();
+
+    /**
+     * Enumeration of possible addressability attribute of all children.
+     */
+    enum ChildAddressabilitySummary {
+        /**
+         * All children are addressable.
+         */
+        ADDRESSABLE,
+        /**
+         * All children are non-addressable, including the case when this node does not have any children.
+         */
+        UNADDRESSABLE,
+        /**
+         * Mixed children, some are addressable and some are not.
+         */
+        MIXED
+    }
+}
index a0ffa04fa9379b4ba90edc0f210759e555d47f3c..e54b7d3a64fb7fb553c967934c1cd4164872ef0c 100644 (file)
@@ -8,13 +8,9 @@
 package org.opendaylight.mdsal.binding.dom.codec.api;
 
 import com.google.common.annotations.Beta;
-import java.util.List;
-import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.Empty;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 
 /**
@@ -23,92 +19,14 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
  *
  * @param <T> DataObject type
  */
-public interface CommonDataObjectCodecTreeNode<T extends DataObject>
-        extends BindingDataObjectCodecTreeParent<Empty>, BindingObjectCodecTreeNode<T> {
-    /**
-     * Returns binding class of interface which represents API of current schema node. The result is same as invoking
-     * {@link DataObject#implementedInterface()} on instance of data.
-     *
-     * @return interface which defines API of binding representation of data.
-     */
-    @Override
-    @NonNull Class<T> getBindingClass();
-
-    /**
-     * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case,
-     * one must issue getChild(ChoiceClass).getChild(CaseClass).
-     *
-     * <p>
-     * This method differs from {@link #getStreamChild(Class)}, that is less strict for interfaces representing
-     * augmentation and cases, that may return {@link BindingCodecTreeNode} even if augmentation interface containing
-     * same data was supplied and does not represent augmentation of this node.
-     *
-     * @param childClass Child class by Binding Stream navigation
-     * @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);
-
-    default <A extends Augmentation<?>> @Nullable BindingAugmentationCodecTreeNode<A> streamAugmentation(
-            final @NonNull Class<A> childClass) {
-        final var result = streamChild(childClass);
-        if (result instanceof BindingAugmentationCodecTreeNode) {
-            return (BindingAugmentationCodecTreeNode<A>) result;
-        } else if (result == null) {
-            return null;
-        } else {
-            throw new IllegalArgumentException(
-                "Child " + childClass.getName() + " results in non-Augmentation " + result);
-        }
-    }
-
-    default <E extends DataObject> @Nullable BindingDataObjectCodecTreeNode<E> streamDataObject(
-            final @NonNull Class<E> childClass) {
-        final var result = streamChild(childClass);
-        if (result instanceof BindingDataObjectCodecTreeNode) {
-            return (BindingDataObjectCodecTreeNode<E>) result;
-        } else if (result == null) {
-            return null;
-        } else {
-            throw new IllegalArgumentException(
-                "Child " + childClass.getName() + " results in non-DataObject " + result);
-        }
-    }
-
-    /**
-     * Returns nested node context using supplied YANG Instance Identifier.
-     *
-     * @param child
-     *            Yang Instance Identifier Argument
-     * @return Context of child
-     * @throws IllegalArgumentException
-     *             If supplied argument does not represent valid child.
-     */
-    @NonNull BindingCodecTreeNode yangPathArgumentChild(YangInstanceIdentifier.@NonNull PathArgument child);
-
-    /**
-     * Returns nested node context using supplied Binding Instance Identifier and adds YANG instance identifiers to
-     * the supplied list.
-     *
-     * @param arg
-     *            Binding Instance Identifier Argument
-     * @param builder
-     *            Mutable instance of list, which is appended by YangInstanceIdentifiers
-     *            as tree is walked. Use null if such side-product is not needed.
-     * @return Context of child
-     * @throws IllegalArgumentException
-     *             If supplied argument does not represent valid child.
-     */
-    @NonNull CommonDataObjectCodecTreeNode<?> bindingPathArgumentChild(InstanceIdentifier.@NonNull PathArgument arg,
-            @Nullable List<YangInstanceIdentifier.PathArgument> builder);
-
+public interface CommonDataObjectCodecTreeNode<T extends DataObject> extends BindingDataContainerCodecTreeNode<T> {
     /**
      * Serializes path argument for current node.
      *
-     * @param arg Binding Path Argument, may be null if Binding Instance Identifier does not have
-     *        representation for current node (e.g. choice or case).
-     * @return Yang Path Argument, may be null if Yang Instance Identifier does not have
-     *         representation for current node (e.g. case).
+     * @param arg Binding Path Argument, may be null if Binding Instance Identifier does not have a representation for
+     *            current node (e.g. choice or case).
+     * @return Yang Path Argument, may be null if Yang Instance Identifier does not have representation for current node
+     *         (e.g. case).
      * @throws IllegalArgumentException If supplied {@code arg} is not valid.
      */
     @Beta
@@ -126,33 +44,4 @@ public interface CommonDataObjectCodecTreeNode<T extends DataObject>
     @Beta
     InstanceIdentifier.@Nullable PathArgument deserializePathArgument(
             YangInstanceIdentifier.@Nullable PathArgument arg);
-
-    /**
-     * Return a summary of addressability of potential children. Binding specification does not allow all DOM tree
-     * elements to be directly addressed, which means some recursive tree operations, like data tree changes do not
-     * have a one-to-one mapping from DOM to binding in all cases. This method provides an optimization hint to guide
-     * translation of data structures, allowing for fast paths when all children are known to either be addressable
-     * or non-addressable.
-     *
-     * @return Summary children addressability.
-     */
-    @NonNull ChildAddressabilitySummary getChildAddressabilitySummary();
-
-    /**
-     * Enumeration of possible addressability attribute of all children.
-     */
-    enum ChildAddressabilitySummary {
-        /**
-         * All children are addressable.
-         */
-        ADDRESSABLE,
-        /**
-         * All children are non-addressable, including the case when this node does not have any children.
-         */
-        UNADDRESSABLE,
-        /**
-         * Mixed children, some are addressable and some are not.
-         */
-        MIXED
-    }
 }
index 93ecf67bf58e51f2d25a630271e7cecc2ab0e95b..f5a31aa9825f760b07a8e5424be990ccc301f239 100644 (file)
@@ -44,15 +44,15 @@ import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
  * public is that it needs to be accessible by code generated at runtime.
  */
 public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
-        extends DataContainerCodecContext<D, T>
+        extends CommonDataObjectCodecContext<D, T>
         permits AugmentationCodecContext, DataObjectCodecContext {
-    private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
-    private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
+    private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byBindingArgClass;
+    private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byStreamClass;
     private final ImmutableMap<NodeIdentifier, CodecContextSupplier> byYang;
     private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
     private final MethodHandle proxyConstructor;
 
-    AbstractDataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
+    AbstractDataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype,
             final CodecDataObjectAnalysis<T> analysis) {
         super(prototype);
         byBindingArgClass = analysis.byBindingArgClass;
@@ -69,24 +69,24 @@ public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject
     }
 
     @Override
-    public final <C extends DataObject> DataContainerCodecContext<C, ?> getStreamChild(final Class<C> childClass) {
+    public final <C extends DataObject> CommonDataObjectCodecContext<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> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
+    public final <C extends DataObject> CommonDataObjectCodecContext<C, ?> streamChild(final Class<C> childClass) {
         final var childProto = streamChildPrototype(childClass);
-        return childProto == null ? null : (DataContainerCodecContext<C, ?>) childProto.get();
+        return childProto == null ? null : (CommonDataObjectCodecContext<C, ?>) childProto.get();
     }
 
-    @Nullable DataContainerCodecPrototype<?> streamChildPrototype(final @NonNull Class<?> childClass) {
+    @Nullable CommonDataObjectCodecPrototype<?> streamChildPrototype(final @NonNull Class<?> childClass) {
         return byStreamClass.get(childClass);
     }
 
     @Override
-    public final DataContainerCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
+    public final CommonDataObjectCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
             final List<PathArgument> builder) {
         final var argType = arg.getType();
         final var context = childNonNull(pathChildPrototype(argType), argType,
@@ -112,7 +112,7 @@ public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject
         return context;
     }
 
-    @Nullable DataContainerCodecPrototype<?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
+    @Nullable CommonDataObjectCodecPrototype<?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
         return byBindingArgClass.get(argType);
     }
 
index d807602db8a8169643cf13a5cfbaaa52f5468d50..6ef9375172f46a06b38c3402e12a1756568d1a62 100644 (file)
@@ -17,7 +17,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 
-final class AugmentationCodecPrototype extends DataContainerCodecPrototype<AugmentRuntimeType> {
+final class AugmentationCodecPrototype extends CommonDataObjectCodecPrototype<AugmentRuntimeType> {
     private final @NonNull ImmutableSet<NodeIdentifier> childArgs;
 
     @SuppressWarnings("unchecked")
index e0798e20988d2a6468d124e61e38a2999cabd14e..af1a6d84dc0d99174b2799ec0ad3fec7c6d25f4c 100644 (file)
@@ -112,8 +112,8 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
         BYTECODE_DIRECTORY = Strings.isNullOrEmpty(dir) ? null : new File(dir);
     }
 
-    private final LoadingCache<Class<?>, DataContainerStreamer<?>> streamers = CacheBuilder.newBuilder().build(
-        new CacheLoader<>() {
+    private final LoadingCache<Class<?>, DataContainerStreamer<?>> streamers = CacheBuilder.newBuilder()
+        .build(new CacheLoader<>() {
             @Override
             public DataContainerStreamer<?> load(final Class<?> key) throws ReflectiveOperationException {
                 final var streamer = DataContainerStreamerGenerator.generateStreamer(loader, BindingCodecContext.this,
@@ -122,8 +122,8 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
                 return (DataContainerStreamer<?>) instance.get(null);
             }
         });
-    private final LoadingCache<Class<?>, DataContainerSerializer> serializers = CacheBuilder.newBuilder().build(
-        new CacheLoader<>() {
+    private final LoadingCache<Class<?>, DataContainerSerializer> serializers = CacheBuilder.newBuilder()
+        .build(new CacheLoader<>() {
             @Override
             public DataContainerSerializer load(final Class<?> key) throws ExecutionException {
                 return new DataContainerSerializer(BindingCodecContext.this, streamers.get(key));
@@ -227,12 +227,7 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
             final List<YangInstanceIdentifier.PathArgument> builder) {
         DataContainerCodecContext<?, ?> current = root;
         for (var bindingArg : binding.getPathArguments()) {
-            final var next = current.bindingPathArgumentChild(bindingArg, builder);
-            if (next == null) {
-                throw new IllegalArgumentException("%s is not valid: parent %s does not have a child %s".formatted(
-                    binding, current.bindingArg(), bindingArg));
-            }
-            current = next;
+            current = current.bindingPathArgumentChild(bindingArg, builder);
         }
         return current;
     }
@@ -299,7 +294,7 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
                 // We do not add path argument for choice, since
                 // it is not supported by binding instance identifier.
                 currentNode = nextNode;
-            } else if (nextNode instanceof DataContainerCodecContext<?, ?> containerNode) {
+            } else if (nextNode instanceof CommonDataObjectCodecContext<?, ?> containerNode) {
                 if (bindingArguments != null) {
                     bindingArguments.add(containerNode.getBindingPathArgument(domArg));
                 }
index c73a47f6915d69062e38e5b10cb3dff03b083e06..243d0f795f0ee3bb1388de1f7449a394b7276f7b 100644 (file)
@@ -95,14 +95,14 @@ 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 DataContainerCodecContext<D, ChoiceRuntimeType>
+final class ChoiceCodecContext<D extends DataObject> extends CommonDataObjectCodecContext<D, ChoiceRuntimeType>
         implements BindingDataObjectCodecTreeNode<D> {
     private static final Logger LOG = LoggerFactory.getLogger(ChoiceCodecContext.class);
 
-    private final ImmutableListMultimap<Class<?>, DataContainerCodecPrototype<?>> ambiguousByCaseChildClass;
-    private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClass;
+    private final ImmutableListMultimap<Class<?>, CommonDataObjectCodecPrototype<?>> ambiguousByCaseChildClass;
+    private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byCaseChildClass;
     private final ImmutableMap<NodeIdentifier, CaseCodecPrototype> byYangCaseChild;
-    private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byClass;
+    private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byClass;
     private final Set<Class<?>> ambiguousByCaseChildWarnings;
 
     ChoiceCodecContext(final Class<D> cls, final ChoiceRuntimeType type, final CodecContextFactory factory) {
@@ -112,9 +112,9 @@ final class ChoiceCodecContext<D extends DataObject> extends DataContainerCodecC
     ChoiceCodecContext(final ChoiceCodecPrototype prototype) {
         super(prototype);
         final var byYangCaseChildBuilder = new HashMap<NodeIdentifier, CaseCodecPrototype>();
-        final var byClassBuilder = new HashMap<Class<?>, DataContainerCodecPrototype<?>>();
+        final var byClassBuilder = new HashMap<Class<?>, CommonDataObjectCodecPrototype<?>>();
         final var childToCase = SetMultimapBuilder.hashKeys().hashSetValues()
-            .<Class<?>, DataContainerCodecPrototype<?>>build();
+            .<Class<?>, CommonDataObjectCodecPrototype<?>>build();
 
         // Load case statements valid in this choice and keep track of their names
         final var choiceType = prototype.getType();
@@ -142,8 +142,8 @@ final class ChoiceCodecContext<D extends DataObject> extends DataContainerCodecC
         byYangCaseChild = ImmutableMap.copyOf(byYangCaseChildBuilder);
 
         // Move unambiguous child->case mappings to byCaseChildClass, removing them from childToCase
-        final var ambiguousByCaseBuilder = ImmutableListMultimap.<Class<?>, DataContainerCodecPrototype<?>>builder();
-        final var unambiguousByCaseBuilder = ImmutableMap.<Class<?>, DataContainerCodecPrototype<?>>builder();
+        final var ambiguousByCaseBuilder = ImmutableListMultimap.<Class<?>, CommonDataObjectCodecPrototype<?>>builder();
+        final var unambiguousByCaseBuilder = ImmutableMap.<Class<?>, CommonDataObjectCodecPrototype<?>>builder();
         for (var entry : Multimaps.asMap(childToCase).entrySet()) {
             final var cases = entry.getValue();
             if (cases.size() != 1) {
@@ -170,7 +170,7 @@ final class ChoiceCodecContext<D extends DataObject> extends DataContainerCodecC
          * This is required due property of binding specification, that if choice is in grouping schema path location is
          * lost, and users may use incorrect case class using copy builders.
          */
-        final var bySubstitutionBuilder = new HashMap<Class<?>, DataContainerCodecPrototype<?>>();
+        final var bySubstitutionBuilder = new HashMap<Class<?>, CommonDataObjectCodecPrototype<?>>();
         final var context = factory.getRuntimeContext();
         for (var caseType : context.getTypes().allCaseChildren(choiceType)) {
             final var caseName = caseType.getIdentifier();
@@ -209,16 +209,16 @@ final class ChoiceCodecContext<D extends DataObject> extends DataContainerCodecC
     }
 
     @Override
-    public <C extends DataObject> DataContainerCodecContext<C, ?> getStreamChild(final Class<C> childClass) {
+    public <C extends DataObject> CommonDataObjectCodecContext<C, ?> getStreamChild(final Class<C> childClass) {
         return childNonNull(streamChild(childClass), childClass,
             "Supplied class %s is not valid case in %s", childClass, bindingArg());
     }
 
     @SuppressWarnings("unchecked")
     @Override
-    public <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
+    public <C extends DataObject> CommonDataObjectCodecContext<C, ?> streamChild(final Class<C> childClass) {
         final var child = byClass.get(childClass);
-        return child == null ? null : (DataContainerCodecContext<C, ?>) child.get();
+        return child == null ? null : (CommonDataObjectCodecContext<C, ?>) child.get();
     }
 
     Iterable<Class<?>> getCaseChildrenClasses() {
@@ -227,7 +227,7 @@ final class ChoiceCodecContext<D extends DataObject> extends DataContainerCodecC
 
     @Override
     public CodecContext yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
-        final DataContainerCodecPrototype<?> cazeProto;
+        final CommonDataObjectCodecPrototype<?> cazeProto;
         if (arg instanceof NodeIdentifierWithPredicates) {
             cazeProto = byYangCaseChild.get(new NodeIdentifier(arg.getNodeType()));
         } else {
@@ -295,7 +295,7 @@ final class ChoiceCodecContext<D extends DataObject> extends DataContainerCodecC
                         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.getBindingClass(),
-                        Lists.transform(inexact, DataContainerCodecPrototype::getBindingClass), new Throwable());
+                        Lists.transform(inexact, CommonDataObjectCodecPrototype::getBindingClass), new Throwable());
                 }
             }
         }
index 954228a25e958730062757f9f55281e59c136d30..98bdbaf81f54478d3883d11ecb005ae5c3bc4d8e 100644 (file)
@@ -49,15 +49,15 @@ final class CodecDataObjectAnalysis<R extends CompositeRuntimeType> {
     private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class,
         AbstractDataObjectCodecContext.class, DataContainerNode.class);
 
-    final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
-    final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
+    final @NonNull ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byStreamClass;
+    final @NonNull ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byBindingArgClass;
     final @NonNull ImmutableMap<NodeIdentifier, CodecContextSupplier> byYang;
     final @NonNull ImmutableMap<String, ValueNodeCodecContext> leafNodes;
     final @NonNull Class<? extends CodecDataObject<?>> generatedClass;
     final @NonNull List<AugmentRuntimeType> possibleAugmentations;
     final @NonNull MethodHandle proxyConstructor;
 
-    CodecDataObjectAnalysis(final DataContainerCodecPrototype<R> prototype, final CodecItemFactory itemFactory,
+    CodecDataObjectAnalysis(final CommonDataObjectCodecPrototype<R> prototype, final CodecItemFactory itemFactory,
             final Method keyMethod) {
         // Preliminaries from prototype
         @SuppressWarnings("unchecked")
@@ -80,8 +80,8 @@ final class CodecDataObjectAnalysis<R extends CompositeRuntimeType> {
         }
         leafNodes = leafBuilder.build();
 
-        final var byBindingArgClassBuilder = new HashMap<Class<?>, DataContainerCodecPrototype<?>>();
-        final var byStreamClassBuilder = new HashMap<Class<?>, DataContainerCodecPrototype<?>>();
+        final var byBindingArgClassBuilder = new HashMap<Class<?>, CommonDataObjectCodecPrototype<?>>();
+        final var byStreamClassBuilder = new HashMap<Class<?>, CommonDataObjectCodecPrototype<?>>();
         final var daoProperties = new HashMap<Class<?>, PropertyInfo>();
         for (var childDataObj : clsToMethod.entrySet()) {
             final var method = childDataObj.getValue();
@@ -159,7 +159,7 @@ final class CodecDataObjectAnalysis<R extends CompositeRuntimeType> {
         proxyConstructor = ctor.asType(DATAOBJECT_TYPE);
     }
 
-    private static @NonNull DataContainerCodecPrototype<?> getChildPrototype(final CompositeRuntimeType type,
+    private static @NonNull CommonDataObjectCodecPrototype<?> getChildPrototype(final CompositeRuntimeType type,
             final CodecContextFactory factory, final CodecItemFactory itemFactory,
             final Class<? extends DataContainer> childClass) {
         final var child = type.bindingChild(JavaTypeName.create(childClass));
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecContext.java
new file mode 100644 (file)
index 0000000..6004a80
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2023 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.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+/**
+ * Base implementation of {@link CommonDataObjectCodecTreeNode}.
+ */
+abstract sealed class CommonDataObjectCodecContext<D extends DataObject, T extends RuntimeTypeContainer>
+        extends DataContainerCodecContext<D, T> implements CommonDataObjectCodecTreeNode<D>
+        permits AbstractDataObjectCodecContext, ChoiceCodecContext {
+    final @NonNull CommonDataObjectCodecPrototype<T> prototype;
+
+    CommonDataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype) {
+        super(prototype.getType());
+        this.prototype = requireNonNull(prototype);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public final Class<D> getBindingClass() {
+        return Class.class.cast(prototype.getBindingClass());
+    }
+
+    @Override
+    protected final CodecContextFactory factory() {
+        return prototype.getFactory();
+    }
+
+    @Override
+    protected final T type() {
+        return prototype.getType();
+    }
+
+    @Override
+    protected NodeIdentifier getDomPathArgument() {
+        return prototype.getYangArg();
+    }
+
+    /**
+     * Returns deserialized Binding Path Argument from YANG instance identifier.
+     */
+    protected PathArgument getBindingPathArgument(final YangInstanceIdentifier.PathArgument domArg) {
+        return bindingArg();
+    }
+
+    protected final PathArgument bindingArg() {
+        return prototype.getBindingArg();
+    }
+
+    protected final QNameModule namespace() {
+        return prototype.getNamespace();
+    }
+}
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecPrototype.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecPrototype.java
new file mode 100644 (file)
index 0000000..67de4fb
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2014 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.mdsal.binding.dom.codec.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+
+abstract sealed class CommonDataObjectCodecPrototype<T extends RuntimeTypeContainer> implements CodecContextSupplier
+        permits AugmentationCodecPrototype, DataObjectCodecPrototype {
+    private static final VarHandle INSTANCE;
+
+    static {
+        try {
+            INSTANCE = MethodHandles.lookup().findVarHandle(CommonDataObjectCodecPrototype.class,
+                "instance", CommonDataObjectCodecContext.class);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private final @NonNull T type;
+    private final @NonNull QNameModule namespace;
+    private final @NonNull CodecContextFactory factory;
+    private final @NonNull Item<?> bindingArg;
+
+    // multiple paths represent augmentation wrapper
+    // FIXME: this means it is either this or 'childArgs'
+
+    // Accessed via INSTANCE
+    @SuppressWarnings("unused")
+    private volatile CommonDataObjectCodecContext<?, T> instance;
+
+    CommonDataObjectCodecPrototype(final Item<?> bindingArg, final QNameModule namespace, final T type,
+            final CodecContextFactory factory) {
+        this.bindingArg = requireNonNull(bindingArg);
+        this.namespace = requireNonNull(namespace);
+        this.type = requireNonNull(type);
+        this.factory = requireNonNull(factory);
+    }
+
+    final @NonNull T getType() {
+        return type;
+    }
+
+    final @NonNull QNameModule getNamespace() {
+        return namespace;
+    }
+
+    final @NonNull CodecContextFactory getFactory() {
+        return factory;
+    }
+
+    final @NonNull Class<?> getBindingClass() {
+        return bindingArg.getType();
+    }
+
+    final @NonNull Item<?> getBindingArg() {
+        return bindingArg;
+    }
+
+    abstract @NonNull NodeIdentifier getYangArg();
+
+    @Override
+    public final CommonDataObjectCodecContext<?, T> get() {
+        final var existing = (CommonDataObjectCodecContext<?, T>) INSTANCE.getAcquire(this);
+        return existing != null ? existing : loadInstance();
+    }
+
+    private @NonNull CommonDataObjectCodecContext<?, T> loadInstance() {
+        final var tmp = createInstance();
+        final var witness = (CommonDataObjectCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
+        return witness == null ? tmp : witness;
+    }
+
+    // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
+    abstract @NonNull CommonDataObjectCodecContext<?, T> createInstance();
+}
index b7ed9619f63d3d8780a38fbc0b23009c3089a81d..2caa3667acb2815b8d6c39a72523f317e162c355 100644 (file)
@@ -26,10 +26,10 @@ import java.util.Optional;
 import java.util.Set;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataContainerCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCodec;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
-import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
 import org.opendaylight.mdsal.binding.dom.codec.api.MissingClassInLoadingStrategyException;
 import org.opendaylight.mdsal.binding.dom.codec.api.MissingSchemaException;
@@ -37,6 +37,7 @@ import org.opendaylight.mdsal.binding.dom.codec.api.MissingSchemaForClassExcepti
 import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
+import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
@@ -48,16 +49,25 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-abstract sealed class DataContainerCodecContext<D extends DataObject, T extends RuntimeTypeContainer>
-        extends CodecContext implements CommonDataObjectCodecTreeNode<D>
-        permits AbstractDataObjectCodecContext, ChoiceCodecContext, RootCodecContext {
+abstract sealed class DataContainerCodecContext<D extends BindingObject & DataContainer, T extends RuntimeTypeContainer>
+        extends CodecContext implements BindingDataContainerCodecTreeNode<D>
+        permits CommonDataObjectCodecContext, RootCodecContext {
     private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecContext.class);
     private static final VarHandle EVENT_STREAM_SERIALIZER;
 
@@ -70,37 +80,27 @@ abstract sealed class DataContainerCodecContext<D extends DataObject, T extends
         }
     }
 
-    final @NonNull DataContainerCodecPrototype<T> prototype;
+    private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
 
     // Accessed via a VarHandle
     @SuppressWarnings("unused")
     private volatile DataContainerSerializer eventStreamSerializer;
 
-    DataContainerCodecContext(final DataContainerCodecPrototype<T> prototype) {
-        this.prototype = requireNonNull(prototype);
+    DataContainerCodecContext(final T type) {
+        childAddressabilitySummary = type instanceof RuntimeType runtimeType
+            ? computeChildAddressabilitySummary(runtimeType.statement())
+                // BindingRuntimeTypes, does not matter
+                : ChildAddressabilitySummary.MIXED;
     }
 
     @Override
     public final ChildAddressabilitySummary getChildAddressabilitySummary() {
-        return prototype.getChildAddressabilitySummary();
+        return childAddressabilitySummary;
     }
 
-    protected final QNameModule namespace() {
-        return prototype.getNamespace();
-    }
-
-    protected final CodecContextFactory factory() {
-        return prototype.getFactory();
-    }
-
-    protected final @NonNull T type() {
-        return prototype.getType();
-    }
+    protected abstract @NonNull CodecContextFactory factory();
 
-    @Override
-    protected NodeIdentifier getDomPathArgument() {
-        return prototype.getYangArg();
-    }
+    protected abstract @NonNull T type();
 
     /**
      * Returns nested node context using supplied YANG Instance Identifier.
@@ -121,7 +121,7 @@ abstract sealed class DataContainerCodecContext<D extends DataObject, T extends
      * @throws IllegalArgumentException If supplied argument does not represent valid child.
      */
     @Override
-    public DataContainerCodecContext<?, ?> bindingPathArgumentChild(final PathArgument arg,
+    public CommonDataObjectCodecContext<?, ?> bindingPathArgumentChild(final PathArgument arg,
             final List<YangInstanceIdentifier.PathArgument> builder) {
         final var child = getStreamChild(arg.getType());
         child.addYangPathArgument(arg, builder);
@@ -147,25 +147,8 @@ abstract sealed class DataContainerCodecContext<D extends DataObject, T extends
         }
     }
 
-    /**
-     * Returns deserialized Binding Path Argument from YANG instance identifier.
-     */
-    protected PathArgument getBindingPathArgument(final YangInstanceIdentifier.PathArgument domArg) {
-        return bindingArg();
-    }
-
-    protected final PathArgument bindingArg() {
-        return prototype.getBindingArg();
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public final Class<D> getBindingClass() {
-        return Class.class.cast(prototype.getBindingClass());
-    }
-
     @Override
-    public abstract <C extends DataObject> DataContainerCodecContext<C, ?> getStreamChild(Class<C> childClass);
+    public abstract <C extends DataObject> CommonDataObjectCodecContext<C, ?> getStreamChild(Class<C> childClass);
 
     /**
      * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case, one
@@ -175,11 +158,11 @@ abstract sealed class DataContainerCodecContext<D extends DataObject, T extends
      * @return Context of child or Optional.empty is supplied class is not applicable in context.
      */
     @Override
-    public abstract <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(Class<C> childClass);
+    public abstract <C extends DataObject> CommonDataObjectCodecContext<C, ?> streamChild(Class<C> childClass);
 
     @Override
     public String toString() {
-        return getClass().getSimpleName() + " [" + prototype.getBindingClass() + "]";
+        return getClass().getSimpleName() + " [" + getBindingClass() + "]";
     }
 
     static final <T extends DataObject, C extends DataContainerCodecContext<T, ?> & BindingNormalizedNodeCodec<T>>
@@ -415,4 +398,80 @@ abstract sealed class DataContainerCodecContext<D extends DataObject, T extends
             Augmentation.class);
         return opt.orElse(null);
     }
+
+
+
+    private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
+        // FIXME: rework this to work on EffectiveStatements
+        if (nodeSchema instanceof DataNodeContainer contaner) {
+            boolean haveAddressable = false;
+            boolean haveUnaddressable = false;
+            for (DataSchemaNode child : contaner.getChildNodes()) {
+                if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
+                    haveAddressable = true;
+                } else if (child instanceof ListSchemaNode list) {
+                    if (list.getKeyDefinition().isEmpty()) {
+                        haveUnaddressable = true;
+                    } else {
+                        haveAddressable = true;
+                    }
+                } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
+                        || child instanceof TypedDataSchemaNode) {
+                    haveUnaddressable = true;
+                } else if (child instanceof ChoiceSchemaNode choice) {
+                    switch (computeChildAddressabilitySummary(choice)) {
+                        case ADDRESSABLE -> haveAddressable = true;
+                        case UNADDRESSABLE -> haveUnaddressable = true;
+                        case MIXED -> {
+                            haveAddressable = true;
+                            haveUnaddressable = true;
+                        }
+                        default -> throw new IllegalStateException("Unhandled accessibility summary for " + child);
+                    }
+                } else {
+                    LOG.warn("Unhandled child node {}", child);
+                }
+            }
+
+            if (!haveAddressable) {
+                // Empty or all are unaddressable
+                return ChildAddressabilitySummary.UNADDRESSABLE;
+            }
+
+            return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
+        } else if (nodeSchema instanceof ChoiceSchemaNode choice) {
+            return computeChildAddressabilitySummary(choice);
+        }
+
+        // No child nodes possible: return unaddressable
+        return ChildAddressabilitySummary.UNADDRESSABLE;
+    }
+
+    private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(
+            final ChoiceSchemaNode choice) {
+        boolean haveAddressable = false;
+        boolean haveUnaddressable = false;
+        for (CaseSchemaNode child : choice.getCases()) {
+            switch (computeChildAddressabilitySummary(child)) {
+                case ADDRESSABLE:
+                    haveAddressable = true;
+                    break;
+                case UNADDRESSABLE:
+                    haveUnaddressable = true;
+                    break;
+                case MIXED:
+                    // A child is mixed, which means we are mixed, too
+                    return ChildAddressabilitySummary.MIXED;
+                default:
+                    throw new IllegalStateException("Unhandled accessibility summary for " + child);
+            }
+        }
+
+        if (!haveAddressable) {
+            // Empty or all are unaddressable
+            return ChildAddressabilitySummary.UNADDRESSABLE;
+        }
+
+        return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
+    }
 }
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecPrototype.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecPrototype.java
deleted file mode 100644 (file)
index 984d3eb..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (c) 2014 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.mdsal.binding.dom.codec.impl;
-
-import static java.util.Objects.requireNonNull;
-
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode.ChildAddressabilitySummary;
-import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
-import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
-import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-abstract sealed class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements CodecContextSupplier
-        permits AugmentationCodecPrototype, DataObjectCodecPrototype {
-    private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
-
-    private static final VarHandle INSTANCE;
-
-    static {
-        try {
-            INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
-                "instance", DataContainerCodecContext.class);
-        } catch (NoSuchFieldException | IllegalAccessException e) {
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-
-    private final @NonNull T type;
-    private final @NonNull QNameModule namespace;
-    private final @NonNull CodecContextFactory factory;
-    private final @NonNull Item<?> bindingArg;
-    private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
-
-    // multiple paths represent augmentation wrapper
-    // FIXME: this means it is either this or 'childArgs'
-
-    // Accessed via INSTANCE
-    @SuppressWarnings("unused")
-    private volatile DataContainerCodecContext<?, T> instance;
-
-    DataContainerCodecPrototype(final Item<?> bindingArg, final QNameModule namespace, final T type,
-            final CodecContextFactory factory) {
-        this.bindingArg = requireNonNull(bindingArg);
-        this.namespace = requireNonNull(namespace);
-        this.type = requireNonNull(type);
-        this.factory = requireNonNull(factory);
-
-        childAddressabilitySummary = type instanceof RuntimeType runtimeType
-            ? computeChildAddressabilitySummary(runtimeType.statement())
-                // BindingRuntimeTypes, does not matter
-                : ChildAddressabilitySummary.MIXED;
-    }
-
-    private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
-        // FIXME: rework this to work on EffectiveStatements
-        if (nodeSchema instanceof DataNodeContainer contaner) {
-            boolean haveAddressable = false;
-            boolean haveUnaddressable = false;
-            for (DataSchemaNode child : contaner.getChildNodes()) {
-                if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
-                    haveAddressable = true;
-                } else if (child instanceof ListSchemaNode list) {
-                    if (list.getKeyDefinition().isEmpty()) {
-                        haveUnaddressable = true;
-                    } else {
-                        haveAddressable = true;
-                    }
-                } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
-                        || child instanceof TypedDataSchemaNode) {
-                    haveUnaddressable = true;
-                } else if (child instanceof ChoiceSchemaNode choice) {
-                    switch (computeChildAddressabilitySummary(choice)) {
-                        case ADDRESSABLE -> haveAddressable = true;
-                        case UNADDRESSABLE -> haveUnaddressable = true;
-                        case MIXED -> {
-                            haveAddressable = true;
-                            haveUnaddressable = true;
-                        }
-                        default -> throw new IllegalStateException("Unhandled accessibility summary for " + child);
-                    }
-                } else {
-                    LOG.warn("Unhandled child node {}", child);
-                }
-            }
-
-            if (!haveAddressable) {
-                // Empty or all are unaddressable
-                return ChildAddressabilitySummary.UNADDRESSABLE;
-            }
-
-            return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
-        } else if (nodeSchema instanceof ChoiceSchemaNode choice) {
-            return computeChildAddressabilitySummary(choice);
-        }
-
-        // No child nodes possible: return unaddressable
-        return ChildAddressabilitySummary.UNADDRESSABLE;
-    }
-
-    private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(
-            final ChoiceSchemaNode choice) {
-        boolean haveAddressable = false;
-        boolean haveUnaddressable = false;
-        for (CaseSchemaNode child : choice.getCases()) {
-            switch (computeChildAddressabilitySummary(child)) {
-                case ADDRESSABLE:
-                    haveAddressable = true;
-                    break;
-                case UNADDRESSABLE:
-                    haveUnaddressable = true;
-                    break;
-                case MIXED:
-                    // A child is mixed, which means we are mixed, too
-                    return ChildAddressabilitySummary.MIXED;
-                default:
-                    throw new IllegalStateException("Unhandled accessibility summary for " + child);
-            }
-        }
-
-        if (!haveAddressable) {
-            // Empty or all are unaddressable
-            return ChildAddressabilitySummary.UNADDRESSABLE;
-        }
-
-        return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
-    }
-
-    final @NonNull T getType() {
-        return type;
-    }
-
-    final @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
-        return childAddressabilitySummary;
-    }
-
-    final @NonNull QNameModule getNamespace() {
-        return namespace;
-    }
-
-    final @NonNull CodecContextFactory getFactory() {
-        return factory;
-    }
-
-    final @NonNull Class<?> getBindingClass() {
-        return bindingArg.getType();
-    }
-
-    final @NonNull Item<?> getBindingArg() {
-        return bindingArg;
-    }
-
-    abstract @NonNull NodeIdentifier getYangArg();
-
-    @Override
-    public final DataContainerCodecContext<?, T> get() {
-        final var existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
-        return existing != null ? existing : loadInstance();
-    }
-
-    private @NonNull DataContainerCodecContext<?, T> loadInstance() {
-        final var tmp = createInstance();
-        final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
-        return witness == null ? tmp : witness;
-    }
-
-    // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
-    abstract @NonNull DataContainerCodecContext<?, T> createInstance();
-}
index 765fb6dc7e63d018162d776ef50aab7bb1469f14..3ba1c785a6a9d49cc9d948ffba76d03f1a78ed1a 100644 (file)
@@ -69,21 +69,21 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
     // Note this the content of this field depends only of invariants expressed as this class's fields or
     // BindingRuntimeContext. It is only accessed via MISMATCHED_AUGMENTED above.
     @SuppressWarnings("unused")
-    private volatile ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> mismatchedAugmented = ImmutableMap.of();
+    private volatile ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> mismatchedAugmented = ImmutableMap.of();
 
-    DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype) {
+    DataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype) {
         this(prototype, CodecItemFactory.of());
     }
 
-    DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype, final CodecItemFactory itemFactory) {
+    DataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype, final CodecItemFactory itemFactory) {
         this(prototype, new CodecDataObjectAnalysis<>(prototype, itemFactory, null));
     }
 
-    DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype, final Method keyMethod) {
+    DataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype, final Method keyMethod) {
         this(prototype, new CodecDataObjectAnalysis<>(prototype, CodecItemFactory.of(), keyMethod));
     }
 
-    private DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
+    private DataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype,
             final CodecDataObjectAnalysis<T> analysis) {
         super(prototype, analysis);
 
@@ -108,13 +108,13 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
     }
 
     @Override
-    final DataContainerCodecPrototype<?> pathChildPrototype(final Class<? extends DataObject> argType) {
+    final CommonDataObjectCodecPrototype<?> pathChildPrototype(final Class<? extends DataObject> argType) {
         final var child = super.pathChildPrototype(argType);
         return child != null ? child : augmentToPrototype.get(argType);
     }
 
     @Override
-    final DataContainerCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
+    final CommonDataObjectCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
         final var child = super.streamChildPrototype(childClass);
         if (child == null && Augmentation.class.isAssignableFrom(childClass)) {
             return getAugmentationProtoByClass(childClass);
@@ -175,7 +175,7 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
         var expected = oldMismatched;
         while (true) {
             final var newMismatched =
-                ImmutableMap.<Class<?>, DataContainerCodecPrototype<?>>builderWithExpectedSize(expected.size() + 1)
+                ImmutableMap.<Class<?>, CommonDataObjectCodecPrototype<?>>builderWithExpectedSize(expected.size() + 1)
                     .putAll(expected)
                     .put(childClass, prototype)
                     .build();
index be2fb03a182a18b21add4475ffb2d9d02be610e3..06bcb13424ceb0b097129e8e8622d86a472ad861 100644 (file)
@@ -15,9 +15,9 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 
-abstract sealed class DataObjectCodecPrototype<T extends RuntimeTypeContainer> extends DataContainerCodecPrototype<T>
+abstract sealed class DataObjectCodecPrototype<T extends RuntimeTypeContainer> extends CommonDataObjectCodecPrototype<T>
         permits CaseCodecPrototype, ChoiceCodecPrototype, ContainerLikeCodecPrototype, ListCodecPrototype,
-                NotificationCodecContext.Prototype, RootCodecContext.Prototype {
+                NotificationCodecContext.Prototype {
     private final @NonNull NodeIdentifier yangArg;
 
     // FIXME: this should not be needed
index d0ca5f876eab3aad719ff0f240d6dfa42053ed12..29fbf03f7dc8337741612a4e8e929b31a1fc3c0d 100644 (file)
@@ -45,7 +45,6 @@ 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.DataRoot;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedListAction;
 import org.opendaylight.yangtools.yang.binding.Notification;
@@ -70,23 +69,6 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absol
 
 final class RootCodecContext<D extends DataObject> extends DataContainerCodecContext<D, BindingRuntimeTypes>
         implements BindingDataObjectCodecTreeNode<D> {
-    /**
-     * Prototype for the root of YANG modeled world. This class only exists because DataContainerCodecContext requires
-     * a prototype.
-     */
-    static final class Prototype extends DataObjectCodecPrototype<BindingRuntimeTypes> {
-        private static final @NonNull NodeIdentifier ROOT_NODEID = NodeIdentifier.create(SchemaContext.NAME);
-
-        private Prototype(final CodecContextFactory factory) {
-            super(DataRoot.class, ROOT_NODEID, factory.getRuntimeContext().getTypes(), factory);
-        }
-
-        @Override
-        RootCodecContext<?> createInstance() {
-            throw new UnsupportedOperationException("Should never be invoked");
-        }
-    }
-
     private final LoadingCache<Class<? extends DataObject>, DataContainerCodecContext<?, ?>> childrenByClass =
         CacheBuilder.newBuilder().build(new CacheLoader<>() {
             @Override
@@ -140,7 +122,6 @@ final class RootCodecContext<D extends DataObject> extends DataContainerCodecCon
                     throw new IllegalArgumentException(key + " does not represent an RPC container");
                 }
 
-                final CodecContextFactory factory = factory();
                 final BindingRuntimeContext context = factory.getRuntimeContext();
 
                 final QName qname = BindingReflections.findQName(key);
@@ -225,25 +206,51 @@ final class RootCodecContext<D extends DataObject> extends DataContainerCodecCon
             }
         });
 
+    private static final @NonNull NodeIdentifier ROOT_NODEID = NodeIdentifier.create(SchemaContext.NAME);
+
+    private final @NonNull CodecContextFactory factory;
+
     RootCodecContext(final CodecContextFactory factory) {
-        super(new Prototype(factory));
+        super(factory.getRuntimeContext().getTypes());
+        this.factory = requireNonNull(factory);
     }
 
     @Override
     public WithStatus getSchema() {
-        return type().getEffectiveModelContext();
+        return factory.getRuntimeContext().getEffectiveModelContext();
+    }
+
+    @Override
+    public Class<D> getBindingClass() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected NodeIdentifier getDomPathArgument() {
+        // FIXME: this is not right
+        return ROOT_NODEID;
+    }
+
+    @Override
+    protected CodecContextFactory factory() {
+        return factory;
+    }
+
+    @Override
+    protected BindingRuntimeTypes type() {
+        return factory.getRuntimeContext().getTypes();
     }
 
     @Override
     @SuppressWarnings("unchecked")
-    public <C extends DataObject> DataContainerCodecContext<C, ?> getStreamChild(final Class<C> childClass) {
+    public <C extends DataObject> CommonDataObjectCodecContext<C, ?> getStreamChild(final Class<C> childClass) {
         final var result = Notification.class.isAssignableFrom(childClass) ? getNotificationImpl(childClass)
             : getOrRethrow(childrenByClass, childClass);
-        return (DataContainerCodecContext<C, ?>) result;
+        return (CommonDataObjectCodecContext<C, ?>) result;
     }
 
     @Override
-    public <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
+    public <C extends DataObject> CommonDataObjectCodecContext<C, ?> streamChild(final Class<C> childClass) {
         // FIXME: implement this
         throw new UnsupportedOperationException("Not supported");
     }
@@ -384,7 +391,7 @@ final class RootCodecContext<D extends DataObject> extends DataContainerCodecCon
     }
 
     @Override
-    public DataContainerCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
+    public CommonDataObjectCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
             final List<PathArgument> builder) {
         final var caseType = arg.getCaseType();
         if (caseType.isPresent()) {