From 341ad234ee117b58f44a7af0ea4eba074626a756 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 27 Jun 2023 18:23:02 +0200 Subject: [PATCH] Split out BindingDataContainerCodecTreeNode 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 --- .../BindingDataContainerCodecTreeNode.java | 133 ++++++++++++ .../api/CommonDataObjectCodecTreeNode.java | 121 +---------- .../impl/AbstractDataObjectCodecContext.java | 20 +- .../impl/AugmentationCodecPrototype.java | 2 +- .../dom/codec/impl/BindingCodecContext.java | 17 +- .../dom/codec/impl/ChoiceCodecContext.java | 28 +-- .../codec/impl/CodecDataObjectAnalysis.java | 12 +- .../impl/CommonDataObjectCodecContext.java | 69 +++++++ .../impl/CommonDataObjectCodecPrototype.java | 89 +++++++++ .../codec/impl/DataContainerCodecContext.java | 149 +++++++++----- .../impl/DataContainerCodecPrototype.java | 189 ------------------ .../codec/impl/DataObjectCodecContext.java | 16 +- .../codec/impl/DataObjectCodecPrototype.java | 4 +- .../dom/codec/impl/RootCodecContext.java | 57 +++--- 14 files changed, 479 insertions(+), 427 deletions(-) create mode 100644 binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataContainerCodecTreeNode.java create mode 100644 binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecContext.java create mode 100644 binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecPrototype.java delete mode 100644 binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecPrototype.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 index 0000000000..9d48250318 --- /dev/null +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataContainerCodecTreeNode.java @@ -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 DataContainer type + */ +public interface BindingDataContainerCodecTreeNode + extends BindingObjectCodecTreeNode, BindingDataObjectCodecTreeParent { + /** + * 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 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). + * + *

+ * 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} + */ + @Nullable CommonDataObjectCodecTreeNode streamChild(@NonNull Class childClass); + + default > @Nullable BindingAugmentationCodecTreeNode streamAugmentation( + final @NonNull Class childClass) { + final var result = streamChild(childClass); + if (result instanceof BindingAugmentationCodecTreeNode) { + return (BindingAugmentationCodecTreeNode) result; + } else if (result == null) { + return null; + } else { + throw new IllegalArgumentException( + "Child " + childClass.getName() + " results in non-Augmentation " + result); + } + } + + default @Nullable BindingDataObjectCodecTreeNode streamDataObject( + final @NonNull Class childClass) { + final var result = streamChild(childClass); + if (result instanceof BindingDataObjectCodecTreeNode) { + return (BindingDataObjectCodecTreeNode) 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 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 + } +} diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/CommonDataObjectCodecTreeNode.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/CommonDataObjectCodecTreeNode.java index a0ffa04fa9..e54b7d3a64 100644 --- a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/CommonDataObjectCodecTreeNode.java +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/CommonDataObjectCodecTreeNode.java @@ -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 DataObject type */ -public interface CommonDataObjectCodecTreeNode - extends BindingDataObjectCodecTreeParent, BindingObjectCodecTreeNode { - /** - * 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 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). - * - *

- * 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} - */ - @Nullable CommonDataObjectCodecTreeNode streamChild(@NonNull Class childClass); - - default > @Nullable BindingAugmentationCodecTreeNode streamAugmentation( - final @NonNull Class childClass) { - final var result = streamChild(childClass); - if (result instanceof BindingAugmentationCodecTreeNode) { - return (BindingAugmentationCodecTreeNode) result; - } else if (result == null) { - return null; - } else { - throw new IllegalArgumentException( - "Child " + childClass.getName() + " results in non-Augmentation " + result); - } - } - - default @Nullable BindingDataObjectCodecTreeNode streamDataObject( - final @NonNull Class childClass) { - final var result = streamChild(childClass); - if (result instanceof BindingDataObjectCodecTreeNode) { - return (BindingDataObjectCodecTreeNode) 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 builder); - +public interface CommonDataObjectCodecTreeNode extends BindingDataContainerCodecTreeNode { /** * 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 @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 - } } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java index 93ecf67bf5..f5a31aa982 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java @@ -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 - extends DataContainerCodecContext + extends CommonDataObjectCodecContext permits AugmentationCodecContext, DataObjectCodecContext { - private final ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; - private final ImmutableMap, DataContainerCodecPrototype> byStreamClass; + private final ImmutableMap, CommonDataObjectCodecPrototype> byBindingArgClass; + private final ImmutableMap, CommonDataObjectCodecPrototype> byStreamClass; private final ImmutableMap byYang; private final ImmutableMap leafChild; private final MethodHandle proxyConstructor; - AbstractDataObjectCodecContext(final DataContainerCodecPrototype prototype, + AbstractDataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, final CodecDataObjectAnalysis analysis) { super(prototype); byBindingArgClass = analysis.byBindingArgClass; @@ -69,24 +69,24 @@ public abstract sealed class AbstractDataObjectCodecContext DataContainerCodecContext getStreamChild(final Class childClass) { + public final CommonDataObjectCodecContext getStreamChild(final Class childClass) { return childNonNull(streamChild(childClass), childClass, "Child %s is not valid child of %s", getBindingClass(), childClass); } @SuppressWarnings("unchecked") @Override - public final DataContainerCodecContext streamChild(final Class childClass) { + public final CommonDataObjectCodecContext streamChild(final Class childClass) { final var childProto = streamChildPrototype(childClass); - return childProto == null ? null : (DataContainerCodecContext) childProto.get(); + return childProto == null ? null : (CommonDataObjectCodecContext) 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 builder) { final var argType = arg.getType(); final var context = childNonNull(pathChildPrototype(argType), argType, @@ -112,7 +112,7 @@ public abstract sealed class AbstractDataObjectCodecContext pathChildPrototype(final @NonNull Class argType) { + @Nullable CommonDataObjectCodecPrototype pathChildPrototype(final @NonNull Class argType) { return byBindingArgClass.get(argType); } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationCodecPrototype.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationCodecPrototype.java index d807602db8..6ef9375172 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationCodecPrototype.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationCodecPrototype.java @@ -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 { +final class AugmentationCodecPrototype extends CommonDataObjectCodecPrototype { private final @NonNull ImmutableSet childArgs; @SuppressWarnings("unchecked") diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java index e0798e2098..af1a6d84dc 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java @@ -112,8 +112,8 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri BYTECODE_DIRECTORY = Strings.isNullOrEmpty(dir) ? null : new File(dir); } - private final LoadingCache, DataContainerStreamer> streamers = CacheBuilder.newBuilder().build( - new CacheLoader<>() { + private final LoadingCache, 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, DataContainerSerializer> serializers = CacheBuilder.newBuilder().build( - new CacheLoader<>() { + private final LoadingCache, 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 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)); } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java index c73a47f691..243d0f795f 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java @@ -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 extends DataContainerCodecContext +final class ChoiceCodecContext extends CommonDataObjectCodecContext implements BindingDataObjectCodecTreeNode { private static final Logger LOG = LoggerFactory.getLogger(ChoiceCodecContext.class); - private final ImmutableListMultimap, DataContainerCodecPrototype> ambiguousByCaseChildClass; - private final ImmutableMap, DataContainerCodecPrototype> byCaseChildClass; + private final ImmutableListMultimap, CommonDataObjectCodecPrototype> ambiguousByCaseChildClass; + private final ImmutableMap, CommonDataObjectCodecPrototype> byCaseChildClass; private final ImmutableMap byYangCaseChild; - private final ImmutableMap, DataContainerCodecPrototype> byClass; + private final ImmutableMap, CommonDataObjectCodecPrototype> byClass; private final Set> ambiguousByCaseChildWarnings; ChoiceCodecContext(final Class cls, final ChoiceRuntimeType type, final CodecContextFactory factory) { @@ -112,9 +112,9 @@ final class ChoiceCodecContext extends DataContainerCodecC ChoiceCodecContext(final ChoiceCodecPrototype prototype) { super(prototype); final var byYangCaseChildBuilder = new HashMap(); - final var byClassBuilder = new HashMap, DataContainerCodecPrototype>(); + final var byClassBuilder = new HashMap, CommonDataObjectCodecPrototype>(); final var childToCase = SetMultimapBuilder.hashKeys().hashSetValues() - ., DataContainerCodecPrototype>build(); + ., 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 extends DataContainerCodecC byYangCaseChild = ImmutableMap.copyOf(byYangCaseChildBuilder); // Move unambiguous child->case mappings to byCaseChildClass, removing them from childToCase - final var ambiguousByCaseBuilder = ImmutableListMultimap., DataContainerCodecPrototype>builder(); - final var unambiguousByCaseBuilder = ImmutableMap., DataContainerCodecPrototype>builder(); + final var ambiguousByCaseBuilder = ImmutableListMultimap., CommonDataObjectCodecPrototype>builder(); + final var unambiguousByCaseBuilder = ImmutableMap., 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 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, DataContainerCodecPrototype>(); + final var bySubstitutionBuilder = new HashMap, 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 extends DataContainerCodecC } @Override - public DataContainerCodecContext getStreamChild(final Class childClass) { + public CommonDataObjectCodecContext getStreamChild(final Class childClass) { return childNonNull(streamChild(childClass), childClass, "Supplied class %s is not valid case in %s", childClass, bindingArg()); } @SuppressWarnings("unchecked") @Override - public DataContainerCodecContext streamChild(final Class childClass) { + public CommonDataObjectCodecContext streamChild(final Class childClass) { final var child = byClass.get(childClass); - return child == null ? null : (DataContainerCodecContext) child.get(); + return child == null ? null : (CommonDataObjectCodecContext) child.get(); } Iterable> getCaseChildrenClasses() { @@ -227,7 +227,7 @@ final class ChoiceCodecContext 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 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()); } } } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java index 954228a25e..98bdbaf81f 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java @@ -49,15 +49,15 @@ final class CodecDataObjectAnalysis { private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, AbstractDataObjectCodecContext.class, DataContainerNode.class); - final @NonNull ImmutableMap, DataContainerCodecPrototype> byStreamClass; - final @NonNull ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; + final @NonNull ImmutableMap, CommonDataObjectCodecPrototype> byStreamClass; + final @NonNull ImmutableMap, CommonDataObjectCodecPrototype> byBindingArgClass; final @NonNull ImmutableMap byYang; final @NonNull ImmutableMap leafNodes; final @NonNull Class> generatedClass; final @NonNull List possibleAugmentations; final @NonNull MethodHandle proxyConstructor; - CodecDataObjectAnalysis(final DataContainerCodecPrototype prototype, final CodecItemFactory itemFactory, + CodecDataObjectAnalysis(final CommonDataObjectCodecPrototype prototype, final CodecItemFactory itemFactory, final Method keyMethod) { // Preliminaries from prototype @SuppressWarnings("unchecked") @@ -80,8 +80,8 @@ final class CodecDataObjectAnalysis { } leafNodes = leafBuilder.build(); - final var byBindingArgClassBuilder = new HashMap, DataContainerCodecPrototype>(); - final var byStreamClassBuilder = new HashMap, DataContainerCodecPrototype>(); + final var byBindingArgClassBuilder = new HashMap, CommonDataObjectCodecPrototype>(); + final var byStreamClassBuilder = new HashMap, CommonDataObjectCodecPrototype>(); final var daoProperties = new HashMap, PropertyInfo>(); for (var childDataObj : clsToMethod.entrySet()) { final var method = childDataObj.getValue(); @@ -159,7 +159,7 @@ final class CodecDataObjectAnalysis { 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 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 index 0000000000..6004a807d4 --- /dev/null +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecContext.java @@ -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 + extends DataContainerCodecContext implements CommonDataObjectCodecTreeNode + permits AbstractDataObjectCodecContext, ChoiceCodecContext { + final @NonNull CommonDataObjectCodecPrototype prototype; + + CommonDataObjectCodecContext(final CommonDataObjectCodecPrototype prototype) { + super(prototype.getType()); + this.prototype = requireNonNull(prototype); + } + + @SuppressWarnings("unchecked") + @Override + public final Class 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 index 0000000000..67de4fb829 --- /dev/null +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecPrototype.java @@ -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 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 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 get() { + final var existing = (CommonDataObjectCodecContext) INSTANCE.getAcquire(this); + return existing != null ? existing : loadInstance(); + } + + private @NonNull CommonDataObjectCodecContext loadInstance() { + final var tmp = createInstance(); + final var witness = (CommonDataObjectCodecContext) 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 createInstance(); +} diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java index b7ed9619f6..2caa3667ac 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java @@ -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 - extends CodecContext implements CommonDataObjectCodecTreeNode - permits AbstractDataObjectCodecContext, ChoiceCodecContext, RootCodecContext { +abstract sealed class DataContainerCodecContext + extends CodecContext implements BindingDataContainerCodecTreeNode + 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 prototype; + private final @NonNull ChildAddressabilitySummary childAddressabilitySummary; // Accessed via a VarHandle @SuppressWarnings("unused") private volatile DataContainerSerializer eventStreamSerializer; - DataContainerCodecContext(final DataContainerCodecPrototype 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 bindingPathArgumentChild(final PathArgument arg, + public CommonDataObjectCodecContext bindingPathArgumentChild(final PathArgument arg, final List builder) { final var child = getStreamChild(arg.getType()); child.addYangPathArgument(arg, builder); @@ -147,25 +147,8 @@ abstract sealed class DataContainerCodecContext getBindingClass() { - return Class.class.cast(prototype.getBindingClass()); - } - @Override - public abstract DataContainerCodecContext getStreamChild(Class childClass); + public abstract CommonDataObjectCodecContext getStreamChild(Class 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 DataContainerCodecContext streamChild(Class childClass); + public abstract CommonDataObjectCodecContext streamChild(Class childClass); @Override public String toString() { - return getClass().getSimpleName() + " [" + prototype.getBindingClass() + "]"; + return getClass().getSimpleName() + " [" + getBindingClass() + "]"; } static final & BindingNormalizedNodeCodec> @@ -415,4 +398,80 @@ abstract sealed class DataContainerCodecContext 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 index 984d3ebee2..0000000000 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecPrototype.java +++ /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 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 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 get() { - final var existing = (DataContainerCodecContext) INSTANCE.getAcquire(this); - return existing != null ? existing : loadInstance(); - } - - private @NonNull DataContainerCodecContext loadInstance() { - final var tmp = createInstance(); - final var witness = (DataContainerCodecContext) 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 createInstance(); -} diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java index 765fb6dc7e..3ba1c785a6 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java @@ -69,21 +69,21 @@ public abstract sealed class DataObjectCodecContext, DataContainerCodecPrototype> mismatchedAugmented = ImmutableMap.of(); + private volatile ImmutableMap, CommonDataObjectCodecPrototype> mismatchedAugmented = ImmutableMap.of(); - DataObjectCodecContext(final DataContainerCodecPrototype prototype) { + DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype) { this(prototype, CodecItemFactory.of()); } - DataObjectCodecContext(final DataContainerCodecPrototype prototype, final CodecItemFactory itemFactory) { + DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, final CodecItemFactory itemFactory) { this(prototype, new CodecDataObjectAnalysis<>(prototype, itemFactory, null)); } - DataObjectCodecContext(final DataContainerCodecPrototype prototype, final Method keyMethod) { + DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, final Method keyMethod) { this(prototype, new CodecDataObjectAnalysis<>(prototype, CodecItemFactory.of(), keyMethod)); } - private DataObjectCodecContext(final DataContainerCodecPrototype prototype, + private DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, final CodecDataObjectAnalysis analysis) { super(prototype, analysis); @@ -108,13 +108,13 @@ public abstract sealed class DataObjectCodecContext pathChildPrototype(final Class argType) { + final CommonDataObjectCodecPrototype pathChildPrototype(final Class 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, DataContainerCodecPrototype>builderWithExpectedSize(expected.size() + 1) + ImmutableMap., CommonDataObjectCodecPrototype>builderWithExpectedSize(expected.size() + 1) .putAll(expected) .put(childClass, prototype) .build(); diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecPrototype.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecPrototype.java index be2fb03a18..06bcb13424 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecPrototype.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecPrototype.java @@ -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 extends DataContainerCodecPrototype +abstract sealed class DataObjectCodecPrototype extends CommonDataObjectCodecPrototype permits CaseCodecPrototype, ChoiceCodecPrototype, ContainerLikeCodecPrototype, ListCodecPrototype, - NotificationCodecContext.Prototype, RootCodecContext.Prototype { + NotificationCodecContext.Prototype { private final @NonNull NodeIdentifier yangArg; // FIXME: this should not be needed diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/RootCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/RootCodecContext.java index d0ca5f876e..29fbf03f7d 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/RootCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/RootCodecContext.java @@ -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 extends DataContainerCodecContext implements BindingDataObjectCodecTreeNode { - /** - * Prototype for the root of YANG modeled world. This class only exists because DataContainerCodecContext requires - * a prototype. - */ - static final class Prototype extends DataObjectCodecPrototype { - 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, DataContainerCodecContext> childrenByClass = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override @@ -140,7 +122,6 @@ final class RootCodecContext 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 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 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 DataContainerCodecContext getStreamChild(final Class childClass) { + public CommonDataObjectCodecContext getStreamChild(final Class childClass) { final var result = Notification.class.isAssignableFrom(childClass) ? getNotificationImpl(childClass) : getOrRethrow(childrenByClass, childClass); - return (DataContainerCodecContext) result; + return (CommonDataObjectCodecContext) result; } @Override - public DataContainerCodecContext streamChild(final Class childClass) { + public CommonDataObjectCodecContext streamChild(final Class childClass) { // FIXME: implement this throw new UnsupportedOperationException("Not supported"); } @@ -384,7 +391,7 @@ final class RootCodecContext extends DataContainerCodecCon } @Override - public DataContainerCodecContext bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg, + public CommonDataObjectCodecContext bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg, final List builder) { final var caseType = arg.getCaseType(); if (caseType.isPresent()) { -- 2.36.6