Update binding-dom adaptation to remove AugmentationNode 63/105463/45
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 22 May 2023 12:04:24 +0000 (14:04 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 27 May 2023 10:58:29 +0000 (12:58 +0200)
AugmentationIdentifier and AugmentationNode are being removed from
yang-data-api.

This patch adapts mdsal-binding-dom-codec to reflect the
fact Augmentation no longer has one-to-one equivalent. This means that
users need to check whether the code tree node refers to an Augmentation
or to a regular DataObject.

One particular sore point is that toNormalized() can no longer return a
plain Entry of NormalizedNode, as Augmentations do not have a backing.
To solve this, we introduce NormalizedResult with two specializations,
NodeResult and AugmentationResult.

In order to deal with DTCL changes, we also memoize
dataBefore/dataAfter, as it guides LazyAugmentationModification's
correct identification whether the a node has appeared, was modified, or
disappeared.

JIRA: MDSAL-820
Change-Id: I0360fd8f74ddb6df82ba64f7a87f6d92ce855162
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
68 files changed:
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDataObjectModification.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingClusteredDOMDataTreeChangeListenerAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortRegistryAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMWriteTransactionAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingStructuralType.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyAugmentationModification.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataObjectModification.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapterTest.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1418AugmentationTest.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModificationTest.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingAugmentationCodecTreeNode.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingCodecTree.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeNode.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeParent.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/CommonDataObjectCodecTreeNode.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java
binding/mdsal-binding-dom-codec/pom.xml
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ActionCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentableCodecDataObject.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationNodeContext.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/BindingToNormalizedStreamWriter.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceNodeCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.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/DataContainerCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecPrototype.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingCodecTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnydataLeafTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnyxmlLeafTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationSubstitutionTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/BinaryKeyTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5524augmentUses.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5845booleanKeyTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingCodecTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CaseSubstitutionTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/EmptyLeafTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierSerializeDeserializeTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/KeyInheritenceTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafrefSerializeDeserializeTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Mdsal668Test.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NormalizedNodeSerializeDeserializeTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/SpecializingLeafrefTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/TypedefTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/UnionTypeWithMultipleIdentityrefsTest.java
binding/mdsal-binding-test-model/src/main/java/org/opendaylight/mdsal/binding/test/model/util/ListsBindingUtils.java
yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyContentBuilder.java
yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibraryContentBuilderImpl.java
yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupport.java
yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyYangLibraryFormatTest.java
yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupportTest.java

diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDataObjectModification.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDataObjectModification.java
new file mode 100644 (file)
index 0000000..e0672ae
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * 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.adapter;
+
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.tree.api.ModificationType.UNMODIFIED;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.VerifyException;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lazily translated {@link DataObjectModification} based on {@link DataTreeCandidateNode}.
+ * {@link AbstractDataObjectModification} represents Data tree change event, but whole tree is not translated or
+ * resolved eagerly, but only child nodes which are directly accessed by user of data object modification.
+ *
+ * <p>
+ * This class is further specialized as {@link LazyAugmentationModification} and {@link LazyDataObjectModification}, as
+ * both use different serialization methods.
+ *
+ * @param <T> Type of Binding {@link DataObject}
+ * @param <N> Type of underlying {@link CommonDataObjectCodecTreeNode}
+ */
+abstract sealed class AbstractDataObjectModification<T extends DataObject, N extends CommonDataObjectCodecTreeNode<T>>
+        implements DataObjectModification<T>
+        permits LazyAugmentationModification, LazyDataObjectModification {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractDataObjectModification.class);
+
+    final @NonNull DataTreeCandidateNode domData;
+    final @NonNull PathArgument identifier;
+    final @NonNull N codec;
+
+    private volatile ImmutableList<AbstractDataObjectModification<?, ?>> childNodesCache;
+    private volatile ModificationType modificationType;
+    private volatile @Nullable T dataBefore;
+    private volatile @Nullable T dataAfter;
+
+    AbstractDataObjectModification(final DataTreeCandidateNode domData, final N codec, final PathArgument identifier) {
+        this.domData = requireNonNull(domData);
+        this.identifier = requireNonNull(identifier);
+        this.codec = requireNonNull(codec);
+    }
+
+    static @Nullable AbstractDataObjectModification<?, ?> from(final CommonDataObjectCodecTreeNode<?> codec,
+            final @NonNull DataTreeCandidateNode current) {
+        if (codec instanceof BindingDataObjectCodecTreeNode<?> childDataObjectCodec) {
+            return new LazyDataObjectModification<>(childDataObjectCodec, current);
+        } else if (codec instanceof BindingAugmentationCodecTreeNode<?> childAugmentationCodec) {
+            return LazyAugmentationModification.forParent(childAugmentationCodec, current);
+        } else {
+            throw new VerifyException("Unhandled codec " + codec);
+        }
+    }
+
+    @Override
+    public final Class<T> getDataType() {
+        return codec.getBindingClass();
+    }
+
+    @Override
+    public final PathArgument getIdentifier() {
+        return identifier;
+    }
+
+    @Override
+    public final ModificationType getModificationType() {
+        var localType = modificationType;
+        if (localType != null) {
+            return localType;
+        }
+
+        final var domModificationType = domModificationType();
+        modificationType = localType = switch (domModificationType) {
+            case APPEARED, WRITE -> ModificationType.WRITE;
+            case DISAPPEARED, DELETE -> ModificationType.DELETE;
+            case SUBTREE_MODIFIED -> resolveSubtreeModificationType();
+            default ->
+                // TODO: Should we lie about modification type instead of exception?
+                throw new IllegalStateException("Unsupported DOM Modification type " + domModificationType);
+        };
+        return localType;
+    }
+
+    @Override
+    public final T getDataBefore() {
+        var local = dataBefore;
+        if (local == null) {
+            dataBefore = local = deserialize(domData.getDataBefore());
+        }
+        return local;
+    }
+
+    @Override
+    public final T getDataAfter() {
+        var local = dataAfter;
+        if (local == null) {
+            dataAfter = local = deserialize(domData.getDataAfter());
+        }
+        return local;
+    }
+
+    private @Nullable T deserialize(final Optional<NormalizedNode> normalized) {
+        return normalized.isEmpty() ? null : deserialize(normalized.orElseThrow());
+    }
+
+    abstract @Nullable T deserialize(@NonNull NormalizedNode normalized);
+
+    @Override
+    public final DataObjectModification<?> getModifiedChild(final PathArgument arg) {
+        final var domArgumentList = new ArrayList<YangInstanceIdentifier.PathArgument>();
+        final var childCodec = codec.bindingPathArgumentChild(arg, domArgumentList);
+        final var toEnter = domArgumentList.iterator();
+
+        // Careful now: we need to validated the first item against subclass
+        var current = toEnter.hasNext() ? firstModifiedChild(toEnter.next()) : domData;
+        // ... and for everything else we can just go wild
+        while (toEnter.hasNext() && current != null) {
+            current = current.getModifiedChild(toEnter.next()).orElse(null);
+        }
+
+        if (current == null || current.getModificationType() == UNMODIFIED) {
+            return null;
+        }
+        return from(childCodec, current);
+    }
+
+    abstract @Nullable DataTreeCandidateNode firstModifiedChild(YangInstanceIdentifier.PathArgument arg);
+
+    @Override
+    public final ImmutableList<AbstractDataObjectModification<?, ?>> getModifiedChildren() {
+        var local = childNodesCache;
+        if (local == null) {
+            childNodesCache = local = createModifiedChilden(codec, domData, domChildNodes());
+        }
+        return local;
+    }
+
+    @Override
+    public final <C extends ChildOf<? super T>> List<DataObjectModification<C>> getModifiedChildren(
+            final Class<C> childType) {
+        return streamModifiedChildren(childType).collect(Collectors.toList());
+    }
+
+    @Override
+    public final <H extends ChoiceIn<? super T> & DataObject, C extends ChildOf<? super H>>
+            List<DataObjectModification<C>> getModifiedChildren(final Class<H> caseType, final Class<C> childType) {
+        return streamModifiedChildren(childType)
+            .filter(child -> caseType.equals(child.identifier.getCaseType().orElse(null)))
+            .collect(Collectors.toList());
+    }
+
+    @SuppressWarnings("unchecked")
+    private <C extends DataObject> Stream<LazyDataObjectModification<C>> streamModifiedChildren(
+            final Class<C> childType) {
+        return getModifiedChildren().stream()
+            .filter(child -> childType.isAssignableFrom(child.getDataType()))
+            .map(child -> (LazyDataObjectModification<C>) child);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C>
+            getModifiedChildListItem(final Class<C> listItem, final K listKey) {
+        return (DataObjectModification<C>) getModifiedChild(IdentifiableItem.of(listItem, listKey));
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final <H extends ChoiceIn<? super T> & DataObject, C extends Identifiable<K> & ChildOf<? super H>,
+            K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(final Class<H> caseType,
+                    final Class<C> listItem, final K listKey) {
+        return (DataObjectModification<C>) getModifiedChild(IdentifiableItem.of(caseType, listItem, listKey));
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(
+            final Class<C> child) {
+        return (DataObjectModification<C>) getModifiedChild(Item.of(child));
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final <H extends ChoiceIn<? super T> & DataObject, C extends ChildOf<? super H>> DataObjectModification<C>
+            getModifiedChildContainer(final Class<H> caseType, final Class<C> child) {
+        return (DataObjectModification<C>) getModifiedChild(Item.of(caseType, child));
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(
+            final Class<C> augmentation) {
+        return (DataObjectModification<C>) getModifiedChild(Item.of(augmentation));
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+    }
+
+    ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("identifier", identifier).add("domData", domData);
+    }
+
+    abstract @NonNull Collection<DataTreeCandidateNode> domChildNodes();
+
+    abstract org.opendaylight.yangtools.yang.data.tree.api.@NonNull ModificationType domModificationType();
+
+    private @NonNull ModificationType resolveSubtreeModificationType() {
+        return switch (codec.getChildAddressabilitySummary()) {
+            case ADDRESSABLE ->
+                // All children are addressable, it is safe to report SUBTREE_MODIFIED
+                ModificationType.SUBTREE_MODIFIED;
+            case UNADDRESSABLE ->
+                // All children are non-addressable, report WRITE
+                ModificationType.WRITE;
+            case MIXED -> {
+                // This case is not completely trivial, as we may have NOT_ADDRESSABLE nodes underneath us. If that
+                // is the case, we need to turn this modification into a WRITE operation, so that the user is able
+                // to observe those nodes being introduced. This is not efficient, but unfortunately unavoidable,
+                // as we cannot accurately represent such changes.
+                for (DataTreeCandidateNode child : domChildNodes()) {
+                    if (BindingStructuralType.recursiveFrom(child) == BindingStructuralType.NOT_ADDRESSABLE) {
+                        // We have a non-addressable child, turn this modification into a write
+                        yield ModificationType.WRITE;
+                    }
+                }
+
+                // No unaddressable children found, proceed in addressed mode
+                yield ModificationType.SUBTREE_MODIFIED;
+            }
+        };
+    }
+
+    private static @NonNull ImmutableList<AbstractDataObjectModification<?, ?>> createModifiedChilden(
+            final CommonDataObjectCodecTreeNode<?> parentCodec, final DataTreeCandidateNode parent,
+            final Collection<DataTreeCandidateNode> children) {
+        final var result = ImmutableList.<AbstractDataObjectModification<?, ?>>builder();
+        populateList(result, parentCodec, parent, children);
+        return result.build();
+    }
+
+    private static void populateList(final ImmutableList.Builder<AbstractDataObjectModification<?, ?>> result,
+            final CommonDataObjectCodecTreeNode<?> parentCodec, final DataTreeCandidateNode parent,
+            final Collection<DataTreeCandidateNode> children) {
+        final var augmentChildren =
+            ArrayListMultimap.<BindingAugmentationCodecTreeNode<?>, DataTreeCandidateNode>create();
+
+        for (var domChildNode : parent.getChildNodes()) {
+            if (domChildNode.getModificationType() != UNMODIFIED) {
+                final var type = BindingStructuralType.from(domChildNode);
+                if (type != BindingStructuralType.NOT_ADDRESSABLE) {
+                    /*
+                     * Even if type is UNKNOWN, from perspective of BindingStructuralType we try to load codec for it.
+                     * We will use that type to further specify debug log.
+                     */
+                    try {
+                        final var childCodec = parentCodec.yangPathArgumentChild(domChildNode.getIdentifier());
+                        if (childCodec instanceof BindingDataObjectCodecTreeNode<?> childDataObjectCodec) {
+                            populateList(result, type, childDataObjectCodec, domChildNode);
+                        } else if (childCodec instanceof BindingAugmentationCodecTreeNode<?> childAugmentationCodec) {
+                            // Defer creation once we have collected all modified children
+                            augmentChildren.put(childAugmentationCodec, domChildNode);
+                        } else {
+                            throw new VerifyException("Unhandled codec %s for type %s".formatted(childCodec, type));
+                        }
+                    } catch (final IllegalArgumentException e) {
+                        if (type == BindingStructuralType.UNKNOWN) {
+                            LOG.debug("Unable to deserialize unknown DOM node {}", domChildNode, e);
+                        } else {
+                            LOG.debug("Binding representation for DOM node {} was not found", domChildNode, e);
+                        }
+                    }
+                }
+            }
+        }
+
+        for (var entry : augmentChildren.asMap().entrySet()) {
+            final var modification = LazyAugmentationModification.forModifications(entry.getKey(), parent,
+                entry.getValue());
+            if (modification != null) {
+                result.add(modification);
+            }
+        }
+    }
+
+    private static void populateList(final ImmutableList.Builder<AbstractDataObjectModification<?, ?>> result,
+            final BindingStructuralType type, final BindingDataObjectCodecTreeNode<?> childCodec,
+            final DataTreeCandidateNode domChildNode) {
+        switch (type) {
+            case INVISIBLE_LIST:
+                // We use parent codec intentionally.
+                populateListWithSingleCodec(result, childCodec, domChildNode.getChildNodes());
+                break;
+            case INVISIBLE_CONTAINER:
+                populateList(result, childCodec, domChildNode, domChildNode.getChildNodes());
+                break;
+            case UNKNOWN:
+            case VISIBLE_CONTAINER:
+                result.add(new LazyDataObjectModification<>(childCodec, domChildNode));
+                break;
+            default:
+        }
+    }
+
+    private static void populateListWithSingleCodec(
+            final ImmutableList.Builder<AbstractDataObjectModification<?, ?>> result,
+            final BindingDataObjectCodecTreeNode<?> codec, final Collection<DataTreeCandidateNode> childNodes) {
+        for (var child : childNodes) {
+            if (child.getModificationType() != UNMODIFIED) {
+                result.add(new LazyDataObjectModification<>(codec, child));
+            }
+        }
+    }
+}
index e7bbc922a23bf6fb18ca266d5b9f5548a6db6b9a..79b9c2febbfddc913c3a074dbc31b69f802bdaad 100644 (file)
@@ -11,13 +11,19 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.base.VerifyException;
+import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.api.query.QueryExpression;
 import org.opendaylight.mdsal.binding.api.query.QueryResult;
 import org.opendaylight.mdsal.binding.dom.adapter.query.DefaultQuery;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeQueryOperations;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
@@ -29,7 +35,7 @@ import org.opendaylight.yangtools.concepts.Delegator;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,7 +48,7 @@ abstract class AbstractForwardedTransaction<T extends DOMDataTreeTransaction> im
 
     AbstractForwardedTransaction(final AdapterContext adapterContext, final T delegateTx) {
         this.adapterContext = requireNonNull(adapterContext, "Codec must not be null");
-        this.delegate = requireNonNull(delegateTx, "Delegate must not be null");
+        delegate = requireNonNull(delegateTx, "Delegate must not be null");
     }
 
     @Override
@@ -69,18 +75,43 @@ abstract class AbstractForwardedTransaction<T extends DOMDataTreeTransaction> im
             final InstanceIdentifier<D> path) {
         checkArgument(!path.isWildcarded(), "Invalid read of wildcarded path %s", path);
 
-        final CurrentAdapterSerializer codec = adapterContext.currentSerializer();
-        final YangInstanceIdentifier domPath = codec.toYangInstanceIdentifier(path);
-
+        final var codecWithPath = adapterContext.currentSerializer().getSubtreeCodecWithPath(path);
+        final var domPath = codecWithPath.path();
+        final var codec = codecWithPath.codec();
         return readOps.read(store, domPath)
-                .transform(optData -> optData.map(domData -> (D) codec.fromNormalizedNode(domPath, domData).getValue()),
-                    MoreExecutors.directExecutor());
+            .transform(optData -> optData.flatMap(data -> decodeRead(codec, data)), MoreExecutors.directExecutor());
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <D extends DataObject> Optional<D> decodeRead(final CommonDataObjectCodecTreeNode<D> codec,
+            final @NonNull NormalizedNode data) {
+        if (codec instanceof BindingDataObjectCodecTreeNode<?> dataObject) {
+            return Optional.of((D) dataObject.deserialize(data));
+        } else if (codec instanceof BindingAugmentationCodecTreeNode<?> augment) {
+            return Optional.ofNullable((D) augment.filterFrom(data));
+        } else {
+            throw new VerifyException("Unhandled codec " + codec);
+        }
     }
 
     protected final @NonNull FluentFuture<Boolean> doExists(final DOMDataTreeReadOperations readOps,
             final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
         checkArgument(!path.isWildcarded(), "Invalid exists of wildcarded path %s", path);
-        return readOps.exists(store, adapterContext.currentSerializer().toYangInstanceIdentifier(path));
+
+        final var codecWithPath = adapterContext.currentSerializer().getSubtreeCodecWithPath(path);
+        final var domPath = codecWithPath.path();
+        if (codecWithPath.codec() instanceof BindingAugmentationCodecTreeNode<?> augment) {
+            // Complicated case: we need to check if any of the children exist, as DOM layer just does not know about
+            //                   this indirection
+            return FluentFuture.from(Futures.transform(
+                Futures.allAsList(augment.childPathArguments().stream()
+                    .map(child -> readOps.exists(store, domPath.node(child)))
+                    .collect(ImmutableList.toImmutableList())),
+                children -> children.contains(Boolean.TRUE),
+                MoreExecutors.directExecutor()));
+        } else {
+            return readOps.exists(store, domPath);
+        }
     }
 
     protected static final <T extends @NonNull DataObject> @NonNull FluentFuture<QueryResult<T>> doExecute(
index cc031c0bdb0ab5a45227d06ea4a7c7a13f414ef4..d3e9cfc9ac82f619f893ce540e0364e661d6123c 100644 (file)
@@ -22,7 +22,8 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
 final class BindingClusteredDOMDataTreeChangeListenerAdapter<T extends DataObject>
         extends BindingDOMDataTreeChangeListenerAdapter<T> implements ClusteredDOMDataTreeChangeListener {
     BindingClusteredDOMDataTreeChangeListenerAdapter(final AdapterContext codec,
-            final ClusteredDataTreeChangeListener<T> listener, final LogicalDatastoreType store) {
-        super(codec, listener, store);
+            final ClusteredDataTreeChangeListener<T> listener, final LogicalDatastoreType store,
+            final Class<T> augment) {
+        super(codec, listener, store, augment);
     }
 }
index 915eeee3501f6de1c8c79028669142dcb0c37bcc..349115e3638136a9d16d6e6911102e8ac5f08e99 100644 (file)
@@ -93,9 +93,9 @@ public class BindingDOMDataBrokerAdapter extends AbstractBindingAdapter<@NonNull
     }
 
     @Override
-    public <T extends DataObject, L extends DataTreeChangeListener<T>> ListenerRegistration<L>
-            registerDataTreeChangeListener(
-            final DataTreeIdentifier<T> treeId, final L listener) {
+    public <T extends DataObject, L extends DataTreeChangeListener<T>>
+            ListenerRegistration<L> registerDataTreeChangeListener(final DataTreeIdentifier<T> treeId,
+                final L listener) {
         if (treeChangeService == null) {
             throw new UnsupportedOperationException("Underlying data broker does not expose DOMDataTreeChangeService.");
         }
index 61240d57f43692fe379a433762e5227a0d134e81..65de385c4b4c3e1ee836093d7ec0ecfdc3686021 100644 (file)
@@ -24,22 +24,32 @@ class BindingDOMDataTreeChangeListenerAdapter<T extends DataObject> implements D
     private final AdapterContext adapterContext;
     private final DataTreeChangeListener<T> listener;
     private final LogicalDatastoreType store;
+    private final Class<T> augment;
+
+    private boolean initialSyncDone;
 
     BindingDOMDataTreeChangeListenerAdapter(final AdapterContext adapterContext,
-            final DataTreeChangeListener<T> listener, final LogicalDatastoreType store) {
+            final DataTreeChangeListener<T> listener, final LogicalDatastoreType store, final Class<T> augment) {
         this.adapterContext = requireNonNull(adapterContext);
         this.listener = requireNonNull(listener);
         this.store = requireNonNull(store);
+        this.augment = augment;
     }
 
     @Override
     public void onDataTreeChanged(final List<DataTreeCandidate> domChanges) {
-        listener.onDataTreeChanged(LazyDataTreeModification.from(adapterContext.currentSerializer(), domChanges,
-            store));
+        final var changes = LazyDataTreeModification.<T>from(adapterContext.currentSerializer(), domChanges, store,
+            augment);
+        if (!changes.isEmpty()) {
+            listener.onDataTreeChanged(changes);
+        } else if (!initialSyncDone) {
+            onInitialData();
+        }
     }
 
     @Override
     public void onInitialData() {
+        initialSyncDone = true;
         listener.onInitialData();
     }
 }
index 1bde0b4b0ce8b0fd8eeb480d6aad6fa3d838b00b..ea84fdc514cf2e8e1cda2427fc6cd3ac9b2b96a9 100644 (file)
@@ -15,6 +15,7 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 
 /**
@@ -36,11 +37,14 @@ final class BindingDOMDataTreeChangeServiceAdapter extends AbstractBindingAdapte
             registerDataTreeChangeListener(final DataTreeIdentifier<T> treeId, final L listener) {
         final DOMDataTreeIdentifier domIdentifier = toDomTreeIdentifier(treeId);
         final LogicalDatastoreType storeType = treeId.getDatastoreType();
+        final Class<T> target = treeId.getRootIdentifier().getTargetType();
+        final Class<T> augment = Augmentation.class.isAssignableFrom(target) ? target : null;
+
         final BindingDOMDataTreeChangeListenerAdapter<T> domListener =
                 listener instanceof ClusteredDataTreeChangeListener
                         ? new BindingClusteredDOMDataTreeChangeListenerAdapter<>(
-                                adapterContext(), (ClusteredDataTreeChangeListener<T>) listener, storeType)
-                        : new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), listener, storeType);
+                                adapterContext(), (ClusteredDataTreeChangeListener<T>) listener, storeType, augment)
+                        : new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), listener, storeType, augment);
 
         final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter<T>> domReg =
                 getDelegate().registerDataTreeChangeListener(domIdentifier, domListener);
index f3f15b7aa3c27d319daf8c304d9ba4190b7b5de7..f2e6fb50938de0d063ea71a25e88ab6cfe11f91b 100644 (file)
@@ -9,9 +9,9 @@ package org.opendaylight.mdsal.binding.dom.adapter;
 
 import com.google.common.util.concurrent.FluentFuture;
 import java.util.Collection;
+import java.util.Objects;
 import java.util.stream.Collectors;
 import org.opendaylight.mdsal.binding.api.DataTreeCommitCohort;
-import org.opendaylight.mdsal.binding.api.DataTreeModification;
 import org.opendaylight.mdsal.common.api.PostCanCommitStep;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeCandidate;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeCommitCohort;
@@ -20,16 +20,20 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 final class BindingDOMDataTreeCommitCohortAdapter<T extends DataObject>
         extends AbstractBindingAdapter<DataTreeCommitCohort<T>> implements DOMDataTreeCommitCohort {
-    BindingDOMDataTreeCommitCohortAdapter(final AdapterContext codec, final DataTreeCommitCohort<T> cohort) {
+    private final Class<T> augment;
+
+    BindingDOMDataTreeCommitCohortAdapter(final AdapterContext codec, final DataTreeCommitCohort<T> cohort,
+            final Class<T> augment) {
         super(codec, cohort);
+        this.augment = augment;
     }
 
     @Override
     public FluentFuture<PostCanCommitStep> canCommit(final Object txId,
             final EffectiveModelContext ctx, final Collection<DOMDataTreeCandidate> candidates) {
-        final Collection<DataTreeModification<T>> modifications = candidates.stream()
-                .map(candidate -> LazyDataTreeModification.<T>create(currentSerializer(), candidate))
-                .collect(Collectors.toList());
-        return getDelegate().canCommit(txId, modifications);
+        return getDelegate().canCommit(txId, candidates.stream()
+            .map(candidate -> LazyDataTreeModification.<T>from(currentSerializer(), candidate, augment))
+            .filter(Objects::nonNull)
+            .collect(Collectors.toList()));
     }
 }
index 3cf014857cb1a9608c9045318f2c56b0cea473da..89624d44991ba2094bf810630656fa52fdb9ab58 100644 (file)
@@ -14,6 +14,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeCommitCohortRegistration;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeCommitCohortRegistry;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 
 final class BindingDOMDataTreeCommitCohortRegistryAdapter
@@ -27,8 +28,10 @@ final class BindingDOMDataTreeCommitCohortRegistryAdapter
     @Override
     public <D extends DataObject, T extends DataTreeCommitCohort<D>> ObjectRegistration<T> registerCommitCohort(
             final DataTreeIdentifier<D> subtree, final T cohort) {
-        final BindingDOMDataTreeCommitCohortAdapter<D> adapter =
-                new BindingDOMDataTreeCommitCohortAdapter<>(adapterContext(), cohort);
+        final Class<D> target = subtree.getRootIdentifier().getTargetType();
+
+        final BindingDOMDataTreeCommitCohortAdapter<D> adapter = new BindingDOMDataTreeCommitCohortAdapter<>(
+            adapterContext(), cohort, Augmentation.class.isAssignableFrom(target) ? target : null);
         final DOMDataTreeIdentifier domPath = currentSerializer().toDOMDataTreeIdentifier(subtree);
         final DOMDataTreeCommitCohortRegistration<?> domReg = getDelegate().registerCommitCohort(domPath, adapter);
         return new ObjectRegistration<>() {
index 0c6bef88148d62b96c9271122bd5bdb0af1e5b72..a1dfc584578eaff22469a6cee19e5ccc0762cdbc 100644 (file)
@@ -9,16 +9,21 @@ package org.opendaylight.mdsal.binding.dom.adapter;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import com.google.common.base.VerifyException;
 import com.google.common.util.concurrent.FluentFuture;
-import java.util.Map.Entry;
+import java.util.HashSet;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.AugmentationResult;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NormalizedResult;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 
 class BindingDOMWriteTransactionAdapter<T extends DOMDataTreeWriteTransaction> extends AbstractForwardedTransaction<T>
@@ -30,41 +35,92 @@ class BindingDOMWriteTransactionAdapter<T extends DOMDataTreeWriteTransaction> e
     @Override
     public final <U extends DataObject> void put(final LogicalDatastoreType store, final InstanceIdentifier<U> path,
             final U data) {
-        final Entry<YangInstanceIdentifier, NormalizedNode> normalized = toNormalized("put", path, data);
-        getDelegate().put(store, normalized.getKey(), normalized.getValue());
+        put(store, toNormalized("put", path, data));
+    }
+
+    private void put(final LogicalDatastoreType store, final NormalizedResult normalized) {
+        final var delegate = getDelegate();
+        final var domPath = normalized.path();
+
+        if (normalized instanceof AugmentationResult augment) {
+            // Augmentation: put() child nodes provided with augmentation, delete() those having no data
+            final var putIds = new HashSet<YangInstanceIdentifier.PathArgument>();
+            for (var child : augment.children()) {
+                final var childId = child.getIdentifier();
+                delegate.put(store, domPath.node(childId), child);
+                putIds.add(childId);
+            }
+            for (var childId : augment.possibleChildren()) {
+                if (!putIds.contains(childId)) {
+                    delegate.delete(store, domPath.node(childId));
+                }
+            }
+        } else if (normalized instanceof NodeResult node) {
+            delegate.put(store, domPath, node.node());
+        } else {
+            throw new VerifyException("Unhandled result " + normalized);
+        }
     }
 
     @Deprecated
     @Override
     public final <U extends DataObject> void mergeParentStructurePut(final LogicalDatastoreType store,
             final InstanceIdentifier<U> path, final U data) {
-        final CurrentAdapterSerializer serializer = adapterContext().currentSerializer();
-        final Entry<YangInstanceIdentifier, NormalizedNode> normalized = toNormalized(serializer, "put", path, data);
-        ensureParentsByMerge(serializer, store, normalized.getKey(), path);
-        getDelegate().put(store, normalized.getKey(), normalized.getValue());
+        final var serializer = adapterContext().currentSerializer();
+        final var normalized = toNormalized(serializer, "put", path, data);
+        ensureParentsByMerge(serializer, store, normalized.path());
+        put(store, normalized);
     }
 
     @Override
     public final <D extends DataObject> void merge(final LogicalDatastoreType store, final InstanceIdentifier<D> path,
             final D data) {
-        final Entry<YangInstanceIdentifier, NormalizedNode> normalized = toNormalized("merge", path, data);
-        getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+        merge(store, toNormalized("merge", path, data));
+    }
+
+    private void merge(final LogicalDatastoreType store, final NormalizedResult normalized) {
+        final var delegate = getDelegate();
+        final var domPath = normalized.path();
+
+        if (normalized instanceof AugmentationResult augment) {
+            // Augmentation: merge individual children
+            for (var child : augment.children()) {
+                delegate.merge(store, domPath.node(child.getIdentifier()), child);
+            }
+        } else if (normalized instanceof NodeResult node) {
+            delegate.merge(store, domPath, node.node());
+        } else {
+            throw new VerifyException("Unhandled result " + normalized);
+        }
     }
 
     @Deprecated
     @Override
     public final <U extends DataObject> void mergeParentStructureMerge(final LogicalDatastoreType store,
             final InstanceIdentifier<U> path, final U data) {
-        final CurrentAdapterSerializer serializer = adapterContext().currentSerializer();
-        final Entry<YangInstanceIdentifier, NormalizedNode> normalized = toNormalized(serializer, "merge", path, data);
-        ensureParentsByMerge(serializer, store, normalized.getKey(), path);
-        getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+        final var serializer = adapterContext().currentSerializer();
+        final var normalized = toNormalized(serializer, "merge", path, data);
+        ensureParentsByMerge(serializer, store, normalized.path());
+        merge(store, normalized);
     }
 
     @Override
     public final void delete(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
         checkArgument(!path.isWildcarded(), "Cannot delete wildcarded path %s", path);
-        getDelegate().delete(store, adapterContext().currentSerializer().toYangInstanceIdentifier(path));
+        final var serializer = adapterContext().currentSerializer();
+
+        // Lookup the codec and the corresponding path
+        final var codecWithPath = serializer.getSubtreeCodecWithPath(path);
+        final var domPath = codecWithPath.path();
+        final var delegate = getDelegate();
+        if (codecWithPath.codec() instanceof BindingAugmentationCodecTreeNode<?> augmentCodec) {
+            // Deletion of an augmentation: issue a delete on all potential children of the augmentation
+            for (var childPath : augmentCodec.childPathArguments()) {
+                delegate.delete(store, domPath.node(childPath));
+            }
+        } else {
+            delegate.delete(store, domPath);
+        }
     }
 
     @Override
@@ -83,24 +139,23 @@ class BindingDOMWriteTransactionAdapter<T extends DOMDataTreeWriteTransaction> e
      *
      * @param store an instance of LogicalDatastoreType
      * @param domPath an instance of YangInstanceIdentifier
-     * @param path an instance of InstanceIdentifier
      */
     private void ensureParentsByMerge(final CurrentAdapterSerializer serializer, final LogicalDatastoreType store,
-            final YangInstanceIdentifier domPath, final InstanceIdentifier<?> path) {
-        final YangInstanceIdentifier parentPath = domPath.getParent();
+            final YangInstanceIdentifier domPath) {
+        final var parentPath = domPath.getParent();
         if (parentPath != null && !parentPath.isEmpty()) {
-            final NormalizedNode parentNode = ImmutableNodes.fromInstanceId(
+            final var parentNode = ImmutableNodes.fromInstanceId(
                 serializer.getRuntimeContext().getEffectiveModelContext(), parentPath);
             getDelegate().merge(store, YangInstanceIdentifier.create(parentNode.getIdentifier()), parentNode);
         }
     }
 
-    private <U extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode> toNormalized(final String operation,
+    private <U extends DataObject> @NonNull NormalizedResult toNormalized(final String operation,
             final InstanceIdentifier<U> path, final U data) {
         return toNormalized(adapterContext().currentSerializer(), operation, path, data);
     }
 
-    private static <U extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode> toNormalized(
+    private static <U extends DataObject> @NonNull NormalizedResult toNormalized(
             final CurrentAdapterSerializer serializer, final String operation, final InstanceIdentifier<U> path,
             final U data) {
         checkArgument(!path.isWildcarded(), "Cannot %s data into wildcarded path %s", operation, path);
index 168d815ba11912393cd6dad942bd48b56a2e6c7b..5d9f7b3765514ce999987d815ac10ea1c461d6b3 100644 (file)
@@ -9,12 +9,10 @@ package org.opendaylight.mdsal.binding.dom.adapter;
 
 import com.google.common.annotations.Beta;
 import java.util.Optional;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.AnyxmlNode;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
@@ -81,7 +79,7 @@ public enum BindingStructuralType {
     }
 
     private static BindingStructuralType from(final PathArgument identifier) {
-        if (identifier instanceof NodeIdentifierWithPredicates || identifier instanceof AugmentationIdentifier) {
+        if (identifier instanceof NodeIdentifierWithPredicates) {
             return VISIBLE_CONTAINER;
         }
         if (identifier instanceof NodeWithValue) {
@@ -128,7 +126,7 @@ public enum BindingStructuralType {
     }
 
     private static boolean isVisibleContainer(final NormalizedNode data) {
-        return data instanceof MapEntryNode || data instanceof ContainerNode || data instanceof AugmentationNode;
+        return data instanceof MapEntryNode || data instanceof ContainerNode;
     }
 
     private static boolean isNotAddressable(final NormalizedNode normalizedNode) {
index a6a8282a26ed4b35f54a1fee58cd6fb108e98a0a..c5512ca321a7263ee6b81257cdf78a32ca012dd4 100644 (file)
@@ -49,7 +49,6 @@ import org.opendaylight.yangtools.yang.binding.contract.Naming;
 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.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.ActionEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
@@ -199,13 +198,6 @@ public final class CurrentAdapterSerializer extends ForwardingBindingDOMCodecSer
         QNameModule lastNamespace;
         do {
             final var arg = it.next();
-            if (arg instanceof AugmentationIdentifier) {
-                final var augChildren = ((AugmentationIdentifier) arg).getPossibleChildNames();
-                verify(!augChildren.isEmpty(), "Invalid empty augmentation %s", arg);
-                lastNamespace = augChildren.iterator().next().getModule();
-                continue;
-            }
-
             final var qname = arg.getNodeType();
             final var stmt = stack.enterDataTree(qname);
             lastNamespace = qname.getModule();
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyAugmentationModification.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyAugmentationModification.java
new file mode 100644 (file)
index 0000000..9b1d1e4
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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.adapter;
+
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.tree.api.ModificationType.UNMODIFIED;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
+
+/**
+ * Specialization of {@link AbstractDataObjectModification} for {@link Augmentation}s. Is based on a parent
+ * {@link DataTreeCandidateNode}, but contains only a subset of its modifications.
+ */
+final class LazyAugmentationModification<A extends Augmentation<?>>
+        extends AbstractDataObjectModification<A, BindingAugmentationCodecTreeNode<A>> {
+    private final @NonNull ImmutableList<DataTreeCandidateNode> domChildNodes;
+
+    private LazyAugmentationModification(final BindingAugmentationCodecTreeNode<A> codec,
+            final DataTreeCandidateNode parent, final ImmutableList<DataTreeCandidateNode> domChildNodes) {
+        super(parent, codec, codec.deserializePathArgument(null));
+        this.domChildNodes = requireNonNull(domChildNodes);
+    }
+
+    static <A extends Augmentation<?>> @Nullable LazyAugmentationModification<A> forModifications(
+            final BindingAugmentationCodecTreeNode<A> codec, final DataTreeCandidateNode parent,
+            final Collection<DataTreeCandidateNode> children) {
+        // Filter out any unmodified children first
+        final var domChildren = children.stream()
+            .filter(childMod -> childMod.getModificationType() != UNMODIFIED)
+            .collect(ImmutableList.toImmutableList());
+        // Only return a modification if there is something left
+        return domChildren.isEmpty() ? null : new LazyAugmentationModification<>(codec, parent, domChildren);
+    }
+
+    static <A extends Augmentation<?>> @Nullable LazyAugmentationModification<A> forParent(
+            final BindingAugmentationCodecTreeNode<A> codec, final DataTreeCandidateNode parent) {
+        final var builder = ImmutableList.<DataTreeCandidateNode>builder();
+        for (var pathArg : codec.childPathArguments()) {
+            parent.getModifiedChild(pathArg).ifPresent(builder::add);
+        }
+        final var domChildren = builder.build();
+        return domChildren.isEmpty() ? null : new LazyAugmentationModification<>(codec, parent, domChildren);
+    }
+
+    @Override
+    ImmutableList<DataTreeCandidateNode> domChildNodes() {
+        return domChildNodes;
+    }
+
+    @Override
+    org.opendaylight.yangtools.yang.data.tree.api.ModificationType domModificationType() {
+        final var before = getDataBefore();
+        final var after = getDataAfter();
+        if (before == null) {
+            return after == null ? org.opendaylight.yangtools.yang.data.tree.api.ModificationType.UNMODIFIED
+                :  org.opendaylight.yangtools.yang.data.tree.api.ModificationType.APPEARED;
+        }
+        return after == null ? org.opendaylight.yangtools.yang.data.tree.api.ModificationType.DISAPPEARED
+            : org.opendaylight.yangtools.yang.data.tree.api.ModificationType.SUBTREE_MODIFIED;
+    }
+
+
+    @Override
+    A deserialize(final NormalizedNode normalized) {
+        return codec.filterFrom(normalized);
+    }
+
+    @Override
+    DataTreeCandidateNode firstModifiedChild(final PathArgument arg) {
+        // Not entirely efficient linear search, but otherwise we'd have to index, which is even slower
+        return domChildNodes.stream()
+            .filter(child -> arg.equals(child.getIdentifier()))
+            .findFirst()
+            .orElse(null);
+    }
+
+    @Override
+    ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper)
+            .add("domType", domData.getModificationType())
+            .add("domChildren", domChildNodes);
+    }
+}
index da5099ee1528876150262da4fae7859e9b86e511..b030e0ede33fabc67e10da2228449b9e1caf104a 100644 (file)
  */
 package org.opendaylight.mdsal.binding.dom.adapter;
 
-import static com.google.common.base.Verify.verify;
-import static java.util.Objects.requireNonNull;
-import static org.opendaylight.yangtools.yang.data.tree.api.ModificationType.UNMODIFIED;
-
-import com.google.common.base.MoreObjects;
-import java.util.ArrayList;
+import com.google.common.base.MoreObjects.ToStringHelper;
 import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.api.DataObjectModification;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
-import org.opendaylight.yangtools.yang.binding.ChoiceIn;
 import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.Identifiable;
-import org.opendaylight.yangtools.yang.binding.Identifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Lazily translated {@link DataObjectModification} based on {@link DataTreeCandidateNode}.
  *
  * <p>
- * {@link LazyDataObjectModification} represents Data tree change event,
- * but whole tree is not translated or resolved eagerly, but only child nodes
- * which are directly accessed by user of data object modification.
+ * {@link LazyDataObjectModification} represents Data tree change event, but whole tree is not translated or resolved
+ * eagerly, but only child nodes which are directly accessed by user of data object modification.
  *
  * @param <T> Type of Binding Data Object
  */
-final class LazyDataObjectModification<T extends DataObject> implements DataObjectModification<T> {
-    private static final Logger LOG = LoggerFactory.getLogger(LazyDataObjectModification.class);
-
-    private final BindingDataObjectCodecTreeNode<T> codec;
-    private final DataTreeCandidateNode domData;
-    private final PathArgument identifier;
-
-    private volatile List<LazyDataObjectModification<?>> childNodesCache;
-    private volatile ModificationType modificationType;
-
-    private LazyDataObjectModification(final BindingDataObjectCodecTreeNode<T> codec,
-            final DataTreeCandidateNode domData) {
-        this.codec = requireNonNull(codec);
-        this.domData = requireNonNull(domData);
-        identifier = codec.deserializePathArgument(domData.getIdentifier());
-    }
-
-    static <T extends DataObject> LazyDataObjectModification<T> create(final BindingDataObjectCodecTreeNode<T> codec,
-            final DataTreeCandidateNode domData) {
-        return new LazyDataObjectModification<>(codec, domData);
-    }
-
-    private static List<LazyDataObjectModification<?>> from(final BindingDataObjectCodecTreeNode<?> parentCodec,
-            final Collection<DataTreeCandidateNode> domChildNodes) {
-        final var result = new ArrayList<LazyDataObjectModification<?>>(domChildNodes.size());
-        populateList(result, parentCodec, domChildNodes);
-        return result;
-    }
-
-    private static void populateList(final List<LazyDataObjectModification<?>> result,
-            final BindingDataObjectCodecTreeNode<?> parentCodec,
-            final Collection<DataTreeCandidateNode> domChildNodes) {
-        for (var domChildNode : domChildNodes) {
-            if (domChildNode.getModificationType() != UNMODIFIED) {
-                final var type = BindingStructuralType.from(domChildNode);
-                if (type != BindingStructuralType.NOT_ADDRESSABLE) {
-                    /*
-                     * Even if type is UNKNOWN, from perspective of BindingStructuralType we try to load codec for it.
-                     * We will use that type to further specify debug log.
-                     */
-                    try {
-                        final var childCodec = parentCodec.yangPathArgumentChild(domChildNode.getIdentifier());
-                        // FIXME: MDSAL-820: this no longer holds
-                        verify(childCodec instanceof BindingDataObjectCodecTreeNode, "Unhandled codec %s for type %s",
-                            childCodec, type);
-                        populateList(result, type, (BindingDataObjectCodecTreeNode<?>) childCodec, domChildNode);
-                    } catch (final IllegalArgumentException e) {
-                        if (type == BindingStructuralType.UNKNOWN) {
-                            LOG.debug("Unable to deserialize unknown DOM node {}", domChildNode, e);
-                        } else {
-                            LOG.debug("Binding representation for DOM node {} was not found", domChildNode, e);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private static void populateList(final List<LazyDataObjectModification<? extends DataObject>> result,
-            final BindingStructuralType type, final BindingDataObjectCodecTreeNode<?> childCodec,
-            final DataTreeCandidateNode domChildNode) {
-        switch (type) {
-            case INVISIBLE_LIST:
-                // We use parent codec intentionally.
-                populateListWithSingleCodec(result, childCodec, domChildNode.getChildNodes());
-                break;
-            case INVISIBLE_CONTAINER:
-                populateList(result, childCodec, domChildNode.getChildNodes());
-                break;
-            case UNKNOWN:
-            case VISIBLE_CONTAINER:
-                result.add(create(childCodec, domChildNode));
-                break;
-            default:
-        }
-    }
-
-    private static void populateListWithSingleCodec(final List<LazyDataObjectModification<? extends DataObject>> result,
-            final BindingDataObjectCodecTreeNode<?> codec, final Collection<DataTreeCandidateNode> childNodes) {
-        for (var child : childNodes) {
-            if (child.getModificationType() != UNMODIFIED) {
-                result.add(create(codec, child));
-            }
-        }
-    }
-
-    @Override
-    public T getDataBefore() {
-        return deserialize(domData.getDataBefore());
-    }
-
-    @Override
-    public T getDataAfter() {
-        return deserialize(domData.getDataAfter());
-    }
-
-    @Override
-    public Class<T> getDataType() {
-        return codec.getBindingClass();
-    }
-
-    @Override
-    public PathArgument getIdentifier() {
-        return identifier;
+final class LazyDataObjectModification<T extends DataObject>
+        extends AbstractDataObjectModification<T, BindingDataObjectCodecTreeNode<T>> {
+    LazyDataObjectModification(final BindingDataObjectCodecTreeNode<T> codec, final DataTreeCandidateNode domData) {
+        super(domData, codec, codec.deserializePathArgument(domData.getIdentifier()));
     }
 
     @Override
-    public ModificationType getModificationType() {
-        var localType = modificationType;
-        if (localType != null) {
-            return localType;
-        }
-
-        modificationType = localType = switch (domData.getModificationType()) {
-            case APPEARED, WRITE -> ModificationType.WRITE;
-            case DISAPPEARED, DELETE -> ModificationType.DELETE;
-            case SUBTREE_MODIFIED -> resolveSubtreeModificationType();
-            default ->
-                // TODO: Should we lie about modification type instead of exception?
-                throw new IllegalStateException("Unsupported DOM Modification type " + domData.getModificationType());
-        };
-        return localType;
-    }
-
-    private @NonNull ModificationType resolveSubtreeModificationType() {
-        return switch (codec.getChildAddressabilitySummary()) {
-            case ADDRESSABLE ->
-                // All children are addressable, it is safe to report SUBTREE_MODIFIED
-                ModificationType.SUBTREE_MODIFIED;
-            case UNADDRESSABLE ->
-                // All children are non-addressable, report WRITE
-                ModificationType.WRITE;
-            case MIXED -> {
-                // This case is not completely trivial, as we may have NOT_ADDRESSABLE nodes underneath us. If that
-                // is the case, we need to turn this modification into a WRITE operation, so that the user is able
-                // to observe those nodes being introduced. This is not efficient, but unfortunately unavoidable,
-                // as we cannot accurately represent such changes.
-                for (DataTreeCandidateNode child : domData.getChildNodes()) {
-                    if (BindingStructuralType.recursiveFrom(child) == BindingStructuralType.NOT_ADDRESSABLE) {
-                        // We have a non-addressable child, turn this modification into a write
-                        yield ModificationType.WRITE;
-                    }
-                }
-
-                // No unaddressable children found, proceed in addressed mode
-                yield ModificationType.SUBTREE_MODIFIED;
-            }
-        };
-    }
-
-    @Override
-    public List<LazyDataObjectModification<?>> getModifiedChildren() {
-        var local = childNodesCache;
-        if (local == null) {
-            childNodesCache = local = from(codec, domData.getChildNodes());
-        }
-        return local;
-    }
-
-    @Override
-    public <C extends ChildOf<? super T>> List<DataObjectModification<C>> getModifiedChildren(
-            final Class<C> childType) {
-        return streamModifiedChildren(childType).collect(Collectors.toList());
-    }
-
-    @Override
-    public <H extends ChoiceIn<? super T> & DataObject, C extends ChildOf<? super H>>
-            List<DataObjectModification<C>> getModifiedChildren(final Class<H> caseType, final Class<C> childType) {
-        return streamModifiedChildren(childType)
-                .filter(child -> caseType.equals(child.identifier.getCaseType().orElse(null)))
-                .collect(Collectors.toList());
-    }
-
-    @SuppressWarnings("unchecked")
-    private <C extends DataObject> Stream<LazyDataObjectModification<C>> streamModifiedChildren(
-            final Class<C> childType) {
-        return getModifiedChildren().stream()
-                .filter(child -> childType.isAssignableFrom(child.getDataType()))
-                .map(child -> (LazyDataObjectModification<C>) child);
+    Collection<DataTreeCandidateNode> domChildNodes() {
+        return domData.getChildNodes();
     }
 
     @Override
-    public DataObjectModification<?> getModifiedChild(final PathArgument arg) {
-        final var domArgumentList = new ArrayList<YangInstanceIdentifier.PathArgument>();
-        final var childCodec = codec.bindingPathArgumentChild(arg, domArgumentList);
-        final var toEnter = domArgumentList.iterator();
-        var current = domData;
-        while (toEnter.hasNext() && current != null) {
-            current = current.getModifiedChild(toEnter.next()).orElse(null);
-        }
-        return current != null && current.getModificationType() != UNMODIFIED ? create(childCodec, current) : null;
+    org.opendaylight.yangtools.yang.data.tree.api.ModificationType domModificationType() {
+        return domData.getModificationType();
     }
 
     @Override
-    @SuppressWarnings("unchecked")
-    public <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C>
-            getModifiedChildListItem(final Class<C> listItem, final K listKey) {
-        return (DataObjectModification<C>) getModifiedChild(IdentifiableItem.of(listItem, listKey));
+    T deserialize(final NormalizedNode normalized) {
+        return codec.deserialize(normalized);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
-    public <H extends ChoiceIn<? super T> & DataObject, C extends Identifiable<K> & ChildOf<? super H>,
-            K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(final Class<H> caseType,
-                    final Class<C> listItem, final K listKey) {
-        return (DataObjectModification<C>) getModifiedChild(IdentifiableItem.of(caseType, listItem, listKey));
+    DataTreeCandidateNode firstModifiedChild(final PathArgument arg) {
+        return domData.getModifiedChild(arg).orElse(null);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
-    public <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(final Class<C> child) {
-        return (DataObjectModification<C>) getModifiedChild(Item.of(child));
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <H extends ChoiceIn<? super T> & DataObject, C extends ChildOf<? super H>> DataObjectModification<C>
-            getModifiedChildContainer(final Class<H> caseType, final Class<C> child) {
-        return (DataObjectModification<C>) getModifiedChild(Item.of(caseType, child));
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(
-            final Class<C> augmentation) {
-        return (DataObjectModification<C>) getModifiedChild(Item.of(augmentation));
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this).add("identifier", identifier).add("domData", domData).toString();
-    }
-
-    private T deserialize(final Optional<NormalizedNode> dataAfter) {
-        return dataAfter.map(codec::deserialize).orElse(null);
+    ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper).add("domData", domData);
     }
 }
index 8c89a5a128ded5f30c7a1ae38ab7dc42d8bad367..530b490649a1353c6738a742c72d96032f2fb413 100644 (file)
@@ -13,15 +13,16 @@ import com.google.common.base.MoreObjects;
 import java.util.ArrayList;
 import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.api.DataObjectModification;
 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
 import org.opendaylight.mdsal.binding.api.DataTreeModification;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeCandidate;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+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.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
 
 /**
@@ -37,37 +38,55 @@ final class LazyDataTreeModification<T extends DataObject> implements DataTreeMo
 
     private LazyDataTreeModification(final DataTreeIdentifier<T> path, final DataObjectModification<T> modification) {
         this.path = requireNonNull(path);
-        this.rootNode = requireNonNull(modification);
+        rootNode = requireNonNull(modification);
     }
 
     @SuppressWarnings({"unchecked", "rawtypes"})
-    static <T extends DataObject> DataTreeModification<T> create(final CurrentAdapterSerializer serializer,
-            final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType) {
-        final InstanceIdentifier<?> bindingPath = serializer.coerceInstanceIdentifier(domChange.getRootPath());
-        final BindingDataObjectCodecTreeNode<?> codec = serializer.getSubtreeCodec(bindingPath);
-        final DataTreeIdentifier<?> path = DataTreeIdentifier.create(datastoreType, bindingPath);
-        return new LazyDataTreeModification(path, LazyDataObjectModification.create(codec, domChange.getRootNode()));
+    static <T extends DataObject> @Nullable DataTreeModification<T> from(final CurrentAdapterSerializer serializer,
+            final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType, final Class<T> augment) {
+        final var bindingPath = createBindingPath(serializer, domChange.getRootPath(), augment);
+        final var codec = serializer.getSubtreeCodec(bindingPath);
+        final var modification = LazyDataObjectModification.from(codec, domChange.getRootNode());
+        return modification == null ? null
+            : new LazyDataTreeModification(DataTreeIdentifier.create(datastoreType, bindingPath), modification);
     }
 
     @SuppressWarnings({"unchecked", "rawtypes"})
-    static <T extends DataObject> DataTreeModification<T> create(final CurrentAdapterSerializer serializer,
-            final DOMDataTreeCandidate candidate) {
-        final DOMDataTreeIdentifier domRootPath = candidate.getRootPath();
-        final InstanceIdentifier<?> bindingPath = serializer.coerceInstanceIdentifier(domRootPath.getRootIdentifier());
-        final BindingDataObjectCodecTreeNode<?> codec = serializer.getSubtreeCodec(bindingPath);
-        return new LazyDataTreeModification(DataTreeIdentifier.create(domRootPath.getDatastoreType(), bindingPath),
-            LazyDataObjectModification.create(codec, candidate.getRootNode()));
+    static <T extends DataObject> @Nullable DataTreeModification<T> from(final CurrentAdapterSerializer serializer,
+            final DOMDataTreeCandidate candidate, final Class<T> augment) {
+        final var domRootPath = candidate.getRootPath();
+        final var bindingPath = createBindingPath(serializer, domRootPath.getRootIdentifier(), augment);
+        final var codec = serializer.getSubtreeCodec(bindingPath);
+        final var modification = LazyDataObjectModification.from(codec, candidate.getRootNode());
+        return modification == null ? null
+            : new LazyDataTreeModification(DataTreeIdentifier.create(domRootPath.getDatastoreType(), bindingPath),
+                modification);
     }
 
     static <T extends DataObject> @NonNull List<DataTreeModification<T>> from(final CurrentAdapterSerializer codec,
-            final List<DataTreeCandidate> domChanges, final LogicalDatastoreType datastoreType) {
-        final List<DataTreeModification<T>> result = new ArrayList<>(domChanges.size());
-        for (final DataTreeCandidate domChange : domChanges) {
-            result.add(LazyDataTreeModification.create(codec, domChange, datastoreType));
+            final List<DataTreeCandidate> domChanges, final LogicalDatastoreType datastoreType,
+            final Class<T> augment) {
+        final var result = new ArrayList<DataTreeModification<T>>(domChanges.size());
+        for (var domChange : domChanges) {
+            final var bindingChange = from(codec, domChange, datastoreType, augment);
+            if (bindingChange != null) {
+                result.add(bindingChange);
+            }
         }
         return result;
     }
 
+    // We are given a DOM path, which does not reflect augmentations, as those are not representable in NormalizedNode
+    // world. This method takes care of reconstructing the InstanceIdentifier, appending the missing Augmentation. This
+    // important to get the correct codec into the mix -- otherwise we would be operating on the parent container's
+    // codec and mis-report what is actually going on.
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private static @NonNull InstanceIdentifier<?> createBindingPath(final CurrentAdapterSerializer serializer,
+            final YangInstanceIdentifier domPath, final Class<?> augment) {
+        final var bindingPath = serializer.coerceInstanceIdentifier(domPath);
+        return augment == null ? bindingPath : bindingPath.augmentation((Class) augment.asSubclass(Augmentation.class));
+    }
+
     @Override
     public DataObjectModification<T> getRootNode() {
         return rootNode;
index c57497c0de47e56b3ce1557c999d62dba086d150..65fd02ff53852b77cf24f4bc6d461eed0139a982 100644 (file)
@@ -19,7 +19,7 @@ import org.opendaylight.mdsal.binding.api.query.QueryExpression;
 import org.opendaylight.mdsal.binding.dom.adapter.query.LambdaDecoder.LambdaTarget;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.dom.api.query.DOMQuery;
 import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate;
 import org.opendaylight.yangtools.concepts.Immutable;
@@ -50,7 +50,7 @@ final class QueryBuilderState {
     private YangInstanceIdentifier relativeSelect;
 
     QueryBuilderState(final DefaultQueryFactory factory, final InstanceIdentifier<?> root) {
-        this.codec = factory.codec();
+        codec = factory.codec();
         this.factory = factory;
         this.root = fromBinding(root);
     }
@@ -67,7 +67,7 @@ final class QueryBuilderState {
     @NonNull BoundMethod bindMethod(final @NonNull InstanceIdentifier<?> bindingPath,
             final @NonNull LeafReference<?, ?> ref) {
         // Verify bindingPath, which will give us something to fish in
-        final BindingDataObjectCodecTreeNode<?> targetCodec = codec.getSubtreeCodec(bindingPath);
+        final CommonDataObjectCodecTreeNode<?> targetCodec = codec.getSubtreeCodec(bindingPath);
         checkState(targetCodec != null, "Failed to find codec for %s", bindingPath);
 
         final WithStatus targetSchema = targetCodec.getSchema();
index 89f1148b4baf25b453032f52ce1cdb4a11d8ea38..7777dd77bd11ec68f0c2ee0a443c6be7b121468e 100644 (file)
@@ -43,18 +43,22 @@ public class BindingDOMDataTreeCommitCohortAdapterTest {
         final AdapterContext codec = new ConstantAdapterContext(registry);
 
         final BindingDOMDataTreeCommitCohortAdapter<?> adapter =
-                new BindingDOMDataTreeCommitCohortAdapter<>(codec, cohort);
+                new BindingDOMDataTreeCommitCohortAdapter<>(codec, cohort, null);
         assertNotNull(adapter);
 
         final DOMDataTreeCandidate domDataTreeCandidate = mock(DOMDataTreeCandidate.class);
         final DOMDataTreeIdentifier domDataTreeIdentifier =
                 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.empty());
-        doReturn(InstanceIdentifier.create(BooleanContainer.class)).when(registry).fromYangInstanceIdentifier(any());
+        final var bindingPath = InstanceIdentifier.create(BooleanContainer.class);
+        doReturn(bindingPath).when(registry).fromYangInstanceIdentifier(any());
         final BindingDataObjectCodecTreeNode<?> bindingCodecTreeNode = mock(BindingDataObjectCodecTreeNode.class);
         doReturn(bindingCodecTreeNode).when(registry).getSubtreeCodec(any(InstanceIdentifier.class));
         doReturn(domDataTreeIdentifier).when(domDataTreeCandidate).getRootPath();
         doReturn(mock(DataTreeCandidateNode.class)).when(domDataTreeCandidate).getRootNode();
-        assertNotNull(LazyDataTreeModification.create(codec.currentSerializer(), domDataTreeCandidate));
+        doReturn(bindingPath.getPathArguments().iterator().next()).when(bindingCodecTreeNode)
+            .deserializePathArgument(null);
+
+        assertNotNull(LazyDataTreeModification.from(codec.currentSerializer(), domDataTreeCandidate, null));
 
         final Object txId = new Object();
 
index 7383c4d037e572bcd6f53d45212bf249eede5e59..cf739f471fba2b0acb9955729bc875311341f5f3 100644 (file)
@@ -21,18 +21,8 @@ import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractSchemaAwareTest;
 import org.opendaylight.mdsal.binding.dom.codec.impl.BindingCodecContext;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.OpendaylightTestRpcServiceService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugment;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.XMLNamespace;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
@@ -40,18 +30,6 @@ import org.opendaylight.yangtools.yang.model.spi.AbstractSchemaContext;
 
 public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest {
 
-    private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
-    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
-            .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).build();
-    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
-        .augmentation(TreeLeafOnlyAugment.class);
-    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
-        .augmentation(TreeComplexUsesAugment.class);
-    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
-    private static final QName NAME_QNAME = QName.create(Top.QNAME, "name");
-    private static final YangInstanceIdentifier BI_TOP_LEVEL_LIST = YangInstanceIdentifier.builder().node(Top.QNAME)
-        .node(TopLevelList.QNAME).nodeWithKey(TopLevelList.QNAME, NAME_QNAME, TOP_FOO_KEY.getName()).build();
-
     private CurrentAdapterSerializer serializer;
 
     @Override
@@ -59,20 +37,6 @@ public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest {
         serializer = new CurrentAdapterSerializer(new BindingCodecContext(runtimeContext));
     }
 
-    @Test
-    public void testComplexAugmentationSerialization() {
-        final PathArgument lastArg = serializer.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES).getLastPathArgument();
-        assertTrue(lastArg instanceof AugmentationIdentifier);
-    }
-
-    @Test
-    public void testLeafOnlyAugmentationSerialization() {
-        final PathArgument leafOnlyLastArg = serializer.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY)
-            .getLastPathArgument();
-        assertTrue(leafOnlyLastArg instanceof AugmentationIdentifier);
-        assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
-    }
-
     @Test
     public void testGetRpcMethodToQName() {
         assertTrue(serializer.createQNameToMethod(OpendaylightTestRpcServiceService.class)
index 9977962b2791c393041a035e56ea3751952f354b..076ed09b58827c36d306794f8074bd1a25f0fa0b 100644 (file)
@@ -134,7 +134,9 @@ public class Bug1418AugmentationTest extends AbstractDataTreeChangeListenerTest
 
         final TestListener<TreeComplexUsesAugment> listener = createListener(CONFIGURATION, COMPLEX_AUGMENT,
                 added(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugmentBefore),
-                replaced(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugmentBefore,
+                // While we are overwriting the augment, at the transaction ends up replacing one child with another,
+                // so the Augmentation ends up not being overwritten, but modified
+                subtreeModified(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugmentBefore,
                         complexUsesAugmentAfter));
 
         writeTx = getDataBroker().newWriteOnlyTransaction();
index 9a7b859184d927407e7987f196027c8bd593eadc..5f968a95e88e09a053e07871a492a777e6bfba9e 100644 (file)
@@ -31,12 +31,15 @@ public class LazyDataTreeModificationTest {
         final DOMDataTreeCandidate domDataTreeCandidate = mock(DOMDataTreeCandidate.class);
         final DOMDataTreeIdentifier domDataTreeIdentifier =
                 new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.empty());
-        doReturn(InstanceIdentifier.create(BooleanContainer.class)).when(registry).fromYangInstanceIdentifier(any());
+        final var bindingPath = InstanceIdentifier.create(BooleanContainer.class);
+        doReturn(bindingPath).when(registry).fromYangInstanceIdentifier(any());
         final BindingDataObjectCodecTreeNode<?> bindingCodecTreeNode = mock(BindingDataObjectCodecTreeNode.class);
         doReturn(bindingCodecTreeNode).when(registry).getSubtreeCodec(any(InstanceIdentifier.class));
         doReturn(domDataTreeIdentifier).when(domDataTreeCandidate).getRootPath();
         doReturn(mock(DataTreeCandidateNode.class)).when(domDataTreeCandidate).getRootNode();
+        doReturn(bindingPath.getPathArguments().iterator().next()).when(bindingCodecTreeNode)
+            .deserializePathArgument(null);
 
-        assertNotNull(LazyDataTreeModification.create(codec.currentSerializer(), domDataTreeCandidate));
+        assertNotNull(LazyDataTreeModification.from(codec.currentSerializer(), domDataTreeCandidate, null));
     }
 }
\ No newline at end of file
diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingAugmentationCodecTreeNode.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingAugmentationCodecTreeNode.java
new file mode 100644 (file)
index 0000000..9550c93
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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 static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+@Beta
+public interface BindingAugmentationCodecTreeNode<T extends Augmentation<?>>
+        extends CommonDataObjectCodecTreeNode<T> {
+    /**
+     * Return an {@link Augmentation} instance backed by specified parent data. Implementations are free to return
+     * either an eager transformation (not retaining {@code parentData}), or a lazy proxy. In both cases they must
+     * ensure the result has at least one property populated (i.e. is not empty).
+     *
+     * @return An Augmentation, or {@code null} if the augmentation would be empty
+     * @throws NullPointerException if {@code parentData} is {@code null}
+     * @throws IllegalArgumentException if {@code parentData} is not a compatible parent
+     */
+    @Nullable T filterFrom(@NonNull DataContainerNode parentData);
+
+    /**
+     * Return an {@link Augmentation} instance backed by specified parent data. Implementations are free to return
+     * either an eager transformation (not retaining {@code parentData}), or a lazy proxy. In both cases they must
+     * ensure the result has at least one property populated (i.e. is not empty).
+     *
+     * @return An Augmentation, or {@code null} if the augmentation would be empty
+     * @throws NullPointerException if {@code parentData} is {@code null}
+     * @throws IllegalArgumentException if {@code parentData} is not a compatible {@link DataContainerNode}
+     */
+    default @Nullable T filterFrom(final @NonNull NormalizedNode parentData) {
+        if (requireNonNull(parentData) instanceof DataContainerNode parentContainer) {
+            return filterFrom(parentContainer);
+        }
+        throw new IllegalArgumentException("Unsupported parent " + parentData.contract());
+    }
+
+    /**
+     * Write the contents of an {@link Augmentation} into a writer. The writer must beinitialized at the
+     * augmentations's {@link Augmentable} parent's equivalent {@link DataContainerNode}.
+     *
+     * @param writer Writer to stream to
+     * @param data Data to stream
+     * @throws NullPointerException if any argument is {@code null}
+     * @throws IOException if a streaming error occurs
+     */
+    void streamTo(@NonNull NormalizedNodeStreamWriter writer, @NonNull T data) throws IOException;
+
+    /**
+     * Returns the {@link PathArgument}s of items contained in this {@link Augmentation}.
+     *
+     * @return A non-empty set of path arguments
+     */
+    @NonNull ImmutableSet<PathArgument> childPathArguments();
+
+    /**
+     * Returns the {@link Class}es of items contain in this {@link Augmentation}.
+     *
+     * @return A non-empty set of classes
+     */
+    @NonNull ImmutableSet<Class<?>> childBindingClasses();
+}
index 37ed59f5b169f515327f57dabdf87010db011813..b1f3a38cf36c8a643badc215e73c6b2630039727 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.api;
 
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.annotations.Beta;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
@@ -23,6 +25,31 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absol
  */
 // TODO: Add more detailed documentation
 public interface BindingCodecTree extends BindingDataObjectCodecTreeParent<Empty> {
+    /**
+     * A DTO holding a {@link CommonDataObjectCodecTreeNode} and the corresponding {@link YangInstanceIdentifier}.
+     *
+     * @param <T> {@link DataObject} type
+     */
+    record CodecWithPath<T extends DataObject>(
+            @NonNull CommonDataObjectCodecTreeNode<T> codec,
+            @NonNull YangInstanceIdentifier path) {
+        public CodecWithPath {
+            requireNonNull(codec);
+            requireNonNull(path);
+        }
+    }
+
+    /**
+     * Look up the codec for specified path, constructing the {@link YangInstanceIdentifier} corresponding to it.
+     *
+     * @param <T> DataObject type
+     * @param path Binding path
+     * @return A {@link CodecWithPath}
+     * @throws NullPointerException if {@code path} is {@code null}
+     * @throws IllegalArgumentException if the codec cannot be resolved
+     */
+    <T extends DataObject> @NonNull CodecWithPath<T> getSubtreeCodecWithPath(InstanceIdentifier<T> path);
+
     /**
      * Look up the codec for specified path.
      *
@@ -32,10 +59,12 @@ public interface BindingCodecTree extends BindingDataObjectCodecTreeParent<Empty
      * @throws NullPointerException if {@code path} is {@code null}
      * @throws IllegalArgumentException if the codec cannot be resolved
      */
-    <T extends DataObject> @NonNull BindingDataObjectCodecTreeNode<T> getSubtreeCodec(InstanceIdentifier<T> path);
+    <T extends DataObject> @NonNull CommonDataObjectCodecTreeNode<T> getSubtreeCodec(InstanceIdentifier<T> path);
 
+    // FIXME: NonNull and throwing exception
     @Nullable BindingCodecTreeNode getSubtreeCodec(YangInstanceIdentifier path);
 
+    // FIXME: NonNull and throwing exception
     @Nullable BindingCodecTreeNode getSubtreeCodec(Absolute path);
 
     /**
index 6804739b0a1ff53c521ab529ba62e973a7b73246..f4d171401d79f61addc7c9cb1e4399e3b9377087 100644 (file)
@@ -9,109 +9,13 @@ package org.opendaylight.mdsal.binding.dom.codec.api;
 
 import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableCollection;
-import java.util.List;
-import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.binding.BindingObject;
 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;
 
 @Beta
 public interface BindingDataObjectCodecTreeNode<T extends DataObject>
-        extends BindingDataObjectCodecTreeParent<Empty>, BindingObjectCodecTreeNode<T>, BindingNormalizedNodeCodec<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 #streamChild(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 Optional.empty is supplied class is not
-     *         applicable in context.
-     */
-    <E extends DataObject> Optional<? extends BindingDataObjectCodecTreeNode<E>> possibleStreamChild(
-            @NonNull Class<E> childClass);
-
-    /**
-     * 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 BindingDataObjectCodecTreeNode<?> bindingPathArgumentChild(InstanceIdentifier.@NonNull PathArgument arg,
-            @Nullable List<YangInstanceIdentifier.PathArgument> builder);
-
-    /**
-     * 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).
-     * @throws IllegalArgumentException If supplied {@code arg} is not valid.
-     */
-    @Beta
-    YangInstanceIdentifier.@Nullable PathArgument serializePathArgument(InstanceIdentifier.@Nullable PathArgument arg);
-
-    /**
-     * Deserializes path argument for current node.
-     *
-     * @param arg Yang Path Argument, may be null if Yang Instance Identifier does not have
-     *         representation for current node (e.g. case).
-     * @return Binding Path Argument, may be null if Binding Instance Identifier does not have
-     *        representation for current node (e.g. choice or case).
-     * @throws IllegalArgumentException If supplied {@code arg} is not valid.
-     */
-    @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();
-
+        extends CommonDataObjectCodecTreeNode<T>, BindingNormalizedNodeCodec<T> {
     /**
      * Returns codec which uses caches serialization / deserialization results.
      *
@@ -124,22 +28,4 @@ public interface BindingDataObjectCodecTreeNode<T extends DataObject>
      */
     @NonNull BindingNormalizedNodeCachingCodec<T> createCachingCodec(
             @NonNull ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier);
-
-    /**
-     * 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 2e561e59a3c2e6129ebc709274d2a2cb803f58c4..477140bb57308572f7f954cbf1b3e566d1e56fdc 100644 (file)
@@ -44,5 +44,5 @@ public interface BindingDataObjectCodecTreeParent<T> {
      * @return Context of child
      * @throws IllegalArgumentException If supplied child class is not valid in specified context.
      */
-    <E extends DataObject> @NonNull BindingDataObjectCodecTreeNode<E> streamChild(@NonNull Class<E> childClass);
+    <E extends DataObject> @NonNull CommonDataObjectCodecTreeNode<E> streamChild(@NonNull Class<E> childClass);
 }
index 773b1778717cf414193ecfa28b369517fa0fa453..b026c7412bacbf003447afb79da73d965e79ddbc 100644 (file)
@@ -7,13 +7,18 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.api;
 
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import java.time.Instant;
 import java.util.Map.Entry;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yangtools.yang.binding.Action;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.BaseNotification;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -24,15 +29,56 @@ import org.opendaylight.yangtools.yang.binding.RpcOutput;
 import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 
 /**
- * Serialization service, which provides two-way serialization between Java
- * Binding Data representation and NormalizedNode representation.
+ * Serialization service, which provides two-way serialization between Java Binding Data representation and
+ * NormalizedNode representation.
  */
 public interface BindingNormalizedNodeSerializer {
+    /**
+     * Result of a {@link BindingNormalizedNodeSerializer#toNormalizedNode(InstanceIdentifier, DataObject)}. Since the
+     * Binding {@link Augmentation} does not have an exact equivalent, there are two specializations of this class:
+     * {@link NodeResult} and {@link AugmentationResult}.
+     */
+    sealed interface NormalizedResult {
+        /**
+         * Return the {@link YangInstanceIdentifier} path of this result.
+         *
+         * @return A {@link YangInstanceIdentifier}
+         */
+        @NonNull YangInstanceIdentifier path();
+    }
+
+    /**
+     * A {@link NormalizedResult} for an {@link Augmentation}.
+     *
+     * @param path A YangInstanceIdentifier identifying the parent of this augmentation
+     * @param possibleChildren {@link PathArgument}s of each possible child
+     * @param children Augmentation children
+     */
+    record AugmentationResult(
+            @NonNull YangInstanceIdentifier path,
+            @NonNull ImmutableSet<PathArgument> possibleChildren,
+            @NonNull ImmutableList<DataContainerChild> children) implements NormalizedResult {
+        public AugmentationResult {
+            requireNonNull(path);
+            requireNonNull(possibleChildren);
+            requireNonNull(children);
+        }
+    }
+
+    record NodeResult(@NonNull YangInstanceIdentifier path, @NonNull NormalizedNode node) implements NormalizedResult {
+        public NodeResult {
+            requireNonNull(path);
+            requireNonNull(node);
+        }
+    }
+
     /**
      * Translates supplied Binding Instance Identifier into NormalizedNode instance identifier.
      *
@@ -58,11 +104,10 @@ public interface BindingNormalizedNodeSerializer {
      *
      * @param path Binding Instance Identifier pointing to data
      * @param data Data object representing data
-     * @return NormalizedNode representation
+     * @return {@link NormalizedResult} representation
      * @throws IllegalArgumentException If supplied Instance Identifier is not valid.
      */
-    <T extends DataObject> @NonNull Entry<YangInstanceIdentifier, NormalizedNode> toNormalizedNode(
-            InstanceIdentifier<T> path, T data);
+    <T extends DataObject> @NonNull NormalizedResult toNormalizedNode(InstanceIdentifier<T> path, T data);
 
     /**
      * Translates supplied YANG Instance Identifier and NormalizedNode into Binding data.
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
new file mode 100644 (file)
index 0000000..1e3a25b
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * 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 com.google.common.annotations.Beta;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+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;
+
+/**
+ * Common interface shared between {@link BindingDataObjectCodecTreeNode} and {@link BindingAugmentationCodecTreeNode}.
+ * This interface should never be implemented on its own.
+ *
+ * @param <T> DataObject type
+ */
+@Beta
+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 #streamChild(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 Optional.empty is supplied class is not
+     *         applicable in context.
+     */
+    <E extends DataObject> Optional<? extends CommonDataObjectCodecTreeNode<E>> possibleStreamChild(
+            @NonNull Class<E> childClass);
+
+    /**
+     * 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);
+
+    /**
+     * 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).
+     * @throws IllegalArgumentException If supplied {@code arg} is not valid.
+     */
+    @Beta
+    YangInstanceIdentifier.@Nullable PathArgument serializePathArgument(InstanceIdentifier.@Nullable PathArgument arg);
+
+    /**
+     * Deserializes path argument for current node.
+     *
+     * @param arg Yang Path Argument, may be null if Yang Instance Identifier does not have
+     *         representation for current node (e.g. case).
+     * @return Binding Path Argument, may be null if Binding Instance Identifier does not have
+     *        representation for current node (e.g. choice or case).
+     * @throws IllegalArgumentException If supplied {@code arg} is not valid.
+     */
+    @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 39f8d6ede0c10e32c01f99a0b38aa4e152887afe..ac225ac44ba1885b5e8860090127dce4657ec0f1 100644 (file)
@@ -13,11 +13,11 @@ import java.time.Instant;
 import java.util.Map.Entry;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingIdentityCodec;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCodec;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingLazyContainerNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
+import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.BaseNotification;
@@ -62,8 +62,7 @@ public abstract class ForwardingBindingDOMCodecServices extends ForwardingObject
     }
 
     @Override
-    public <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode> toNormalizedNode(
-            final InstanceIdentifier<T> path, final T data) {
+    public <T extends DataObject> NormalizedResult toNormalizedNode(final InstanceIdentifier<T> path, final T data) {
         return delegate().toNormalizedNode(path, data);
     }
 
@@ -161,11 +160,16 @@ public abstract class ForwardingBindingDOMCodecServices extends ForwardingObject
     @Override
     public BindingStreamEventWriter newRpcWriter(final Class<? extends DataContainer> rpcInputOrOutput,
             final NormalizedNodeStreamWriter streamWriter) {
-        return delegate().newRpcWriter(rpcInputOrOutput,streamWriter);
+        return delegate().newRpcWriter(rpcInputOrOutput, streamWriter);
     }
 
     @Override
-    public <T extends DataObject> BindingDataObjectCodecTreeNode<T> getSubtreeCodec(final InstanceIdentifier<T> path) {
+    public <T extends DataObject> CodecWithPath<T> getSubtreeCodecWithPath(final InstanceIdentifier<T> path) {
+        return delegate().getSubtreeCodecWithPath(path);
+    }
+
+    @Override
+    public <T extends DataObject> CommonDataObjectCodecTreeNode<T> getSubtreeCodec(final InstanceIdentifier<T> path) {
         return delegate().getSubtreeCodec(path);
     }
 
@@ -195,7 +199,7 @@ public abstract class ForwardingBindingDOMCodecServices extends ForwardingObject
     }
 
     @Override
-    public <E extends DataObject> BindingDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
+    public <E extends DataObject> CommonDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
         return delegate().streamChild(childClass);
     }
 }
index 59dcdf932fc632c9af6c34ca950df5ae6bd7864f..71c4d0844d5f2a1c8f814d0c236331456e2b0126 100644 (file)
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-data-impl</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>yang-data-util</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-model-api</artifactId>
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
new file mode 100644 (file)
index 0000000..9fff1e6
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * 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 com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.lang.invoke.MethodHandle;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
+import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
+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.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+
+/**
+ * Abstract base for {@link DataObjectCodecContext} and {@link AugmentationNodeContext}. They share most of their
+ * mechanics, but notably:
+ * <ol>
+ *   <li>DataObjectCodecContext has an exact DistinctNodeContainer and YangInstanceIdentifier mapping and can be the
+ *       target of augmentations (i.e. can implement Augmentable contract)</li>
+ *   <li>AugmentationNodeContext has neither of those traits and really is just a filter of its parent
+ *       DistinctNodeContainer</li>
+ * </ol>
+ *
+ * <p>
+ * Unfortunately {@code Augmentation} is a also a {@link DataObject}, so things get a bit messy.
+ *
+ * <p>
+ * While this class is public, it not part of API surface and is an implementation detail. The only reason for it being
+ * public is that it needs to be accessible by code generated at runtime.
+ */
+public abstract class AbstractDataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
+        extends DataContainerCodecContext<D, T> {
+    private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
+    private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
+    private final ImmutableMap<PathArgument, NodeContextSupplier> byYang;
+    private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
+    private final MethodHandle proxyConstructor;
+
+    AbstractDataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
+            final CodecDataObjectAnalysis<T> analysis) {
+        super(prototype);
+        byBindingArgClass = analysis.byBindingArgClass;
+        byStreamClass = analysis.byStreamClass;
+        byYang = analysis.byYang;
+        leafChild = analysis.leafNodes;
+        proxyConstructor = analysis.proxyConstructor;
+    }
+
+    @Override
+    public final WithStatus getSchema() {
+        // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
+        return (WithStatus) getType().statement();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
+        return (DataContainerCodecContext<C, ?>) childNonNull(streamChildPrototype(childClass), childClass,
+            "Child %s is not valid child of %s", getBindingClass(), childClass).get();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public final <C extends DataObject> Optional<DataContainerCodecContext<C, ?>> possibleStreamChild(
+            final Class<C> childClass) {
+        final var childProto = streamChildPrototype(childClass);
+        if (childProto != null) {
+            return Optional.of((DataContainerCodecContext<C, ?>) childProto.get());
+        }
+        return Optional.empty();
+    }
+
+    @Nullable DataContainerCodecPrototype<?> streamChildPrototype(final @NonNull Class<?> childClass) {
+        return byStreamClass.get(childClass);
+    }
+
+    @Override
+    public final DataContainerCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
+            final List<PathArgument> builder) {
+        final var argType = arg.getType();
+        final var context = childNonNull(pathChildPrototype(argType), argType,
+            "Class %s is not valid child of %s", argType, getBindingClass())
+            .get();
+        if (context instanceof ChoiceNodeCodecContext<?> choice) {
+            choice.addYangPathArgument(arg, builder);
+
+            final var caseType = arg.getCaseType();
+            final var type = arg.getType();
+            final DataContainerCodecContext<?, ?> caze;
+            if (caseType.isPresent()) {
+                // Non-ambiguous addressing this should not pose any problems
+                caze = choice.streamChild(caseType.orElseThrow());
+            } else {
+                caze = choice.getCaseByChildClass(type);
+            }
+
+            caze.addYangPathArgument(arg, builder);
+            return caze.bindingPathArgumentChild(arg, builder);
+        }
+        context.addYangPathArgument(arg, builder);
+        return context;
+    }
+
+    @Nullable DataContainerCodecPrototype<?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
+        return byBindingArgClass.get(argType);
+    }
+
+    @Override
+    public final NodeCodecContext yangPathArgumentChild(final PathArgument arg) {
+        final var lookup = arg instanceof NodeIdentifierWithPredicates ? new NodeIdentifier(arg.getNodeType()) : arg;
+        return childNonNull(yangChildSupplier(lookup), arg,
+            "Argument %s is not valid child of %s", arg, getSchema())
+            .get();
+    }
+
+    // FIXME: Never contains NodeIdentifierWithPredicates, what about NodeWithValue?
+    //        If it can't be here, it is always NodeIdentifier and we should specify that
+    @Nullable NodeContextSupplier yangChildSupplier(final @NonNull PathArgument arg) {
+        return byYang.get(arg);
+    }
+
+    @SuppressWarnings("checkstyle:illegalCatch")
+    final @NonNull D createBindingProxy(final DistinctNodeContainer<?, ?> node) {
+        try {
+            return (D) proxyConstructor.invokeExact(this, node);
+        } catch (final Throwable e) {
+            Throwables.throwIfUnchecked(e);
+            throw new IllegalStateException(e);
+        }
+    }
+
+    final ValueNodeCodecContext getLeafChild(final String name) {
+        final ValueNodeCodecContext value = leafChild.get(name);
+        if (value == null) {
+            throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass());
+        }
+        return value;
+    }
+
+    final @NonNull ImmutableSet<PathArgument> byYangKeySet() {
+        return byYang.keySet();
+    }
+
+    final @NonNull ImmutableSet<Class<?>> byBindingArgClassKeySet() {
+        return byBindingArgClass.keySet();
+    }
+
+    abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
+        DistinctNodeContainer<PathArgument, NormalizedNode> data);
+}
index acd87508ea4c78fc0e5d4d90ce7005e644a50bcf..b1ec0159aae6e6f2fa48786fed42c8147ce3fcd3 100644 (file)
@@ -16,20 +16,20 @@ import org.opendaylight.mdsal.binding.runtime.api.OutputRuntimeType;
  * This is not really a codec context, but rather a holder of input and output codec contexts.
  */
 final class ActionCodecContext {
-    private final DataContainerCodecContext<?, InputRuntimeType> input;
-    private final DataContainerCodecContext<?, OutputRuntimeType> output;
+    private final DataObjectCodecContext<?, InputRuntimeType> input;
+    private final DataObjectCodecContext<?, OutputRuntimeType> output;
 
-    ActionCodecContext(final DataContainerCodecContext<?, InputRuntimeType> input,
-        final DataContainerCodecContext<?, OutputRuntimeType> output) {
+    ActionCodecContext(final DataObjectCodecContext<?, InputRuntimeType> input,
+            final DataObjectCodecContext<?, OutputRuntimeType> output) {
         this.input = requireNonNull(input);
         this.output = requireNonNull(output);
     }
 
-    DataContainerCodecContext<?, InputRuntimeType> input() {
+    DataObjectCodecContext<?, InputRuntimeType> input() {
         return input;
     }
 
-    DataContainerCodecContext<?, OutputRuntimeType> output() {
+    DataObjectCodecContext<?, OutputRuntimeType> output() {
         return output;
     }
 }
index dcee340dd53edb6616e9ac8cecf0af01d73b7a2b..25eadbf195c7a55ffff3bf277ede79c697dab7ef 100644 (file)
@@ -12,13 +12,13 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.collect.ImmutableMap;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
-import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 /**
  * A base class for {@link DataObject}s which are also {@link Augmentable}, backed by {@link DataObjectCodecContext}.
@@ -44,7 +44,7 @@ public abstract class AugmentableCodecDataObject<T extends DataObject & Augmenta
     @SuppressWarnings("unused")
     private volatile ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> cachedAugmentations;
 
-    protected AugmentableCodecDataObject(final DataObjectCodecContext<T, ?> context,
+    protected AugmentableCodecDataObject(final AbstractDataObjectCodecContext<T, ?> context,
             final DistinctNodeContainer<?, ?> data) {
         super(context, data);
     }
@@ -54,25 +54,24 @@ public abstract class AugmentableCodecDataObject<T extends DataObject & Augmenta
     public final <A extends Augmentation<T>> @Nullable A augmentation(final Class<A> augmentationType) {
         requireNonNull(augmentationType, "Supplied augmentation must not be null.");
 
-        final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> aug = acquireAugmentations();
+        final var aug = acquireAugmentations();
         if (aug != null) {
             return (A) aug.get(augmentationType);
         }
 
         @SuppressWarnings("rawtypes")
-        final Optional<DataContainerCodecContext<?, ?>> optAugCtx = codecContext().possibleStreamChild(
-            (Class) augmentationType);
+        final var optAugCtx = codecContext().possibleStreamChild((Class) augmentationType);
         if (optAugCtx.isPresent()) {
-            final DataContainerCodecContext<?, ?> augCtx = optAugCtx.orElseThrow();
+            final var augCtx = (AugmentationNodeContext<A>) optAugCtx.orElseThrow();
             // Due to binding specification not representing grouping instantiations we can end up having the same
             // augmentation applied to a grouping multiple times. While these augmentations have the same shape, they
             // are still represented by distinct binding classes and therefore we need to make sure the result matches
             // the augmentation the user is requesting -- otherwise a strict receiver would end up with a cryptic
             // ClassCastException.
             if (augmentationType.isAssignableFrom(augCtx.getBindingClass())) {
-                final NormalizedNode augData = codecData().childByArg(augCtx.getDomPathArgument());
-                if (augData != null) {
-                    return (A) augCtx.deserialize(augData);
+                final var augObj = augCtx.filterFrom((DataContainerNode) codecData());
+                if (augObj != null) {
+                    return augObj;
                 }
             }
         }
@@ -90,9 +89,8 @@ public abstract class AugmentableCodecDataObject<T extends DataObject & Augmenta
     }
 
     @SuppressWarnings("unchecked")
-    private ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> loadAugmentations() {
-        final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> ret = ImmutableMap.copyOf(
-            codecContext().getAllAugmentationsFrom(codecData()));
+    private @NonNull ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> loadAugmentations() {
+        final var ret = ImmutableMap.copyOf(codecContext().getAllAugmentationsFrom(codecData()));
         final Object witness = CACHED_AUGMENTATIONS.compareAndExchangeRelease(this, null, ret);
         return witness == null ? ret : (ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>>) witness;
     }
index 59f2e226e816189f3ea7bdc2a420f1e857de0fb2..50573245c43a22c7ab516b8ea4426cb608a18f5d 100644 (file)
@@ -7,25 +7,87 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 
 final class AugmentationNodeContext<D extends DataObject & Augmentation<?>>
-        extends DataObjectCodecContext<D, AugmentRuntimeType> {
-    AugmentationNodeContext(final DataContainerCodecPrototype<AugmentRuntimeType> prototype) {
-        super(prototype);
+        extends AbstractDataObjectCodecContext<D, AugmentRuntimeType> implements BindingAugmentationCodecTreeNode<D> {
+    AugmentationNodeContext(final DataContainerCodecPrototype.Augmentation prototype) {
+        super(prototype, new CodecDataObjectAnalysis<>(prototype, CodecItemFactory.of(), null));
+    }
+
+    @Override
+    public PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
+        if (!bindingArg().equals(arg)) {
+            throw new IllegalArgumentException("Unexpected argument " + arg);
+        }
+        return null;
+    }
+
+    @Override
+    public InstanceIdentifier.PathArgument deserializePathArgument(final PathArgument arg) {
+        if (arg != null) {
+            throw new IllegalArgumentException("Unexpected argument " + arg);
+        }
+        return bindingArg();
+    }
+
+    @Override
+    public D filterFrom(final DataContainerNode parentData) {
+        for (var childArg : ((DataContainerCodecPrototype.Augmentation) prototype).getChildArgs()) {
+            if (parentData.childByArg(childArg) != null) {
+                return createProxy(parentData);
+            }
+        }
+        return null;
+    }
+
+    private @NonNull D createProxy(final @NonNull DataContainerNode parentData) {
+        return createBindingProxy(parentData);
     }
 
     @Override
-    public D deserialize(final NormalizedNode data) {
-        return createBindingProxy(checkDataArgument(AugmentationNode.class, data));
+    public void streamTo(final NormalizedNodeStreamWriter writer, final D data) throws IOException {
+        eventStreamSerializer().serialize(requireNonNull(data), new BindingToNormalizedStreamWriter(this, writer));
+    }
+
+    @Override
+    public ImmutableSet<PathArgument> childPathArguments() {
+        return byYangKeySet();
+    }
+
+    @Override
+    public ImmutableSet<Class<?>> childBindingClasses() {
+        return byBindingArgClassKeySet();
     }
 
     @Override
     protected Object deserializeObject(final NormalizedNode normalizedNode) {
-        return deserialize(normalizedNode);
+        return filterFrom(checkDataArgument(DataContainerNode.class, normalizedNode));
+    }
+
+    @Override
+    protected PathArgument getDomPathArgument() {
+        return null;
+    }
+
+    @Override
+    Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
+            final DistinctNodeContainer<PathArgument, NormalizedNode> data) {
+        return Map.of();
     }
-}
\ No newline at end of file
+}
index 4989f809a2aa9212ae0ee1311e717c55b4647dde..65193771673e1402c6d1607f4e2a666573a44e4b 100644 (file)
@@ -16,6 +16,7 @@ import com.google.common.base.Strings;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.io.File;
@@ -38,11 +39,13 @@ import java.util.concurrent.ExecutionException;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.kohsuke.MetaInfServices;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCodec;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeWriterFactory;
 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.impl.NodeCodecContext.CodecContextFactory;
 import org.opendaylight.mdsal.binding.dom.codec.spi.AbstractBindingNormalizedNodeSerializer;
 import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
@@ -69,6 +72,7 @@ import org.opendaylight.yangtools.yang.binding.RpcInput;
 import org.opendaylight.yangtools.yang.binding.RpcOutput;
 import org.opendaylight.yangtools.yang.common.QName;
 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.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
@@ -119,6 +123,7 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
     }
 
     private static final Logger LOG = LoggerFactory.getLogger(BindingCodecContext.class);
+    private static final @NonNull NodeIdentifier FAKE_NODEID = new NodeIdentifier(QName.create("fake", "fake"));
     private static final File BYTECODE_DIRECTORY;
 
     static {
@@ -272,7 +277,19 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
             checkArgument(currentNode instanceof DataContainerCodecContext,
                 "Unexpected child of non-container node %s", currentNode);
             final var previous = (DataContainerCodecContext<?, ?>) currentNode;
-            final var nextNode = previous.yangPathArgumentChild(domArg);
+            var nextNode = previous.yangPathArgumentChild(domArg);
+
+            /**
+             * Compatibility case: if it's determined the node belongs to augmentation
+             * then insert augmentation path argument in between.
+             */
+            if (nextNode instanceof AugmentationNodeContext<?> augmContext) {
+                if (bindingArguments != null) {
+                    bindingArguments.add(augmContext.bindingArg());
+                }
+                currentNode = nextNode;
+                nextNode = augmContext.yangPathArgumentChild(domArg);
+            }
 
             /*
              * List representation in YANG Instance Identifier consists of two
@@ -487,15 +504,26 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
     }
 
     @Override
-    public <E extends DataObject> BindingDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
+    public <E extends DataObject> CommonDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
         return root.streamChild(childClass);
     }
 
     @Override
     @SuppressWarnings("unchecked")
-    public <T extends DataObject> BindingDataObjectCodecTreeNode<T> getSubtreeCodec(final InstanceIdentifier<T> path) {
+    public <T extends DataObject> CodecWithPath<T> getSubtreeCodecWithPath(final InstanceIdentifier<T> path) {
+        final var yangArgs = new ArrayList<YangInstanceIdentifier.PathArgument>();
+        final var codecContext = getCodecContextNode(path, yangArgs);
+
+        // TODO Do we need defensive check here?
+        return new CodecWithPath<>((CommonDataObjectCodecTreeNode<T>) codecContext,
+            YangInstanceIdentifier.create(yangArgs));
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T extends DataObject> CommonDataObjectCodecTreeNode<T> getSubtreeCodec(final InstanceIdentifier<T> path) {
         // TODO Do we need defensive check here?
-        return (BindingDataObjectCodecTreeNode<T>) getCodecContextNode(path, null);
+        return (CommonDataObjectCodecTreeNode<T>) getCodecContextNode(path, null);
     }
 
     @Override
@@ -519,24 +547,43 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
     }
 
     @Override
-    public <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode> toNormalizedNode(
-            final InstanceIdentifier<T> path, final T data) {
-        final NormalizedNodeResult result = new NormalizedNodeResult();
-        // We create DOM stream writer which produces normalized nodes
-        final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
-
+    public <T extends DataObject> NormalizedResult toNormalizedNode(final InstanceIdentifier<T> path, final T data) {
         // We create Binding Stream Writer which translates from Binding to Normalized Nodes
-        final Entry<YangInstanceIdentifier, BindingStreamEventWriter> writeCtx = newWriterAndIdentifier(path,
-            domWriter);
+        final var yangArgs = new ArrayList<YangInstanceIdentifier.PathArgument>();
+        final var codecContext = getCodecContextNode(path, yangArgs);
+        final var yangPath = YangInstanceIdentifier.create(yangArgs);
+
+        // We create DOM stream writer which produces normalized nodes
+        final var result = new NormalizedNodeResult();
+        final var domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final var bindingWriter = new BindingToNormalizedStreamWriter(codecContext, domWriter);
+        final var augment = codecContext instanceof BindingAugmentationCodecTreeNode<?> augmentNode ? augmentNode
+            : null;
 
-        // We get serializer which reads binding data and uses Binding To Normalized Node writer to write result
         try {
-            getSerializer(path.getTargetType()).serialize(data, writeCtx.getValue());
+            // Augmentations do not have a representation, so we are faking a ContainerNode as the parent and we will be
+            // extracting the resulting children.
+            if (augment != null) {
+                domWriter.startContainerNode(FAKE_NODEID, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+            }
+
+            // We get serializer which reads binding data and uses Binding To Normalized Node writer to write result
+            getSerializer(path.getTargetType()).serialize(data, bindingWriter);
+
+            if (augment != null) {
+                domWriter.endNode();
+            }
         } catch (final IOException e) {
             LOG.error("Unexpected failure while serializing path {} data {}", path, data, e);
             throw new IllegalStateException("Failed to create normalized node", e);
         }
-        return Map.entry(writeCtx.getKey(), result.getResult());
+
+        // Terminate the fake container and extract it to the result
+        if (augment != null) {
+            return new AugmentationResult(yangPath, augment.childPathArguments(),
+                ImmutableList.copyOf(((ContainerNode) result.getResult()).body()));
+        }
+        return new NodeResult(yangPath, result.getResult());
     }
 
     @Override
index 30fd3282ddb06c8263af55d49be030c7b1823b79..f5bdc9e7956892aa19a461bba13b93006250fe42 100644 (file)
@@ -25,7 +25,6 @@ import org.opendaylight.yangtools.yang.binding.Identifiable;
 import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
@@ -96,16 +95,16 @@ final class BindingToNormalizedStreamWriter implements AnydataBindingStreamWrite
     @Override
     public void endNode() throws IOException {
         NodeCodecContext left = schema.pop();
-        // NormalizedNode writer does not have entry into case, but into choice
-        // so for leaving case, we do not emit endNode.
-        if (!(left instanceof CaseNodeCodecContext)) {
+        // Due to writer does not start a new node on startCase() and on startAugmentationNode()
+        // node ending should not be triggered when associated endNode() is invoked.
+        if (!(left instanceof CaseNodeCodecContext) && !(left instanceof AugmentationNodeContext)) {
             delegate.endNode();
         }
     }
 
     private Map.Entry<NodeIdentifier, Object> serializeLeaf(final String localName, final Object value) {
         final var current = current();
-        if (!(current instanceof DataObjectCodecContext<?, ?> currentCasted)) {
+        if (!(current instanceof AbstractDataObjectCodecContext<?, ?> currentCasted)) {
             throw new IllegalArgumentException("Unexpected current context " + current);
         }
 
@@ -153,9 +152,8 @@ final class BindingToNormalizedStreamWriter implements AnydataBindingStreamWrite
     }
 
     @Override
-    public void startAugmentationNode(final Class<? extends Augmentation<?>> augmentationType)
-            throws IOException {
-        delegate.startAugmentationNode(enter(augmentationType, AugmentationIdentifier.class));
+    public void startAugmentationNode(final Class<? extends Augmentation<?>> augmentationType) throws IOException {
+        enter(augmentationType, NodeIdentifier.class);
     }
 
     @Override
index 6f2e3a60ba8bebf8a685c2b70e40bdb3ede6f535..893938dfb2090aba3f78be66e8246f3176711c3f 100644 (file)
@@ -10,19 +10,21 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableSet;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCodec;
 import org.opendaylight.yangtools.yang.binding.BindingObject;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
-class CachingNormalizedNodeCodec<D extends DataObject> extends AbstractBindingNormalizedNodeCacheHolder implements
-        BindingNormalizedNodeCachingCodec<D> {
-    private final DataContainerCodecContext<D, ?> context;
+class CachingNormalizedNodeCodec<D extends DataObject,
+        C extends DataContainerCodecContext<D, ?> & BindingNormalizedNodeCodec<D>>
+        extends AbstractBindingNormalizedNodeCacheHolder implements BindingNormalizedNodeCachingCodec<D> {
+    private final @NonNull C context;
 
-    CachingNormalizedNodeCodec(final DataContainerCodecContext<D, ?> subtreeRoot,
-            final ImmutableSet<Class<? extends BindingObject>> cacheSpec) {
+    CachingNormalizedNodeCodec(final C context, final ImmutableSet<Class<? extends BindingObject>> cacheSpec) {
         super(cacheSpec);
-        this.context = requireNonNull(subtreeRoot);
+        this.context = requireNonNull(context);
     }
 
     @Override
index 46992c260f55ce03bb2e9d0f0217f9362915c449..39dd3f7c15fc295b9a9a511cf7bf58956e457f58 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
@@ -32,10 +33,13 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType;
+import org.opendaylight.yangtools.yang.binding.BindingObject;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
@@ -44,9 +48,6 @@ 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.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
-import org.opendaylight.yangtools.yang.data.util.NormalizedNodeSchemaUtils;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
 import org.slf4j.Logger;
@@ -99,7 +100,8 @@ import org.slf4j.LoggerFactory;
  * ambiguous reference and issue warn once when they are encountered -- tracking warning information in
  * {@link #ambiguousByCaseChildWarnings}.
  */
-final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCodecContext<D, ChoiceRuntimeType> {
+final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCodecContext<D, ChoiceRuntimeType>
+        implements BindingDataObjectCodecTreeNode<D> {
     private static final Logger LOG = LoggerFactory.getLogger(ChoiceNodeCodecContext.class);
 
     private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChild;
@@ -134,16 +136,6 @@ final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCo
             // Updates collection of YANG instance identifier to case
             for (var stmt : cazeDef.getType().statement().effectiveSubstatements()) {
                 if (stmt instanceof DataSchemaNode cazeChild) {
-                    if (cazeChild.isAugmenting()) {
-                        final AugmentationSchemaNode augment = NormalizedNodeSchemaUtils.findCorrespondingAugment(
-                            // FIXME: bad cast
-                            (DataSchemaNode) cazeDef.getType().statement(), cazeChild);
-                        if (augment != null) {
-                            byYangCaseChildBuilder.put(DataSchemaContextNode.augmentationIdentifierFrom(augment),
-                                cazeDef);
-                            continue;
-                        }
-                    }
                     byYangCaseChildBuilder.put(NodeIdentifier.create(cazeChild.getQName()), cazeDef);
                 }
             }
@@ -271,7 +263,12 @@ final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCo
             return null;
         }
         final DataContainerCodecPrototype<?> caze = byYangCaseChild.get(first.getIdentifier());
-        return (D) caze.get().deserialize(data);
+        return (D) caze.getDataObject().deserialize(data);
+    }
+
+    @Override
+    public NormalizedNode serialize(final D data) {
+        return serializeImpl(data);
     }
 
     @Override
@@ -291,6 +288,12 @@ final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCo
         return getDomPathArgument();
     }
 
+    @Override
+    public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
+            final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
+        return createCachingCodec(this, cacheSpecifier);
+    }
+
     DataContainerCodecContext<?, ?> getCaseByChildClass(final @NonNull Class<? extends DataObject> type) {
         DataContainerCodecPrototype<?> result = byCaseChildClass.get(type);
         if (result == null) {
index 1d7b2168cc841b76cf9d4365521169ccb5d92ec4..a7d01e51ea92b83fd8930477faabdc8ba8e16100 100644 (file)
@@ -41,7 +41,7 @@ public abstract class CodecDataObject<T extends DataObject> implements DataObjec
         }
     }
 
-    private final @NonNull DataObjectCodecContext<T, ?> context;
+    private final @NonNull AbstractDataObjectCodecContext<T, ?> context;
     @SuppressWarnings("rawtypes")
     private final @NonNull DistinctNodeContainer data;
 
@@ -50,7 +50,8 @@ public abstract class CodecDataObject<T extends DataObject> implements DataObjec
     // FIXME: consider using a primitive int-based cache (with 0 being uninit)
     private volatile Integer cachedHashcode;
 
-    protected CodecDataObject(final DataObjectCodecContext<T, ?> context, final DistinctNodeContainer<?, ?> data) {
+    protected CodecDataObject(final AbstractDataObjectCodecContext<T, ?> context,
+            final DistinctNodeContainer<?, ?> data) {
         this.data = requireNonNull(data, "Data must not be null");
         this.context = requireNonNull(context, "Context must not be null");
     }
@@ -105,7 +106,7 @@ public abstract class CodecDataObject<T extends DataObject> implements DataObjec
 
     protected abstract boolean codecEquals(Object obj);
 
-    final @NonNull DataObjectCodecContext<T, ?> codecContext() {
+    final @NonNull AbstractDataObjectCodecContext<T, ?> codecContext() {
         return context;
     }
 
index 71dd5a3ca3ca10b6b9337c8ab24a0cf5ef4eafc9..455f2b42232e6080ee1c8f114b220856dd06b002 100644 (file)
@@ -42,9 +42,9 @@ import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
  */
 final class CodecDataObjectAnalysis<R extends CompositeRuntimeType> {
     private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class,
-        DataObjectCodecContext.class, DistinctNodeContainer.class);
+        AbstractDataObjectCodecContext.class, DistinctNodeContainer.class);
     private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class,
-        DataObjectCodecContext.class, DistinctNodeContainer.class);
+        AbstractDataObjectCodecContext.class, DistinctNodeContainer.class);
 
     final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
     final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
index 27a565652faa37a92cf39f137777c89a9fc98f21..addd780649fbd43875839395cbfaab00edb9ab0a 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableCollection;
@@ -27,9 +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.BindingDataObjectCodecTreeNode;
 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;
@@ -48,7 +48,6 @@ 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.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
@@ -57,7 +56,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 abstract class DataContainerCodecContext<D extends DataObject, T extends RuntimeTypeContainer> extends NodeCodecContext
-        implements BindingDataObjectCodecTreeNode<D>  {
+        implements CommonDataObjectCodecTreeNode<D> {
     private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecContext.class);
     private static final VarHandle EVENT_STREAM_SERIALIZER;
 
@@ -70,7 +69,7 @@ abstract class DataContainerCodecContext<D extends DataObject, T extends Runtime
         }
     }
 
-    private final @NonNull DataContainerCodecPrototype<T> prototype;
+    final @NonNull DataContainerCodecPrototype<T> prototype;
 
     // Accessed via a VarHandle
     @SuppressWarnings("unused")
@@ -98,7 +97,7 @@ abstract class DataContainerCodecContext<D extends DataObject, T extends Runtime
     }
 
     @Override
-    protected final YangInstanceIdentifier.PathArgument getDomPathArgument() {
+    protected YangInstanceIdentifier.PathArgument getDomPathArgument() {
         return prototype.getYangArg();
     }
 
@@ -138,7 +137,10 @@ abstract class DataContainerCodecContext<D extends DataObject, T extends Runtime
      */
     void addYangPathArgument(final PathArgument arg, final List<YangInstanceIdentifier.PathArgument> builder) {
         if (builder != null) {
-            builder.add(getDomPathArgument());
+            final var yangArg = getDomPathArgument();
+            if (yangArg != null) {
+                builder.add(yangArg);
+            }
         }
     }
 
@@ -178,19 +180,17 @@ abstract class DataContainerCodecContext<D extends DataObject, T extends Runtime
         return getClass().getSimpleName() + " [" + prototype.getBindingClass() + "]";
     }
 
-    @Override
-    public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
-            final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
-        if (cacheSpecifier.isEmpty()) {
-            return new NonCachingCodec<>(this);
-        }
-        return new CachingNormalizedNodeCodec<>(this, ImmutableSet.copyOf(cacheSpecifier));
+    static final <T extends DataObject, C extends DataContainerCodecContext<T, ?> & BindingNormalizedNodeCodec<T>>
+            @NonNull BindingNormalizedNodeCachingCodec<T> createCachingCodec(final C context,
+                final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
+        return cacheSpecifier.isEmpty() ? new NonCachingCodec<>(context)
+            : new CachingNormalizedNodeCodec<>(context, ImmutableSet.copyOf(cacheSpecifier));
     }
 
     protected final <V> @NonNull V childNonNull(final @Nullable V nullable,
             final YangInstanceIdentifier.PathArgument child, final String message, final Object... args) {
         if (nullable == null) {
-            throw childNullException(extractName(child), message, args);
+            throw childNullException(child.getNodeType(), message, args);
         }
         return nullable;
     }
@@ -249,15 +249,6 @@ abstract class DataContainerCodecContext<D extends DataObject, T extends Runtime
         return new IncorrectNestingException(message, args);
     }
 
-    private static QName extractName(final YangInstanceIdentifier.PathArgument child) {
-        if (child instanceof AugmentationIdentifier) {
-            final Set<QName> children = ((AugmentationIdentifier) child).getPossibleChildNames();
-            checkArgument(!children.isEmpty(), "Augmentation without childs must not be used in data");
-            return children.iterator().next();
-        }
-        return child.getNodeType();
-    }
-
     final DataObjectSerializer eventStreamSerializer() {
         final DataObjectSerializer existing = (DataObjectSerializer) EVENT_STREAM_SERIALIZER.getAcquire(this);
         return existing != null ? existing : loadEventStreamSerializer();
@@ -270,8 +261,7 @@ abstract class DataContainerCodecContext<D extends DataObject, T extends Runtime
         return witness == null ? loaded : (DataObjectSerializer) witness;
     }
 
-    @Override
-    public NormalizedNode serialize(final D data) {
+    final @NonNull NormalizedNode serializeImpl(final @NonNull D data) {
         final NormalizedNodeResult result = new NormalizedNodeResult();
         // We create DOM stream writer which produces normalized nodes
         final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
index b90045df2bc8c8389ce3229b441e2091e1c771f7..82022d40f06d711281848b5d8f20f10adda39621 100644 (file)
@@ -10,10 +10,11 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.collect.ImmutableSet;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode.ChildAddressabilitySummary;
+import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode.ChildAddressabilitySummary;
 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
@@ -32,7 +33,6 @@ import org.opendaylight.yangtools.yang.binding.Identifiable;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
@@ -50,7 +50,77 @@ import org.opendaylight.yangtools.yang.model.api.stmt.PresenceEffectiveStatement
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-final class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements NodeContextSupplier {
+abstract sealed class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements NodeContextSupplier {
+    static final class Augmentation extends DataContainerCodecPrototype<AugmentRuntimeType> {
+        private final @NonNull ImmutableSet<NodeIdentifier> childArgs;
+
+        @SuppressWarnings("unchecked")
+        Augmentation(final Class<?> cls, final QNameModule namespace, final AugmentRuntimeType type,
+                final CodecContextFactory factory, final ImmutableSet<NodeIdentifier> childArgs) {
+            super(Item.of((Class<? extends DataObject>) cls), namespace, type, factory);
+            this.childArgs = requireNonNull(childArgs);
+        }
+
+        @Override
+        PathArgument getYangArg() {
+            throw new UnsupportedOperationException("Augmentation does not have PathArgument address");
+        }
+
+        @Override
+        AugmentationNodeContext<?> createInstance() {
+            return new AugmentationNodeContext<>(this);
+        }
+
+        // Guaranteed to be non-empty
+        @NonNull ImmutableSet<NodeIdentifier> getChildArgs() {
+            return childArgs;
+        }
+    }
+
+    static final class Regular<T extends RuntimeTypeContainer> extends DataContainerCodecPrototype<T> {
+        private final @NonNull PathArgument yangArg;
+
+        @SuppressWarnings("unchecked")
+        private Regular(final Class<?> cls, final PathArgument yangArg, final T type,
+                final CodecContextFactory factory) {
+            this(Item.of((Class<? extends DataObject>) cls), yangArg, type, factory);
+        }
+
+        private Regular(final Item<?> bindingArg, final PathArgument yangArg, final T type,
+                final CodecContextFactory factory) {
+            super(bindingArg, yangArg.getNodeType().getModule(), type, factory);
+            this.yangArg = requireNonNull(yangArg);
+        }
+
+        @Override
+        PathArgument getYangArg() {
+            return yangArg;
+        }
+
+        @Override
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        DataContainerCodecContext<?, T> createInstance() {
+            final var type = getType();
+            if (type instanceof ContainerLikeRuntimeType containerLike) {
+                if (containerLike instanceof ContainerRuntimeType container
+                    && container.statement().findFirstEffectiveSubstatement(PresenceEffectiveStatement.class)
+                        .isEmpty()) {
+                    return new NonPresenceContainerNodeCodecContext(this);
+                }
+                return new ContainerNodeCodecContext(this);
+            } else if (type instanceof ListRuntimeType) {
+                return Identifiable.class.isAssignableFrom(getBindingClass())
+                        ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListRuntimeType>) this)
+                                : new ListNodeCodecContext(this);
+            } else if (type instanceof ChoiceRuntimeType) {
+                return new ChoiceNodeCodecContext(this);
+            } else if (type instanceof CaseRuntimeType) {
+                return new CaseNodeCodecContext(this);
+            }
+            throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + type);
+        }
+    }
+
     private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
 
     private static final VarHandle INSTANCE;
@@ -68,36 +138,24 @@ final class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implemen
     private final @NonNull QNameModule namespace;
     private final @NonNull CodecContextFactory factory;
     private final @NonNull Item<?> bindingArg;
-    private final @NonNull PathArgument yangArg;
     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;
 
-    @SuppressWarnings("unchecked")
-    private DataContainerCodecPrototype(final Class<?> cls, final PathArgument yangArg, final T type,
-            final CodecContextFactory factory) {
-        this(Item.of((Class<? extends DataObject>) cls), yangArg, type, factory);
-    }
-
-    private DataContainerCodecPrototype(final Item<?> bindingArg, final PathArgument yangArg, final T type,
+    private DataContainerCodecPrototype(final Item<?> bindingArg, final QNameModule namespace, final T type,
             final CodecContextFactory factory) {
         this.bindingArg = requireNonNull(bindingArg);
-        this.yangArg = requireNonNull(yangArg);
+        this.namespace = requireNonNull(namespace);
         this.type = requireNonNull(type);
         this.factory = requireNonNull(factory);
 
-        if (yangArg instanceof AugmentationIdentifier augId) {
-            final var childNames = augId.getPossibleChildNames();
-            verify(!childNames.isEmpty(), "Unexpected empty identifier for %s", type);
-            namespace = childNames.iterator().next().getModule();
-        } else {
-            namespace = yangArg.getNodeType().getModule();
-        }
-
-        childAddressabilitySummary = type instanceof RuntimeType
-            ? computeChildAddressabilitySummary(((RuntimeType) type).statement())
+        childAddressabilitySummary = type instanceof RuntimeType runtimeType
+            ? computeChildAddressabilitySummary(runtimeType.statement())
                 // BindingRuntimeTypes, does not matter
                 : ChildAddressabilitySummary.MIXED;
     }
@@ -177,29 +235,23 @@ final class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implemen
     }
 
     static DataContainerCodecPrototype<BindingRuntimeTypes> rootPrototype(final CodecContextFactory factory) {
-        return new DataContainerCodecPrototype<>(DataRoot.class, NodeIdentifier.create(SchemaContext.NAME),
+        return new Regular<>(DataRoot.class, NodeIdentifier.create(SchemaContext.NAME),
             factory.getRuntimeContext().getTypes(), factory);
     }
 
     static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Class<?> cls, final T type,
             final CodecContextFactory factory) {
-        return new DataContainerCodecPrototype<>(cls, createIdentifier(type), type, factory);
+        return new Regular<>(cls, createIdentifier(type), type, factory);
     }
 
     static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T type,
             final CodecContextFactory factory) {
-        return new DataContainerCodecPrototype<>(bindingArg, createIdentifier(type), type, factory);
-    }
-
-    static DataContainerCodecPrototype<AugmentRuntimeType> from(final Class<?> augClass,
-            final AugmentationIdentifier arg, final AugmentRuntimeType schema, final CodecContextFactory factory) {
-        return new DataContainerCodecPrototype<>(augClass, arg, schema, factory);
+        return new Regular<>(bindingArg, createIdentifier(type), type, factory);
     }
 
     static DataContainerCodecPrototype<NotificationRuntimeType> from(final Class<?> augClass,
             final NotificationRuntimeType schema, final CodecContextFactory factory) {
-        return new DataContainerCodecPrototype<>(augClass, NodeIdentifier.create(schema.statement().argument()), schema,
-            factory);
+        return new Regular<>(augClass, NodeIdentifier.create(schema.statement().argument()), schema, factory);
     }
 
     private static @NonNull NodeIdentifier createIdentifier(final CompositeRuntimeType type) {
@@ -208,40 +260,45 @@ final class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implemen
         return NodeIdentifier.create((QName) arg);
     }
 
-    @NonNull T getType() {
+    final @NonNull T getType() {
         return type;
     }
 
-    @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
+    final @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
         return childAddressabilitySummary;
     }
 
-    @NonNull QNameModule getNamespace() {
+    final @NonNull QNameModule getNamespace() {
         return namespace;
     }
 
-    @NonNull CodecContextFactory getFactory() {
+    final @NonNull CodecContextFactory getFactory() {
         return factory;
     }
 
-    @NonNull Class<?> getBindingClass() {
+    final @NonNull Class<?> getBindingClass() {
         return bindingArg.getType();
     }
 
-    @NonNull Item<?> getBindingArg() {
+    final @NonNull Item<?> getBindingArg() {
         return bindingArg;
     }
 
-    @NonNull PathArgument getYangArg() {
-        return yangArg;
-    }
+    abstract @NonNull PathArgument getYangArg();
 
     @Override
-    public DataContainerCodecContext<?, T> get() {
-        final DataContainerCodecContext<?, T> existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
+    public final DataContainerCodecContext<?, T> get() {
+        final var existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
         return existing != null ? existing : loadInstance();
     }
 
+    @SuppressWarnings("unchecked")
+    final <R extends CompositeRuntimeType> DataObjectCodecContext<?, R> getDataObject() {
+        final var context = get();
+        verify(context instanceof DataObjectCodecContext, "Unexpected instance %s", context);
+        return (DataObjectCodecContext<?, R>) context;
+    }
+
     private @NonNull DataContainerCodecContext<?, T> loadInstance() {
         final var tmp = createInstance();
         final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
@@ -250,25 +307,5 @@ final class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implemen
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
     // This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
-    private @NonNull DataContainerCodecContext<?, T> createInstance() {
-        // FIXME: make protected abstract
-        if (type instanceof ContainerLikeRuntimeType containerLike) {
-            if (containerLike instanceof ContainerRuntimeType container
-                && container.statement().findFirstEffectiveSubstatement(PresenceEffectiveStatement.class).isEmpty()) {
-                return new NonPresenceContainerNodeCodecContext(this);
-            }
-            return new ContainerNodeCodecContext(this);
-        } else if (type instanceof ListRuntimeType) {
-            return Identifiable.class.isAssignableFrom(getBindingClass())
-                    ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListRuntimeType>) this)
-                            : new ListNodeCodecContext(this);
-        } else if (type instanceof ChoiceRuntimeType) {
-            return new ChoiceNodeCodecContext(this);
-        } else if (type instanceof AugmentRuntimeType) {
-            return new AugmentationNodeContext(this);
-        } else if (type instanceof CaseRuntimeType) {
-            return new CaseNodeCodecContext(this);
-        }
-        throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + type);
-    }
+    abstract @NonNull DataContainerCodecContext<?, T> createInstance();
 }
index 9c941089fd7a434d35072f7005a708d5ff8842fc..44404ad34649364dd08be9260f6bcb498ab9ed6e 100644 (file)
@@ -10,40 +10,35 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static com.google.common.base.Preconditions.checkArgument;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
 import java.lang.reflect.Method;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
-import org.opendaylight.mdsal.binding.runtime.api.AugmentableRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BindingObject;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -53,7 +48,7 @@ import org.slf4j.LoggerFactory;
  */
 @Beta
 public abstract class DataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
-        extends DataContainerCodecContext<D, T> {
+        extends AbstractDataObjectCodecContext<D, T> implements BindingDataObjectCodecTreeNode<D> {
     private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class);
 
     private static final VarHandle MISMATCHED_AUGMENTED;
@@ -67,14 +62,9 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
         }
     }
 
-    private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
-    private final ImmutableMap<YangInstanceIdentifier.PathArgument, NodeContextSupplier> byYang;
-    private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
-    private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
-    private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> augmentationByYang;
-    private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> augmentationByStream;
+    private final ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation> augmentToPrototype;
+    private final ImmutableMap<PathArgument, Class<?>> yangToAugmentClass;
     private final @NonNull Class<? extends CodecDataObject<?>> generatedClass;
-    private final MethodHandle proxyConstructor;
 
     // 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.
@@ -95,151 +85,83 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
 
     private DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
             final CodecDataObjectAnalysis<T> analysis) {
-        super(prototype);
+        super(prototype, analysis);
 
         // Inherit analysis stuff
-        leafChild = analysis.leafNodes;
-        proxyConstructor = analysis.proxyConstructor;
         generatedClass = analysis.generatedClass;
-        byBindingArgClass = analysis.byBindingArgClass;
-        byStreamClass = analysis.byStreamClass;
-        byYang = analysis.byYang;
 
         // Deal with augmentations, which are not something we analysis provides
-        final var augByYang = new HashMap<PathArgument, DataContainerCodecPrototype<?>>();
-        final var augByStream = new HashMap<Class<?>, DataContainerCodecPrototype<?>>();
+        final var augPathToBinding = new HashMap<PathArgument, Class<?>>();
+        final var augClassToProto = new HashMap<Class<?>, DataContainerCodecPrototype.Augmentation>();
         for (var augment : analysis.possibleAugmentations) {
             final var augProto = loadAugmentPrototype(augment);
             if (augProto != null) {
-                final var augYangArg = augProto.getYangArg();
-                if (augByYang.putIfAbsent(augYangArg, augProto) == null) {
-                    LOG.trace("Discovered new YANG mapping {} -> {} in {}", augYangArg, augProto, this);
-                }
                 final var augBindingClass = augProto.getBindingClass();
-                if (augByStream.putIfAbsent(augBindingClass, augProto) == null) {
-                    LOG.trace("Discovered new class mapping {} -> {} in {}", augBindingClass, augProto, this);
+                for (var childPath : augProto.getChildArgs()) {
+                    augPathToBinding.putIfAbsent(childPath, augBindingClass);
                 }
+                augClassToProto.putIfAbsent(augBindingClass, augProto);
             }
         }
-        augmentationByYang = ImmutableMap.copyOf(augByYang);
-        augmentationByStream = ImmutableMap.copyOf(augByStream);
-    }
-
-    @Override
-    public final WithStatus getSchema() {
-        // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
-        return (WithStatus) getType().statement();
+        yangToAugmentClass = ImmutableMap.copyOf(augPathToBinding);
+        augmentToPrototype = ImmutableMap.copyOf(augClassToProto);
     }
 
     @Override
-    @SuppressWarnings("unchecked")
-    public <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
-        return (DataContainerCodecContext<C, ?>) childNonNull(streamChildPrototype(childClass), childClass,
-            "Child %s is not valid child of %s", getBindingClass(), childClass).get();
+    final DataContainerCodecPrototype<?> pathChildPrototype(final Class<? extends DataObject> argType) {
+        final var child = super.pathChildPrototype(argType);
+        return child != null ? child : augmentToPrototype.get(argType);
     }
 
-    private DataContainerCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
-        final DataContainerCodecPrototype<?> childProto = byStreamClass.get(childClass);
-        if (childProto != null) {
-            return childProto;
-        }
-        if (Augmentation.class.isAssignableFrom(childClass)) {
-            return augmentationByClass(childClass);
-        }
-        return null;
-    }
-
-    @SuppressWarnings("unchecked")
     @Override
-    public <C extends DataObject> Optional<DataContainerCodecContext<C, ?>> possibleStreamChild(
-            final Class<C> childClass) {
-        final DataContainerCodecPrototype<?> childProto = streamChildPrototype(childClass);
-        if (childProto != null) {
-            return Optional.of((DataContainerCodecContext<C, ?>) childProto.get());
+    final DataContainerCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
+        final var child = super.streamChildPrototype(childClass);
+        if (child == null && Augmentation.class.isAssignableFrom(childClass)) {
+            return getAugmentationProtoByClass(childClass);
         }
-        return Optional.empty();
+        return child;
     }
 
     @Override
-    public DataContainerCodecContext<?,?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
-            final List<YangInstanceIdentifier.PathArgument> builder) {
-
-        final Class<? extends DataObject> argType = arg.getType();
-        DataContainerCodecPrototype<?> ctxProto = byBindingArgClass.get(argType);
-        if (ctxProto == null && Augmentation.class.isAssignableFrom(argType)) {
-            ctxProto = augmentationByClass(argType);
-        }
-        final DataContainerCodecContext<?, ?> context = childNonNull(ctxProto, argType,
-            "Class %s is not valid child of %s", argType, getBindingClass()).get();
-        if (context instanceof ChoiceNodeCodecContext<?> choice) {
-            choice.addYangPathArgument(arg, builder);
-
-            final Optional<? extends Class<? extends DataObject>> caseType = arg.getCaseType();
-            final Class<? extends DataObject> type = arg.getType();
-            final DataContainerCodecContext<?, ?> caze;
-            if (caseType.isPresent()) {
-                // Non-ambiguous addressing this should not pose any problems
-                caze = choice.streamChild(caseType.orElseThrow());
-            } else {
-                caze = choice.getCaseByChildClass(type);
+    final NodeContextSupplier yangChildSupplier(final PathArgument arg) {
+        final var child = super.yangChildSupplier(arg);
+        if (child == null) {
+            final var augClass = yangToAugmentClass.get(arg);
+            if (augClass != null) {
+                return augmentToPrototype.get(augClass);
             }
-
-            caze.addYangPathArgument(arg, builder);
-            return caze.bindingPathArgumentChild(arg, builder);
-        }
-        context.addYangPathArgument(arg, builder);
-        return context;
-    }
-
-    @Override
-    public NodeCodecContext yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
-        final NodeContextSupplier childSupplier;
-        if (arg instanceof NodeIdentifierWithPredicates) {
-            childSupplier = byYang.get(new NodeIdentifier(arg.getNodeType()));
-        } else if (arg instanceof AugmentationIdentifier) {
-            childSupplier = augmentationByYang.get(arg);
-        } else {
-            childSupplier = byYang.get(arg);
-        }
-
-        return childNonNull(childSupplier, arg, "Argument %s is not valid child of %s", arg, getSchema()).get();
-    }
-
-    protected final ValueNodeCodecContext getLeafChild(final String name) {
-        final ValueNodeCodecContext value = leafChild.get(name);
-        if (value == null) {
-            throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass());
         }
-        return value;
+        return child;
     }
 
-    private @Nullable DataContainerCodecPrototype<?> augmentationByClass(final @NonNull Class<?> childClass) {
-        final DataContainerCodecPrototype<?> childProto = augmentationByStream.get(childClass);
-        return childProto != null ? childProto : mismatchedAugmentationByClass(childClass);
+    private DataContainerCodecPrototype.@Nullable Augmentation getAugmentationProtoByClass(
+            final @NonNull Class<?> augmClass) {
+        final var childProto = augmentToPrototype.get(augmClass);
+        return childProto != null ? childProto : mismatchedAugmentationByClass(augmClass);
     }
 
-    private @Nullable DataContainerCodecPrototype<?> mismatchedAugmentationByClass(final @NonNull Class<?> childClass) {
+    private DataContainerCodecPrototype.@Nullable Augmentation mismatchedAugmentationByClass(
+            final @NonNull Class<?> childClass) {
         /*
          * It is potentially mismatched valid augmentation - we look up equivalent augmentation using reflection
          * and walk all stream child and compare augmentations classes if they are equivalent. When we find a match
          * we'll cache it so we do not need to perform reflection operations again.
          */
-        final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> local =
-                (ImmutableMap<Class<?>, DataContainerCodecPrototype<?>>) MISMATCHED_AUGMENTED.getAcquire(this);
-        final DataContainerCodecPrototype<?> mismatched = local.get(childClass);
+        final var local =
+            (ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation>) MISMATCHED_AUGMENTED.getAcquire(this);
+        final var mismatched = local.get(childClass);
         return mismatched != null ? mismatched : loadMismatchedAugmentation(local, childClass);
-
     }
 
-    private @Nullable DataContainerCodecPrototype<?> loadMismatchedAugmentation(
-            final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> oldMismatched,
+    private DataContainerCodecPrototype.@Nullable Augmentation loadMismatchedAugmentation(
+            final ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation> oldMismatched,
             final @NonNull Class<?> childClass) {
         @SuppressWarnings("rawtypes")
         final Class<?> augTarget = BindingReflections.findAugmentationTarget((Class) childClass);
         // Do not bother with proposals which are not augmentations of our class, or do not match what the runtime
         // context would load.
         if (getBindingClass().equals(augTarget) && belongsToRuntimeContext(childClass)) {
-            for (final DataContainerCodecPrototype<?> realChild : augmentationByStream.values()) {
+            for (var realChild : augmentToPrototype.values()) {
                 if (Augmentation.class.isAssignableFrom(realChild.getBindingClass())
                         && isSubstitutionFor(childClass, realChild.getBindingClass())) {
                     return cacheMismatched(oldMismatched, childClass, realChild);
@@ -250,19 +172,19 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
         return null;
     }
 
-    private @NonNull DataContainerCodecPrototype<?> cacheMismatched(
-            final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> oldMismatched,
-            final @NonNull Class<?> childClass, final @NonNull DataContainerCodecPrototype<?> prototype) {
+    private DataContainerCodecPrototype.@NonNull Augmentation cacheMismatched(
+            final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation> oldMismatched,
+            final @NonNull Class<?> childClass, final DataContainerCodecPrototype.@NonNull Augmentation prototype) {
 
-        ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> expected = oldMismatched;
+        var expected = oldMismatched;
         while (true) {
-            final Map<Class<?>, DataContainerCodecPrototype<?>> newMismatched =
-                    ImmutableMap.<Class<?>, DataContainerCodecPrototype<?>>builderWithExpectedSize(expected.size() + 1)
-                        .putAll(expected)
-                        .put(childClass, prototype)
-                        .build();
+            final var newMismatched =
+                ImmutableMap.<Class<?>, DataContainerCodecPrototype<?>>builderWithExpectedSize(expected.size() + 1)
+                    .putAll(expected)
+                    .put(childClass, prototype)
+                    .build();
 
-            final var witness = (ImmutableMap<Class<?>, DataContainerCodecPrototype<?>>)
+            final var witness = (ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation>)
                 MISMATCHED_AUGMENTED.compareAndExchangeRelease(this, expected, newMismatched);
             if (witness == expected) {
                 LOG.trace("Cached mismatched augmentation {} -> {} in {}", childClass, prototype, this);
@@ -270,7 +192,7 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
             }
 
             expected = witness;
-            final DataContainerCodecPrototype<?> existing = expected.get(childClass);
+            final var existing = expected.get(childClass);
             if (existing != null) {
                 LOG.trace("Using raced mismatched augmentation {} -> {} in {}", childClass, existing, this);
                 return existing;
@@ -290,16 +212,19 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
         return cls.equals(loaded);
     }
 
-    private @Nullable DataContainerCodecPrototype<?> loadAugmentPrototype(final AugmentRuntimeType augment) {
+    private DataContainerCodecPrototype.@Nullable Augmentation loadAugmentPrototype(final AugmentRuntimeType augment) {
         // FIXME: in face of deviations this code should be looking at declared view, i.e. all possibilities at augment
         //        declaration site
-        final var possibleChildren = augment.statement()
+        final var childPaths = augment.statement()
             .streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class)
-            .map(stmt -> (QName) stmt.argument())
+            .map(stmt -> new NodeIdentifier((QName) stmt.argument()))
             .collect(ImmutableSet.toImmutableSet());
-        if (possibleChildren.isEmpty()) {
+
+        final var it = childPaths.iterator();
+        if (!it.hasNext()) {
             return null;
         }
+        final var namespace = it.next().getNodeType().getModule();
 
         final var factory = factory();
         final GeneratedType javaType = augment.javaType();
@@ -310,48 +235,34 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
             throw new IllegalStateException(
                 "RuntimeContext references type " + javaType + " but failed to load its class", e);
         }
-
-        return DataContainerCodecPrototype.from(augClass, new AugmentationIdentifier(possibleChildren), augment,
-            factory);
-    }
-
-    @SuppressWarnings("checkstyle:illegalCatch")
-    protected final @NonNull D createBindingProxy(final DistinctNodeContainer<?, ?> node) {
-        try {
-            return (D) proxyConstructor.invokeExact(this, node);
-        } catch (final Throwable e) {
-            Throwables.throwIfUnchecked(e);
-            throw new IllegalStateException(e);
-        }
+        return new DataContainerCodecPrototype.Augmentation(augClass, namespace, augment, factory, childPaths);
     }
 
+    @Override
     @SuppressWarnings("unchecked")
     Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
             final DistinctNodeContainer<PathArgument, NormalizedNode> data) {
-
-        @SuppressWarnings("rawtypes")
-        final Map map = new HashMap<>();
-
-        for (final NormalizedNode childValue : data.body()) {
-            if (childValue instanceof AugmentationNode augDomNode) {
-                final DataContainerCodecPrototype<?> codecProto = augmentationByYang.get(augDomNode.getIdentifier());
-                if (codecProto != null) {
-                    final DataContainerCodecContext<?, ?> codec = codecProto.get();
-                    map.put(codec.getBindingClass(), codec.deserializeObject(augDomNode));
-                }
+        /**
+         * Due to augmentation fields are at same level as direct children the data of each augmentation needs to be
+         * aggregated into own container node, then only deserialized using associated prototype.
+         */
+        final var builders = new HashMap<Class<?>, DataContainerNodeBuilder>();
+        for (var childValue : data.body()) {
+            final var bindingClass = yangToAugmentClass.get(childValue.getIdentifier());
+            if (bindingClass != null) {
+                builders.computeIfAbsent(bindingClass,
+                    key -> Builders.containerBuilder()
+                        .withNodeIdentifier(new NodeIdentifier(data.getIdentifier().getNodeType())))
+                        .addChild(childValue);
             }
         }
-        for (final DataContainerCodecPrototype<?> value : augmentationByStream.values()) {
-            final var augClass = value.getBindingClass();
-            // Do not perform duplicate deserialization if we have already created the corresponding augmentation
-            // and validate whether the proposed augmentation is valid ion this instantiation context.
-            if (!map.containsKey(augClass)
-                && ((AugmentableRuntimeType) getType()).augments().contains(value.getType())) {
-                final NormalizedNode augData = data.childByArg(value.getYangArg());
-                if (augData != null) {
-                    // ... make sure we do not replace an e
-                    map.putIfAbsent(augClass, value.get().deserializeObject(augData));
-                }
+        @SuppressWarnings("rawtypes")
+        final var map = new HashMap();
+        for (final var entry : builders.entrySet()) {
+            final var bindingClass = entry.getKey();
+            final var codecProto = augmentToPrototype.get(bindingClass);
+            if (codecProto != null) {
+                map.put(bindingClass, codecProto.get().deserializeObject(entry.getValue().build()));
             }
         }
         return map;
@@ -362,15 +273,25 @@ public abstract class DataObjectCodecContext<D extends DataObject, T extends Com
     }
 
     @Override
-    public InstanceIdentifier.PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
+    public InstanceIdentifier.PathArgument deserializePathArgument(final PathArgument arg) {
         checkArgument(getDomPathArgument().equals(arg));
         return bindingArg();
     }
 
     @Override
-    public YangInstanceIdentifier.PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
+    public PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
         checkArgument(bindingArg().equals(arg));
         return getDomPathArgument();
     }
 
+    @Override
+    public NormalizedNode serialize(final D data) {
+        return serializeImpl(data);
+    }
+
+    @Override
+    public final BindingNormalizedNodeCachingCodec<D> createCachingCodec(
+            final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
+        return createCachingCodec(this, cacheSpecifier);
+    }
 }
index a964068d529f71b78ba382c0a878bc950a482db0..64615d607f9b4c0ac89661ee21847ab7698830ff 100644 (file)
@@ -130,7 +130,7 @@ final class NotificationCodecContext<D extends DataObject & BaseNotification>
         static {
             try {
                 LOAD_CTOR_ARGS = MethodVariableAccess.allArgumentsOf(new MethodDescription.ForLoadedConstructor(
-                    AugmentableCodecDataObject.class.getDeclaredConstructor(DataObjectCodecContext.class,
+                    AugmentableCodecDataObject.class.getDeclaredConstructor(AbstractDataObjectCodecContext.class,
                         DistinctNodeContainer.class)));
             } catch (NoSuchMethodException e) {
                 throw new ExceptionInInitializerError(e);
index b93ec3f2ad581ed79c01f92d65cb004949b6942b..220f78a24d6141491344cbbe7d9b7f437417a3cc 100644 (file)
@@ -16,6 +16,7 @@ import com.google.common.base.Throwables;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableCollection;
 import com.google.common.util.concurrent.UncheckedExecutionException;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -24,6 +25,8 @@ import java.util.Optional;
 import java.util.function.BiFunction;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
 import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.runtime.api.ActionRuntimeType;
@@ -38,6 +41,7 @@ import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.Action;
+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;
@@ -60,7 +64,8 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 
-final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCodecContext<D, BindingRuntimeTypes> {
+final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCodecContext<D, BindingRuntimeTypes>
+        implements BindingDataObjectCodecTreeNode<D> {
 
     private final LoadingCache<Class<? extends DataObject>, DataContainerCodecContext<?, ?>> childrenByClass =
         CacheBuilder.newBuilder().build(new CacheLoader<>() {
@@ -231,6 +236,11 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         throw new UnsupportedOperationException("Could not create Binding data representation for root");
     }
 
+    @Override
+    public NormalizedNode serialize(final D data) {
+        return serializeImpl(data);
+    }
+
     ActionCodecContext getAction(final Class<? extends Action<?, ?, ?>> action) {
         return getOrRethrow(actionsByClass, action);
     }
@@ -284,9 +294,9 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         final ActionRuntimeType schema = factory().getRuntimeContext().getActionDefinition(action);
         return new ActionCodecContext(
             DataContainerCodecPrototype.from(asClass(args[inputOffset], RpcInput.class), schema.input(),
-                factory()).get(),
+                factory()).getDataObject(),
             DataContainerCodecPrototype.from(asClass(args[outputOffset], RpcOutput.class), schema.output(),
-                factory()).get());
+                factory()).getDataObject());
     }
 
     private static <T extends DataObject> Class<? extends T> asClass(final Type type, final Class<T> target) {
@@ -361,6 +371,12 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         return super.bindingPathArgumentChild(arg, builder);
     }
 
+    @Override
+    public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
+            final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
+        return createCachingCodec(this, cacheSpecifier);
+    }
+
     private static Class<?> findCaseChoice(final Class<? extends DataObject> caseClass) {
         for (var type : caseClass.getGenericInterfaces()) {
             if (type instanceof Class<?> typeClass && ChoiceIn.class.isAssignableFrom(typeClass)) {
index 9a711376078828da5052b643a9893c8c40f57c8c..78bc227483fb7a00c5c2f5160cc08d06e736c19a 100644 (file)
@@ -9,14 +9,12 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import static org.junit.Assert.assertEquals;
 
-import java.util.Map.Entry;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public abstract class AbstractBindingCodecTest extends AbstractBindingRuntimeTest {
     protected BindingCodecContext codecContext;
@@ -33,14 +31,13 @@ public abstract class AbstractBindingCodecTest extends AbstractBindingRuntimeTes
 
     @Before
     public void before() {
-        this.codecContext = new BindingCodecContext(getRuntimeContext());
+        codecContext = new BindingCodecContext(getRuntimeContext());
     }
 
     @SuppressWarnings("unchecked")
     protected <T extends DataObject> T thereAndBackAgain(final InstanceIdentifier<T> path, final T data) {
-        final Entry<YangInstanceIdentifier, NormalizedNode> there = codecContext.toNormalizedNode(path, data);
-        final Entry<InstanceIdentifier<?>, DataObject> backAgain = codecContext.fromNormalizedNode(there.getKey(),
-            there.getValue());
+        final var there = (NodeResult) codecContext.toNormalizedNode(path, data);
+        final var backAgain = codecContext.fromNormalizedNode(there.path(), there.node());
         assertEquals(path, backAgain.getKey());
         return (T) backAgain.getValue();
     }
index c5be2078b6caeb22f0284cc5ccb6e21376967fc9..982d1deaa4a70bf35e9430be93c1f552e6aee6ec 100644 (file)
@@ -18,6 +18,7 @@ import static org.junit.Assert.assertSame;
 import java.util.Map.Entry;
 import javax.xml.transform.dom.DOMSource;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.mdsal438.norev.Cont;
 import org.opendaylight.yang.gen.v1.mdsal438.norev.ContBuilder;
 import org.opendaylight.yang.gen.v1.mdsal438.norev.cont.ContAny;
@@ -96,10 +97,10 @@ public class AnydataLeafTest extends AbstractBindingCodecTest {
 
     @Test
     public void testAnydataFromBinding() {
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
+        final var entry = (NodeResult) codecContext.toNormalizedNode(
             InstanceIdentifier.create(Cont.class), new ContBuilder().setContAny(new FakeCont()).build());
-        assertEquals(YangInstanceIdentifier.create(CONT_NODE_ID), entry.getKey());
-        assertEquals(cont, entry.getValue());
+        assertEquals(YangInstanceIdentifier.create(CONT_NODE_ID), entry.path());
+        assertEquals(cont, entry.node());
     }
 
     private final class FakeData extends AbstractOpaqueData<DOMSource> {
index c78e7e47875a5b62878e03c28018bc2b3cac92b6..584c4fa11fbdfd79f13dcb824916e3ed7dc90577 100644 (file)
@@ -18,6 +18,7 @@ import static org.junit.Assert.assertSame;
 import java.util.Map.Entry;
 import javax.xml.transform.dom.DOMSource;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.mdsal437.norev.Cont;
 import org.opendaylight.yang.gen.v1.mdsal437.norev.ContBuilder;
 import org.opendaylight.yang.gen.v1.mdsal437.norev.cont.ContAny;
@@ -96,10 +97,10 @@ public class AnyxmlLeafTest extends AbstractBindingCodecTest {
 
     @Test
     public void testAnyxmlFromBinding() {
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
+        final var entry = (NodeResult) codecContext.toNormalizedNode(
             InstanceIdentifier.create(Cont.class), new ContBuilder().setContAny(new FakeCont()).build());
-        assertEquals(YangInstanceIdentifier.create(CONT_NODE_ID), entry.getKey());
-        assertEquals(cont, entry.getValue());
+        assertEquals(YangInstanceIdentifier.create(CONT_NODE_ID), entry.path());
+        assertEquals(cont, entry.node());
     }
 
     private final class FakeData extends AbstractOpaqueData<DOMSource> {
index 73495710def572c0bd63be32f8c638728632a691..314fc5a463888f26c4baa0020ea623fd1fd90a6d 100644 (file)
@@ -9,8 +9,8 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import static org.junit.Assert.assertEquals;
 
-import java.util.Map.Entry;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.RpcComplexUsesAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.RpcComplexUsesAugmentBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
@@ -20,8 +20,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.te
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public class AugmentationSubstitutionTest extends AbstractBindingCodecTest {
     private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
@@ -38,8 +36,8 @@ public class AugmentationSubstitutionTest extends AbstractBindingCodecTest {
             .withKey(TOP_FOO_KEY)
             .addAugmentation(new TreeComplexUsesAugmentBuilder(createComplexData()).build())
             .build();
-        final NormalizedNode domTreeEntry = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, baTree).getValue();
-        final NormalizedNode domRpcEntry = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, baRpc).getValue();
+        final var domTreeEntry = ((NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, baTree)).node();
+        final var domRpcEntry = ((NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, baRpc)).node();
         assertEquals(domTreeEntry, domRpcEntry);
     }
 
@@ -50,10 +48,9 @@ public class AugmentationSubstitutionTest extends AbstractBindingCodecTest {
             .addAugmentation(new TreeComplexUsesAugmentBuilder(createComplexData()).build())
             .build();
 
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
-            BA_TOP_LEVEL_LIST, manuallyConstructed);
-        final TopLevelList deserialized = (TopLevelList) codecContext.fromNormalizedNode(entry.getKey(),
-            entry.getValue()).getValue();
+        final var result = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, manuallyConstructed);
+        final TopLevelList deserialized =
+            (TopLevelList) codecContext.fromNormalizedNode(result.path(), result.node()).getValue();
         assertEquals(manuallyConstructed, deserialized);
         final TopLevelList copiedFromDeserialized = new TopLevelListBuilder(deserialized).build();
         assertEquals(manuallyConstructed, copiedFromDeserialized);
index 29876e170039f3563a9f96c30554ac887a82ca70..250320fb4f2dd401f28f8f89429214bdb72088aa 100644 (file)
@@ -10,13 +10,11 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import java.util.Map.Entry;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.odl.test.binary.key.rev160101.BinaryList;
 import org.opendaylight.yang.gen.v1.odl.test.binary.key.rev160101.BinaryListBuilder;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public class BinaryKeyTest extends AbstractBindingCodecTest {
     private final InstanceIdentifier<BinaryList> instanceIdentifier = InstanceIdentifier.create(BinaryList.class);
@@ -46,8 +44,7 @@ public class BinaryKeyTest extends AbstractBindingCodecTest {
     }
 
     private BinaryList process(final BinaryList binaryList) {
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
-            instanceIdentifier, binaryList);
-        return (BinaryList) codecContext.fromNormalizedNode(entry.getKey(), entry.getValue()).getValue();
+        final var entry = (NodeResult) codecContext.toNormalizedNode(instanceIdentifier, binaryList);
+        return (BinaryList) codecContext.fromNormalizedNode(entry.path(), entry.node()).getValue();
     }
 }
index 242894b3f6db012d157bb495456744179d46f74a..37792e83e67f38a3ab4307bb2cbbe97cdae1bd14 100644 (file)
@@ -69,7 +69,7 @@ public class Bug5524augmentUses extends AbstractBindingCodecTest {
                 .build())
             .build();
 
-        final BindingDataObjectCodecTreeNode<Module4Main> subtreeCodec = codecContext.getSubtreeCodec(
+        final var subtreeCodec = (BindingDataObjectCodecTreeNode<Module4Main>) codecContext.getSubtreeCodec(
                 InstanceIdentifier.create(Module4Main.class));
         final NormalizedNode serialized = subtreeCodec.serialize(module4Main);
         final NormalizedNode manualSerialized = subtreeCodec.serialize(manualModule4Main);
index 99d5ba6cbed9c1962c1fb911b171a0c7cee2685e..6fe7a53f27e27fc52953f49ed2b44de3fae0fd31 100644 (file)
@@ -41,7 +41,7 @@ public class Bug5845booleanKeyTest extends AbstractBindingCodecTest {
                         .build()))
                 .build();
 
-        final BindingDataObjectCodecTreeNode<BooleanContainer> subtreeCodec = codecContext.getSubtreeCodec(
+        final var subtreeCodec = (BindingDataObjectCodecTreeNode<BooleanContainer>) codecContext.getSubtreeCodec(
                 InstanceIdentifier.create(BooleanContainer.class));
         final NormalizedNode serializedInt = subtreeCodec.serialize(booleanContainerInt);
         assertNotNull(serializedInt);
index 3cf587f867f0d40ab278d3db67f4fc6b54630a97..adaa456dc0debeffd0acf0d8e3f3c0fbec849334 100644 (file)
@@ -72,8 +72,8 @@ public class CachingCodecTest extends AbstractBindingCodecTest {
     @Before
     public void before() {
         super.before();
-        topNode = codecContext.getSubtreeCodec(TOP_PATH);
-        contNode = codecContext.getSubtreeCodec(CONT_PATH);
+        topNode = (BindingDataObjectCodecTreeNode<Top>) codecContext.getSubtreeCodec(TOP_PATH);
+        contNode = (BindingDataObjectCodecTreeNode<Cont>) codecContext.getSubtreeCodec(CONT_PATH);
     }
 
     private static Map<TopLevelListKey, TopLevelList> createList(final int num) {
index 5af771e14f9bd58402bdebe32173a340fdb09ab4..b8b4f84b22fd615a5f9e029272245e0c80c80161 100644 (file)
@@ -11,6 +11,7 @@ import static org.junit.Assert.assertEquals;
 
 import java.util.Collections;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.RpcComplexUsesAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.RpcComplexUsesAugmentBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
@@ -26,7 +27,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.te
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public class CaseSubstitutionTest extends AbstractBindingCodecTest {
 
@@ -53,8 +53,8 @@ public class CaseSubstitutionTest extends AbstractBindingCodecTest {
             .withKey(CHOICE_FOO_KEY)
             .setChoiceInChoiceList(new ComplexViaUsesBuilder(createComplexData()).build())
             .build();
-        final NormalizedNode domTreeEntry = codecContext.toNormalizedNode(BA_CHOICE_LIST, baTree).getValue();
-        final NormalizedNode domRpcEntry = codecContext.toNormalizedNode(BA_CHOICE_LIST, baRpc).getValue();
+        final var domTreeEntry = ((NodeResult) codecContext.toNormalizedNode(BA_CHOICE_LIST, baTree)).node();
+        final var domRpcEntry = ((NodeResult) codecContext.toNormalizedNode(BA_CHOICE_LIST, baRpc)).node();
         assertEquals(domTreeEntry, domRpcEntry);
     }
 
index 3a7594a616663ddc24839b4a0dd6f647f4a4747a..467d7244c151d85183ff451aea0af00c11300072 100644 (file)
@@ -10,13 +10,11 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Collections;
-import java.util.Map.Entry;
+import java.util.Map;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.RpcComplexUsesAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.RpcComplexUsesAugmentBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.top.top.level.list.choice.in.list.EmptyLeaf;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.top.top.level.list.choice.in.list.EmptyLeafBuilder;
@@ -25,23 +23,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.te
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.top.level.list.ChoiceInList;
-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.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public class EmptyLeafTest extends AbstractBindingCodecTest {
 
     private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
     private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class)
             .child(TopLevelList.class, TOP_FOO_KEY).build();
-    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
-            .augmentation(TreeLeafOnlyAugment.class);
-    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
-            .augmentation(TreeComplexUsesAugment.class);
-    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
 
     @Test
     public void testCaseWithEmptyLeafType() {
@@ -49,10 +38,9 @@ public class EmptyLeafTest extends AbstractBindingCodecTest {
             .withKey(TOP_FOO_KEY)
             .setChoiceInList(new EmptyLeafBuilder().setEmptyType(Empty.value()).build())
             .build();
-        final Entry<YangInstanceIdentifier, NormalizedNode> dom = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
+        final var dom = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
             withEmptyCase);
-        final Entry<InstanceIdentifier<?>, DataObject> readed = codecContext.fromNormalizedNode(dom.getKey(),
-            dom.getValue());
+        final var readed = codecContext.fromNormalizedNode(dom.path(), dom.node());
         final ChoiceInList list = ((TopLevelList) readed.getValue()).getChoiceInList();
         assertTrue(list instanceof EmptyLeaf);
         assertNotNull(((EmptyLeaf) list).getEmptyType());
@@ -61,8 +49,8 @@ public class EmptyLeafTest extends AbstractBindingCodecTest {
     // FIXME: either remove this method or take advantage of it
     private static RpcComplexUsesAugment createComplexData() {
         return new RpcComplexUsesAugmentBuilder()
-                .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping("foo").build())
-                .setListViaUses(Collections.emptyMap())
-                .build();
+            .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping("foo").build())
+            .setListViaUses(Map.of())
+            .build();
     }
 }
index 941b86c6c7b8069444cc57a51f509fb3147fe209..2d63758e26bf8a8c46ba215481326fd3eb77f5d4 100644 (file)
@@ -22,8 +22,6 @@ import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.Lst;
 import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.lst.Foo;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.notification.rev150205.OutOfPixieDustNotification;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.md.sal.knock.knock.rev180723.KnockKnockInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
@@ -39,7 +37,6 @@ import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
@@ -50,15 +47,10 @@ public class InstanceIdentifierSerializeDeserializeTest extends AbstractBindingC
     private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
     private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
             .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).build();
-    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY =
-            BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class);
-    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES =
-            BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class);
 
     public static final QName TOP_QNAME = Top.QNAME;
     public static final QName TOP_LEVEL_LIST_QNAME = QName.create(TOP_QNAME, "top-level-list");
     public static final QName TOP_LEVEL_LIST_KEY = QName.create(TOP_QNAME, "name");
-    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
 
     public static final YangInstanceIdentifier BI_TOP_PATH = YangInstanceIdentifier.of(TOP_QNAME);
     public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_PATH = BI_TOP_PATH.node(TOP_LEVEL_LIST_QNAME);
@@ -120,20 +112,6 @@ public class InstanceIdentifierSerializeDeserializeTest extends AbstractBindingC
         assertEquals(TopLevelList.QNAME, lastPathArgument.getNodeType());
     }
 
-    @Test
-    public void testBindingAwareIIToYangIIAugmentation() {
-        final PathArgument lastArg = codecContext.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES).getLastPathArgument();
-        assertTrue(lastArg instanceof AugmentationIdentifier);
-    }
-
-    @Test
-    public void testBindingAwareIIToYangIILeafOnlyAugmentation() {
-        final PathArgument leafOnlyLastArg = codecContext.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY)
-                .getLastPathArgument();
-        assertTrue(leafOnlyLastArg instanceof AugmentationIdentifier);
-        assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
-    }
-
     @Test
     public void testChoiceCaseGroupingFromBinding() {
         final YangInstanceIdentifier contBase = codecContext.toYangInstanceIdentifier(
index b7ebc32d7396f7b0b2129d8b3553fe959be1683f..4ce746aec5bf78fea2e473b1fbc799978ea66807 100644 (file)
@@ -16,6 +16,8 @@ import org.opendaylight.yang.gen.v1.mdsal._355.norev.OspfStatLsdbBrief;
 import org.opendaylight.yang.gen.v1.mdsal._355.norev.OspfStatLsdbBriefKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
@@ -25,10 +27,8 @@ import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Uint8;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 
 public class InstanceIdentifierTest extends AbstractBindingCodecTest {
     private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
@@ -36,25 +36,24 @@ public class InstanceIdentifierTest extends AbstractBindingCodecTest {
             .child(TopLevelList.class, TOP_FOO_KEY).build();
     private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
             .augmentation(TreeLeafOnlyAugment.class);
-    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
-            .augmentation(TreeComplexUsesAugment.class);
-    private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+    private static final InstanceIdentifier<ListViaUses> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
+            .augmentation(TreeComplexUsesAugment.class).child(ListViaUses.class, new ListViaUsesKey("bar"));
 
     @Test
     public void testComplexAugmentationSerialization() {
-        final YangInstanceIdentifier yangII = codecContext.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES);
-        final PathArgument lastArg = yangII.getLastPathArgument();
-        assertTrue("Last argument should be AugmentationIdentifier", lastArg instanceof AugmentationIdentifier);
-        final InstanceIdentifier<?> bindingII = codecContext.fromYangInstanceIdentifier(yangII);
-        assertEquals(BA_TREE_COMPLEX_USES, bindingII);
+        // augmentation child pointer fully recoverable after reverse transformation
+        final YangInstanceIdentifier yii = codecContext.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES);
+        final InstanceIdentifier<?> converted = codecContext.fromYangInstanceIdentifier(yii);
+        assertEquals(BA_TREE_COMPLEX_USES, converted);
     }
 
     @Test
     public void testLeafOnlyAugmentationSerialization() {
-        final PathArgument leafOnlyLastArg = codecContext.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY)
-                .getLastPathArgument();
-        assertTrue("Last argument should be AugmentationIdentifier", leafOnlyLastArg instanceof AugmentationIdentifier);
-        assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
+        // augmentation only pointer translated to parent node being augmented,
+        // because of augmentation only have no corresponding yang identifier
+        final YangInstanceIdentifier yii = codecContext.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY);
+        final InstanceIdentifier<?> converted = codecContext.fromYangInstanceIdentifier(yii);
+        assertEquals(BA_TOP_LEVEL_LIST, converted);
     }
 
     @Test
index 18bf6c92e661beacefdc9c9e8e66b8bb5decc3ba..ec9bfe4cc1d48125bd788fd5d8d0d73f56d399a3 100644 (file)
@@ -12,6 +12,7 @@ import static org.junit.Assert.assertEquals;
 import com.google.common.collect.ImmutableMap;
 import java.util.Map.Entry;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.Def;
 import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.DefBuilder;
 import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.grp.LstBuilder;
@@ -20,8 +21,6 @@ import org.opendaylight.yang.gen.v1.mdsal442.keyuse.norev.Use;
 import org.opendaylight.yang.gen.v1.mdsal442.keyuse.norev.UseBuilder;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public class KeyInheritenceTest extends AbstractBindingCodecTest {
     private static final LstKey KEY = new LstKey("foo");
@@ -37,25 +36,24 @@ public class KeyInheritenceTest extends AbstractBindingCodecTest {
 
     @Test
     public void testFromBinding() {
-        final Entry<YangInstanceIdentifier, NormalizedNode> domDef = codecContext.toNormalizedNode(DEF_IID, DEF);
-        Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(domDef.getKey(),
-            domDef.getValue());
+        final var domDef = (NodeResult) codecContext.toNormalizedNode(DEF_IID, DEF);
+        Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(domDef.path(), domDef.node());
         assertEquals(DEF_IID, entry.getKey());
         final Def codecDef = (Def) entry.getValue();
 
-        final Entry<YangInstanceIdentifier, NormalizedNode> domUse = codecContext.toNormalizedNode(USE_IID, USE);
-        entry = codecContext.fromNormalizedNode(domUse.getKey(), domUse.getValue());
+        final var domUse = (NodeResult) codecContext.toNormalizedNode(USE_IID, USE);
+        entry = codecContext.fromNormalizedNode(domUse.path(), domUse.node());
         assertEquals(USE_IID, entry.getKey());
         final Use codecUse = (Use) entry.getValue();
 
         Use copiedUse = new UseBuilder(DEF).build();
         assertEquals(USE, copiedUse);
-        assertEquals(domUse.getValue(), codecContext.toNormalizedNode(USE_IID, copiedUse).getValue());
+        assertEquals(domUse.node(), ((NodeResult) codecContext.toNormalizedNode(USE_IID, copiedUse)).node());
         copiedUse = new UseBuilder(codecDef).build();
         assertEquals(USE, copiedUse);
-        assertEquals(domUse.getValue(), codecContext.toNormalizedNode(USE_IID, copiedUse).getValue());
+        assertEquals(domUse.node(), ((NodeResult) codecContext.toNormalizedNode(USE_IID, copiedUse)).node());
         copiedUse = new UseBuilder(codecUse).build();
         assertEquals(USE, copiedUse);
-        assertEquals(domUse.getValue(), codecContext.toNormalizedNode(USE_IID, copiedUse).getValue());
+        assertEquals(domUse.node(), ((NodeResult) codecContext.toNormalizedNode(USE_IID, copiedUse)).node());
     }
 }
index 25015fae3d5b4fc8e5ac5f4998e2f36236a1bfca..099eb0aa42cfb7d9da3d43491a867d7a5e9b9660 100644 (file)
@@ -9,29 +9,27 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import static org.junit.Assert.assertEquals;
 
-import java.util.Map.Entry;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.ThirdParty;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexLeaves;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexLeavesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Int32StringUnion;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
-import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public class LeafReferenceTest extends AbstractBindingCodecTest {
 
     private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
-    private static final InstanceIdentifier<TreeComplexLeaves> BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class)
-            .child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeComplexLeaves.class).build();
+    private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class)
+            .child(TopLevelList.class, TOP_FOO_KEY).build();
 
     @Test
     public void testCaseWithLeafReferencesType() {
-        final TreeComplexLeaves binding = new TreeComplexLeavesBuilder()
+        final TreeComplexLeaves augment = new TreeComplexLeavesBuilder()
             .setIdentity(ThirdParty.VALUE)
             .setIdentityRef(ThirdParty.VALUE)
             .setSimpleType(10)
@@ -39,12 +37,15 @@ public class LeafReferenceTest extends AbstractBindingCodecTest {
             .setSchemaUnawareUnion(new Int32StringUnion("foo"))
             .setSchemaUnawareUnionRef(new Int32StringUnion(10))
             .build();
-        final Entry<YangInstanceIdentifier, NormalizedNode> dom = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
-            binding);
-        final Entry<InstanceIdentifier<?>, DataObject> readed = codecContext.fromNormalizedNode(dom.getKey(),
-            dom.getValue());
-        final TreeComplexLeaves readedAugment = (TreeComplexLeaves) readed.getValue();
+        final TopLevelList list = new TopLevelListBuilder()
+            .setName("foo")
+            .addAugmentation(augment)
+            .build();
+        final var dom = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
+            list);
+        final var readed = codecContext.fromNormalizedNode(dom.path(), dom.node());
+        final var readAugment = ((TopLevelList) readed.getValue()).augmentation(TreeComplexLeaves.class);
 
-        assertEquals(binding,readedAugment);
+        assertEquals(augment, readAugment);
     }
 }
index d9207dff629858fe3d1e6bb5487a371270d6161a..7fecd0b8baf0fc8b7d7c6e8941f7e4c719f3b23d 100644 (file)
@@ -10,37 +10,33 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import java.util.Map.Entry;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.bug8449.rev170516.Cont;
 import org.opendaylight.yang.gen.v1.bug8449.rev170516.Cont.Ref;
 import org.opendaylight.yang.gen.v1.bug8449.rev170516.ContBuilder;
 import org.opendaylight.yang.gen.v1.bug8449.rev170516.ContInt32;
 import org.opendaylight.yang.gen.v1.bug8449.rev170516.ContInt32.RefUnionInt32;
 import org.opendaylight.yang.gen.v1.bug8449.rev170516.ContInt32Builder;
-import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.Uint32;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public class LeafrefSerializeDeserializeTest extends AbstractBindingCodecTest {
 
     @Test
     public void listReferenceTest() {
         final YangInstanceIdentifier contYII = YangInstanceIdentifier.builder().node(Cont.QNAME).build();
-        final InstanceIdentifier<?> fromYangInstanceIdentifier = this.codecContext.fromYangInstanceIdentifier(contYII);
+        final InstanceIdentifier<?> fromYangInstanceIdentifier = codecContext.fromYangInstanceIdentifier(contYII);
         assertNotNull(fromYangInstanceIdentifier);
 
         final InstanceIdentifier<Cont> BA_II_CONT = InstanceIdentifier.builder(Cont.class).build();
         final Ref refVal = new Ref("myvalue");
         final Cont data = new ContBuilder().setRef(refVal).build();
-        final Entry<YangInstanceIdentifier, NormalizedNode> normalizedNode =
-                this.codecContext.toNormalizedNode(BA_II_CONT, data);
+        final var normalizedNode = (NodeResult) codecContext.toNormalizedNode(BA_II_CONT, data);
         assertNotNull(normalizedNode);
 
-        final Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode =
-                this.codecContext.fromNormalizedNode(contYII, normalizedNode.getValue());
+        final var fromNormalizedNode = codecContext.fromNormalizedNode(contYII, normalizedNode.node());
         assertNotNull(fromNormalizedNode);
         final Cont value = (Cont) fromNormalizedNode.getValue();
         assertEquals(refVal, value.getRef());
@@ -49,18 +45,16 @@ public class LeafrefSerializeDeserializeTest extends AbstractBindingCodecTest {
     @Test
     public void uint32LeafrefTest() {
         final YangInstanceIdentifier contYII = YangInstanceIdentifier.builder().node(ContInt32.QNAME).build();
-        final InstanceIdentifier<?> fromYangInstanceIdentifier = this.codecContext.fromYangInstanceIdentifier(contYII);
+        final InstanceIdentifier<?> fromYangInstanceIdentifier = codecContext.fromYangInstanceIdentifier(contYII);
         assertNotNull(fromYangInstanceIdentifier);
 
         final InstanceIdentifier<ContInt32> BA_II_CONT = InstanceIdentifier.builder(ContInt32.class).build();
         final RefUnionInt32 refVal = new RefUnionInt32(Uint32.valueOf(5));
         final ContInt32 data = new ContInt32Builder().setRefUnionInt32(refVal).build();
-        final Entry<YangInstanceIdentifier, NormalizedNode> normalizedNode =
-                this.codecContext.toNormalizedNode(BA_II_CONT, data);
+        final var normalizedNode = (NodeResult) codecContext.toNormalizedNode(BA_II_CONT, data);
         assertNotNull(normalizedNode);
 
-        final Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode =
-                this.codecContext.fromNormalizedNode(contYII, normalizedNode.getValue());
+        final var fromNormalizedNode = codecContext.fromNormalizedNode(contYII, normalizedNode.node());
         assertNotNull(fromNormalizedNode);
         final ContInt32 value = (ContInt32) fromNormalizedNode.getValue();
         assertEquals(refVal, value.getRefUnionInt32());
index 5d8580297eddf6bc57bb8cbee2de9e76cc7d27f0..5a6d826a1aff34e0e114f0c51c02156f072d7997 100644 (file)
@@ -11,6 +11,7 @@ import static org.junit.Assert.assertEquals;
 
 import java.util.Set;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.mdsal668.norev.Foo;
 import org.opendaylight.yang.gen.v1.mdsal668.norev.FooBuilder;
 import org.opendaylight.yang.gen.v1.mdsal668.norev.bar.Bar;
@@ -39,8 +40,8 @@ public class Mdsal668Test extends AbstractBindingCodecTest {
                         .build())
                     .build())
                 .build())
-            .build(), codecContext.toNormalizedNode(FOO_IID,
-                new FooBuilder().setBar(new BarBuilder().setBar(Set.of(FOO_IID)).build()).build())
-            .getValue());
+            .build(),
+            ((NodeResult) codecContext.toNormalizedNode(FOO_IID,
+                new FooBuilder().setBar(new BarBuilder().setBar(Set.of(FOO_IID)).build()).build())).node());
     }
 }
index e79e3b7725554716eb1eac2335a3d8c16f04efc6..5e7c0325070954f0a948c38f987d2c399116640d 100644 (file)
@@ -13,7 +13,6 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThrows;
 import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.top;
 import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.topLevelList;
-import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.augmentationBuilder;
 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.choiceBuilder;
 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.leafSetBuilder;
@@ -31,10 +30,10 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TopChoiceAugment1Builder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TopChoiceAugment2Builder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugmentBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.top.choice.augment1.AugmentChoice1;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.top.choice.augment1.augment.choice1.Case1Builder;
@@ -61,14 +60,11 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 
 public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodecTest {
     public static final String TOP_LEVEL_LIST_FOO_KEY_VALUE = "foo";
@@ -85,16 +81,11 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
     public static final QName CHOICE_CONTAINER_QNAME = ChoiceContainer.QNAME;
     public static final QName CHOICE_IDENTIFIER_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "identifier");
     public static final QName CHOICE_IDENTIFIER_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "id");
-    public static final QName SIMPLE_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "simple-id");
     public static final QName EXTENDED_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "extended-id");
     private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
 
     private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
             .builder(Top.class).child(TopLevelList.class, TOP_LEVEL_LIST_FOO_KEY).build();
-    private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY =
-            BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class);
-    private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES =
-            BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class);
 
     public static final YangInstanceIdentifier BI_TOP_PATH = YangInstanceIdentifier.of(TOP_QNAME);
     public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_PATH = BI_TOP_PATH.node(TOP_LEVEL_LIST_QNAME);
@@ -106,17 +97,13 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
 
     @Test
     public void containerToNormalized() {
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
-            InstanceIdentifier.create(Top.class), top());
-        final ContainerNode topNormalized = getEmptyTop();
-        assertEquals(topNormalized, entry.getValue());
+        final var entry = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.create(Top.class), top());
+        assertEquals(getEmptyTop(), entry.node());
     }
 
     @Test
     public void containerFromNormalized() {
-        final ContainerNode topNormalized = getEmptyTop();
-        final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(BI_TOP_PATH,
-            topNormalized);
+        final var entry = codecContext.fromNormalizedNode(BI_TOP_PATH, getEmptyTop());
         assertEquals(top(), entry.getValue());
     }
 
@@ -131,10 +118,8 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
 
     @Test
     public void equalsWithAugment() {
-        final ContainerNode topNormalizedWithAugments = getNormalizedTopWithAugments(augmentationBuilder()
-            .withNodeIdentifier(new AugmentationIdentifier(Set.of(AGUMENT_STRING_Q)))
-            .withChild(leafNode(AGUMENT_STRING_Q, AUGMENT_STRING_VALUE))
-            .build());
+        final ContainerNode topNormalizedWithAugments =
+            getNormalizedTopWithChildren(leafNode(AGUMENT_STRING_Q, AUGMENT_STRING_VALUE));
         final ContainerNode topNormalized = getEmptyTop();
 
         final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(BI_TOP_PATH,
@@ -168,16 +153,9 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
 
     @Test
     public void equalsWithMultipleAugments() {
-        final ContainerNode topNormalizedWithAugments = getNormalizedTopWithAugments(
-            augmentationBuilder()
-                .withNodeIdentifier(new AugmentationIdentifier(Set.of(AGUMENT_STRING_Q)))
-                .withChild(leafNode(AGUMENT_STRING_Q, AUGMENT_STRING_VALUE))
-                .build(),
-            augmentationBuilder()
-                .withNodeIdentifier(new AugmentationIdentifier(Set.of(AUGMENT_INT_Q)))
-                .withChild(leafNode(AUGMENT_INT_Q, AUGMENT_INT_VALUE))
-                .build());
-
+        final ContainerNode topNormalizedWithAugments = getNormalizedTopWithChildren(
+            leafNode(AGUMENT_STRING_Q, AUGMENT_STRING_VALUE),
+            leafNode(AUGMENT_INT_Q, AUGMENT_INT_VALUE));
         final Entry<InstanceIdentifier<?>, DataObject> entryWithAugments = codecContext.fromNormalizedNode(BI_TOP_PATH,
             topNormalizedWithAugments);
         Top topWithAugments = topWithAugments(Map.of(
@@ -195,11 +173,10 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
         assertNotEquals(entryWithAugments.getValue(), topWithAugments);
     }
 
-    private static ContainerNode getNormalizedTopWithAugments(final AugmentationNode... augChild) {
+    private static ContainerNode getNormalizedTopWithChildren(final DataContainerChild... children) {
         final var builder = containerBuilder();
-
-        for (AugmentationNode augmentationNode : augChild) {
-            builder.withChild(augmentationNode);
+        for (DataContainerChild child : children) {
+            builder.withChild(child);
         }
         return builder.withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
                     .withChild(mapNodeBuilder(TOP_LEVEL_LIST_QNAME).build()).build();
@@ -216,58 +193,59 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
 
     @Test
     public void listWithKeysToNormalized() {
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
-            BA_TOP_LEVEL_LIST, topLevelList(TOP_LEVEL_LIST_FOO_KEY));
-        final MapEntryNode topLevelListNormalized = mapEntryBuilder()
+        final var entry = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
+            topLevelList(TOP_LEVEL_LIST_FOO_KEY));
+        assertEquals(mapEntryBuilder()
             .withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME,
                 TOP_LEVEL_LIST_FOO_KEY_VALUE))
             .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
-            .build();
-        assertEquals(topLevelListNormalized, entry.getValue());
+            .build(),
+            entry.node());
     }
 
     @Test
     public void listWithKeysFromNormalized() {
-        final MapEntryNode topLevelListNormalized = mapEntryBuilder()
+        final var entry = codecContext.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, mapEntryBuilder()
             .withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME,
                 TOP_LEVEL_LIST_FOO_KEY_VALUE))
             .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
-            .build();
-        final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(
-            BI_TOP_LEVEL_LIST_FOO_PATH, topLevelListNormalized);
+            .build());
         assertEquals(topLevelList(TOP_LEVEL_LIST_FOO_KEY), entry.getValue());
     }
 
     @Test
     public void leafOnlyAugmentationToNormalized() {
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
-            BA_TREE_LEAF_ONLY, new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build());
-        final AugmentationNode augmentationNode = augmentationBuilder()
-            .withNodeIdentifier(new AugmentationIdentifier(Set.of(SIMPLE_VALUE_QNAME)))
-            .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue"))
-            .build();
-        assertEquals(augmentationNode, entry.getValue());
+        final var entry = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
+            topLevelList(TOP_LEVEL_LIST_FOO_KEY,
+                new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build()));
+        assertEquals(mapEntryBuilder()
+            .withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME,
+                TOP_LEVEL_LIST_FOO_KEY_VALUE))
+            .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+            .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue")) // augmentation child
+            .build(),
+            entry.node());
     }
 
     @Test
     public void leafOnlyAugmentationFromNormalized() {
-        final AugmentationIdentifier augmentationId = new AugmentationIdentifier(Set.of(SIMPLE_VALUE_QNAME));
-        final AugmentationNode augmentationNode = augmentationBuilder()
-            .withNodeIdentifier(augmentationId)
-            .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue"))
-            .build();
-        final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(
-            BI_TOP_LEVEL_LIST_FOO_PATH.node(augmentationId), augmentationNode);
-        assertEquals(new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build(), entry.getValue());
+        final var entry = codecContext.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, mapEntryBuilder()
+            .withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME,
+                TOP_LEVEL_LIST_FOO_KEY_VALUE))
+            .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+            .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue")) // augmentation child
+            .build());
+        assertEquals(
+            topLevelList(TOP_LEVEL_LIST_FOO_KEY,
+                new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build()),
+            entry.getValue());
     }
 
     @Test
     public void orderedleafListToNormalized() {
-        Top top = new TopBuilder().setTopLevelOrderedLeafList(List.of("foo")).build();
-
-        Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
-            InstanceIdentifier.create(Top.class), top);
-        ContainerNode containerNode = containerBuilder()
+        final var entry = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.create(Top.class),
+            new TopBuilder().setTopLevelOrderedLeafList(List.of("foo")).build());
+        assertEquals(containerBuilder()
             .withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
             .withChild(orderedLeafSetBuilder()
                 .withNodeIdentifier(new NodeIdentifier(TOP_LEVEL_ORDERED_LEAF_LIST_QNAME))
@@ -276,17 +254,15 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
                     .withValue("foo")
                     .build())
                 .build())
-            .build();
-        assertEquals(containerNode, entry.getValue());
+            .build(),
+            entry.node());
     }
 
     @Test
     public void leafListToNormalized() {
-        final Top top = new TopBuilder().setTopLevelLeafList(Set.of("foo")).build();
-
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
-            InstanceIdentifier.create(Top.class), top);
-        final ContainerNode containerNode = containerBuilder()
+        final var entry = (NodeResult) codecContext.toNormalizedNode(
+            InstanceIdentifier.create(Top.class), new TopBuilder().setTopLevelLeafList(Set.of("foo")).build());
+        assertEquals(containerBuilder()
             .withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
             .withChild(leafSetBuilder()
                 .withNodeIdentifier(new NodeIdentifier(TOP_LEVEL_LEAF_LIST_QNAME))
@@ -295,13 +271,13 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
                     .withValue("foo")
                     .build())
                 .build())
-            .build();
-        assertEquals(containerNode, entry.getValue());
+            .build(),
+            entry.node());
     }
 
     @Test
     public void leafListFromNormalized() {
-        final ContainerNode topWithLeafList = containerBuilder()
+        final var entry = codecContext.fromNormalizedNode(BI_TOP_PATH, containerBuilder()
             .withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
             .withChild(leafSetBuilder()
                 .withNodeIdentifier(new NodeIdentifier(TOP_LEVEL_LEAF_LIST_QNAME))
@@ -310,16 +286,13 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
                     .withValue("foo")
                     .build())
                 .build())
-            .build();
-        final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(BI_TOP_PATH,
-            topWithLeafList);
-        final Top top = new TopBuilder().setTopLevelLeafList(Set.of("foo")).build();
-        assertEquals(top, entry.getValue());
+            .build());
+        assertEquals(new TopBuilder().setTopLevelLeafList(Set.of("foo")).build(), entry.getValue());
     }
 
     @Test
     public void orderedLeafListFromNormalized() {
-        ContainerNode topWithLeafList = containerBuilder()
+        final var entry = codecContext.fromNormalizedNode(BI_TOP_PATH, containerBuilder()
             .withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
             .withChild(orderedLeafSetBuilder()
                 .withNodeIdentifier(new NodeIdentifier(TOP_LEVEL_ORDERED_LEAF_LIST_QNAME))
@@ -327,19 +300,19 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
                     .withNodeIdentifier(new NodeWithValue<>(TOP_LEVEL_ORDERED_LEAF_LIST_QNAME, "foo"))
                     .withValue("foo").build())
                 .build())
-            .build();
-        Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(BI_TOP_PATH, topWithLeafList);
-        Top top = new TopBuilder().setTopLevelOrderedLeafList(List.of("foo")).build();
-        assertEquals(top, entry.getValue());
+            .build());
+        assertEquals(new TopBuilder().setTopLevelOrderedLeafList(List.of("foo")).build(), entry.getValue());
     }
 
     @Test
     public void choiceToNormalized() {
-        final ChoiceContainer choiceContainerBA = new ChoiceContainerBuilder().setIdentifier(new ExtendedBuilder()
-            .setExtendedId(new ExtendedIdBuilder().setId("identifier_value").build()).build()).build();
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
-            InstanceIdentifier.create(ChoiceContainer.class), choiceContainerBA);
-        final ContainerNode choiceContainer = containerBuilder()
+        final var entry = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.create(ChoiceContainer.class),
+            new ChoiceContainerBuilder()
+                .setIdentifier(new ExtendedBuilder()
+                    .setExtendedId(new ExtendedIdBuilder().setId("identifier_value").build())
+                    .build())
+                .build());
+        assertEquals(containerBuilder()
             .withNodeIdentifier(new NodeIdentifier(CHOICE_CONTAINER_QNAME))
             .withChild(choiceBuilder()
                 .withNodeIdentifier(new NodeIdentifier(CHOICE_IDENTIFIER_QNAME))
@@ -348,8 +321,8 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
                     .withChild(leafNode(CHOICE_IDENTIFIER_ID_QNAME, "identifier_value"))
                     .build())
                 .build())
-            .build();
-        assertEquals(choiceContainer, entry.getValue());
+            .build(),
+            entry.node());
     }
 
     @Test
@@ -450,27 +423,28 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
 
     @Test
     public void orderedLisToNormalized() {
-        final TopLevelList topLevelList = new TopLevelListBuilder()
+        final var entry = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, new TopLevelListBuilder()
             .withKey(TOP_LEVEL_LIST_FOO_KEY)
             .setNestedList(List.of(
                 new NestedListBuilder().withKey(new NestedListKey("foo")).build(),
                 new NestedListBuilder().withKey(new NestedListKey("bar")).build()))
-            .build();
-        final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
-            topLevelList);
-        final MapEntryNode foo = mapEntryBuilder().withNodeIdentifier(NodeIdentifierWithPredicates.of(
-                TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
-                .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
-                .withChild(orderedMapBuilder()
-                    .withNodeIdentifier(new NodeIdentifier(NESTED_LIST_QNAME))
-                    .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo"))
-                    .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar")).build()).build();
-        assertEquals(foo, entry.getValue());
+            .build());
+        assertEquals(mapEntryBuilder()
+            .withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME,
+                TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+            .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+            .withChild(orderedMapBuilder()
+                .withNodeIdentifier(new NodeIdentifier(NESTED_LIST_QNAME))
+                .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo"))
+                .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar"))
+                .build())
+            .build(),
+            entry.node());
     }
 
     @Test
     public void orderedLisFromNormalized() {
-        final MapEntryNode foo = mapEntryBuilder()
+        final var entry = codecContext.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, mapEntryBuilder()
             .withNodeIdentifier(NodeIdentifierWithPredicates.of(
                 TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
             .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
@@ -479,16 +453,14 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
                 .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo"))
                 .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar"))
                 .build())
-            .build();
-        final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(
-            BI_TOP_LEVEL_LIST_FOO_PATH, foo);
-        final TopLevelList topLevelList = new TopLevelListBuilder()
+            .build());
+        assertEquals(new TopLevelListBuilder()
             .withKey(TOP_LEVEL_LIST_FOO_KEY)
             .setNestedList(List.of(
                 new NestedListBuilder().withKey(new NestedListKey("foo")).build(),
                 new NestedListBuilder().withKey(new NestedListKey("bar")).build()))
-            .build();
-        assertEquals(topLevelList, entry.getValue());
+            .build(),
+            entry.getValue());
     }
 
     @Test
@@ -498,49 +470,41 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
         final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container");
         final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf");
 
-        final AugmentationIdentifier aug1Id = new AugmentationIdentifier(Set.of(augmentChoice1QName));
-        final AugmentationIdentifier aug2Id = new AugmentationIdentifier(Set.of(augmentChoice2QName));
         final NodeIdentifier augmentChoice1Id = new NodeIdentifier(augmentChoice1QName);
         final NodeIdentifier augmentChoice2Id = new NodeIdentifier(augmentChoice2QName);
         final NodeIdentifier containerId = new NodeIdentifier(containerQName);
 
-        final TopBuilder tBuilder = new TopBuilder();
-        final TopChoiceAugment1Builder tca1Builder = new TopChoiceAugment1Builder();
-        final Case1Builder c1Builder = new Case1Builder();
-        final TopChoiceAugment2Builder tca2Builder = new TopChoiceAugment2Builder();
-        final Case11Builder c11Builder = new Case11Builder();
-        final Case11ChoiceCaseContainerBuilder cccc1Builder = new Case11ChoiceCaseContainerBuilder();
-        cccc1Builder.setCase11ChoiceCaseLeaf("leaf-value");
-        c11Builder.setCase11ChoiceCaseContainer(cccc1Builder.build());
-        tca2Builder.setAugmentChoice2(c11Builder.build());
-        c1Builder.addAugmentation(tca2Builder.build());
-        tca1Builder.setAugmentChoice1(c1Builder.build());
-        tBuilder.addAugmentation(tca1Builder.build());
-        final Top top = tBuilder.build();
-
-        final Entry<YangInstanceIdentifier, NormalizedNode> biResult = codecContext.toNormalizedNode(
-            InstanceIdentifier.create(Top.class), top);
-
-        final NormalizedNode topNormalized = containerBuilder()
+        final Top top = new TopBuilder().addAugmentation(
+            // top is augmented with choice1 having case1
+            new TopChoiceAugment1Builder().setAugmentChoice1(
+                new Case1Builder().addAugmentation(
+                    // case1 is augmented with choice2 having case11 (with container having leaf)
+                    new TopChoiceAugment2Builder().setAugmentChoice2(
+                        new Case11Builder().setCase11ChoiceCaseContainer(
+                                new Case11ChoiceCaseContainerBuilder()
+                                        .setCase11ChoiceCaseLeaf("leaf-value").build()
+                        ).build()
+                    ).build()
+                ).build()
+            ).build()
+        ).build();
+
+        final var biResult = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.create(Top.class), top);
+
+        final var topNormalized = containerBuilder()
             .withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
-            .withChild(augmentationBuilder().withNodeIdentifier(aug1Id)
-                .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id)
-                    .withChild(augmentationBuilder().withNodeIdentifier(aug2Id)
-                        .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id)
-                            .withChild(containerBuilder().withNodeIdentifier(containerId)
-                                .withChild(leafNode(leafQName, "leaf-value"))
-                                .build())
-                            .build())
-                        .build())
+            .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id) // choice 1
+                .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id) // choice 2
+                    .withChild(containerBuilder().withNodeIdentifier(containerId)
+                        .withChild(leafNode(leafQName, "leaf-value")).build())
                     .build())
                 .build())
             .build();
 
-        assertEquals(BI_TOP_PATH, biResult.getKey());
-        assertEquals(topNormalized, biResult.getValue());
+        assertEquals(BI_TOP_PATH, biResult.path());
+        assertEquals(topNormalized, biResult.node());
 
-        final Entry<InstanceIdentifier<?>, DataObject> baResult = codecContext.fromNormalizedNode(BI_TOP_PATH,
-            topNormalized);
+        final var baResult = codecContext.fromNormalizedNode(BI_TOP_PATH, topNormalized);
 
         assertEquals(InstanceIdentifier.create(Top.class), baResult.getKey());
         assertEquals(top, baResult.getValue());
index f4d9c1e847af4f0ab00c147b24718fc950ee35ab..e09f5d9cf048697a8c5c09958c1a6307f93c9149 100644 (file)
@@ -10,16 +10,14 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static junit.framework.TestCase.assertTrue;
 import static org.junit.Assert.assertEquals;
 
-import java.util.Map;
 import java.util.Set;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.mdsal426.norev.BarCont;
 import org.opendaylight.yang.gen.v1.mdsal426.norev.BarContBuilder;
 import org.opendaylight.yang.gen.v1.mdsal426.norev.BooleanCont;
 import org.opendaylight.yang.gen.v1.mdsal426.norev.BooleanContBuilder;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 public class SpecializingLeafrefTest extends AbstractBindingCodecTest {
     private static final InstanceIdentifier<BooleanCont> BOOLEAN_CONT_II = InstanceIdentifier
@@ -32,11 +30,9 @@ public class SpecializingLeafrefTest extends AbstractBindingCodecTest {
     public void specifiedBooleanLeafTest() {
         final BooleanCont booleanCont  = new BooleanContBuilder().setIsFoo(true).build();
 
-        final Map.Entry<YangInstanceIdentifier, NormalizedNode> res = codecContext
-                .toNormalizedNode(BOOLEAN_CONT_II, booleanCont);
+        final var res = (NodeResult) codecContext.toNormalizedNode(BOOLEAN_CONT_II, booleanCont);
 
-        final BooleanCont booleanContBinding = (BooleanCont)codecContext
-                .fromNormalizedNode(res.getKey(), res.getValue()).getValue();
+        final var booleanContBinding = (BooleanCont) codecContext.fromNormalizedNode(res.path(), res.node()).getValue();
 
         assertTrue(booleanContBinding.getIsFoo());
     }
@@ -45,11 +41,9 @@ public class SpecializingLeafrefTest extends AbstractBindingCodecTest {
     public void specifiedCommonLeafTest() {
         final BarCont barCont  = new BarContBuilder().setLeaf2("foo").build();
 
-        final Map.Entry<YangInstanceIdentifier, NormalizedNode> res = codecContext
-                .toNormalizedNode(BAR_CONT_II, barCont);
+        final var res = (NodeResult) codecContext.toNormalizedNode(BAR_CONT_II, barCont);
 
-        final BarCont booleanContBinding = (BarCont)codecContext
-                .fromNormalizedNode(res.getKey(), res.getValue()).getValue();
+        final var booleanContBinding = (BarCont) codecContext.fromNormalizedNode(res.path(), res.node()).getValue();
 
         assertEquals(booleanContBinding.getLeaf2(), "foo");
     }
@@ -59,11 +53,9 @@ public class SpecializingLeafrefTest extends AbstractBindingCodecTest {
         final Set<String> testSet = Set.of("test");
         final BarCont barCont  = new BarContBuilder().setLeafList1(testSet).build();
 
-        final Map.Entry<YangInstanceIdentifier, NormalizedNode> res = codecContext
-                .toNormalizedNode(BAR_CONT_II, barCont);
+        final var res = (NodeResult) codecContext.toNormalizedNode(BAR_CONT_II, barCont);
 
-        final BarCont barContAfterConverting = (BarCont)codecContext
-                .fromNormalizedNode(res.getKey(), res.getValue()).getValue();
+        final var barContAfterConverting = (BarCont) codecContext.fromNormalizedNode(res.path(), res.node()).getValue();
 
         assertEquals(barContAfterConverting.getLeafList1(), testSet);
     }
index 28c89be391adff4953c924e1bf61779adfeca59e..89762a0721b207fc0417f66fc705ce39e03f9107 100644 (file)
@@ -9,23 +9,18 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import static org.junit.Assert.assertEquals;
 
-import java.util.Map.Entry;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.bug8903.rev170829.DefaultPolicy;
 import org.opendaylight.yang.gen.v1.bug8903.rev170829.DefaultPolicyBuilder;
 import org.opendaylight.yang.gen.v1.bug8903.rev170829.PolicyLoggingFlag;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.typedef.empty.rev170829.TestCont;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.typedef.empty.rev170829.TestContBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.typedef.empty.rev170829.TypedefEmpty;
-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;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-
 
 public class TypedefTest extends AbstractBindingCodecTest {
-
     private static final InstanceIdentifier<DefaultPolicy> BA_DEFAULT_POLICY =
             InstanceIdentifier.builder(DefaultPolicy.class).build();
     private static final InstanceIdentifier<TestCont> BA_TEST_CONT =
@@ -38,13 +33,10 @@ public class TypedefTest extends AbstractBindingCodecTest {
                 .setAction2(new PolicyLoggingFlag(false))
                 .setAction3(true)
                 .build();
-        final Entry<YangInstanceIdentifier, NormalizedNode> dom =
-                codecContext.toNormalizedNode(BA_DEFAULT_POLICY, binding);
-        final Entry<InstanceIdentifier<?>, DataObject> readed =
-                codecContext.fromNormalizedNode(dom.getKey(),dom.getValue());
-
-        assertEquals(binding,readed.getValue());
+        final var dom = (NodeResult) codecContext.toNormalizedNode(BA_DEFAULT_POLICY, binding);
+        final var readed = codecContext.fromNormalizedNode(dom.path(),dom.node());
 
+        assertEquals(binding, readed.getValue());
     }
 
     @Test
@@ -54,12 +46,9 @@ public class TypedefTest extends AbstractBindingCodecTest {
                 .setEmptyLeaf2(new TypedefEmpty(Empty.value()))
                 .setEmptyLeaf3(Empty.value())
                 .build();
-        final Entry<YangInstanceIdentifier, NormalizedNode> dom =
-                codecContext.toNormalizedNode(BA_TEST_CONT, binding);
-        final Entry<InstanceIdentifier<?>, DataObject> readed =
-                codecContext.fromNormalizedNode(dom.getKey(),dom.getValue());
-
-        assertEquals(binding,readed.getValue());
+        final var dom = (NodeResult) codecContext.toNormalizedNode(BA_TEST_CONT, binding);
+        final var readed = codecContext.fromNormalizedNode(dom.path(),dom.node());
 
+        assertEquals(binding, readed.getValue());
     }
 }
index e2eef29f93785b770ad75115b87e9121d431b8ad..5be7477d55cd80d9d2c4e2e28c933aeb11f2c0c0 100644 (file)
@@ -12,15 +12,14 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Map;
 import java.util.Optional;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.yang.union.test.rev220428.IdentOne;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.yang.union.test.rev220428.IdentTwo;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.yang.union.test.rev220428.Top;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.yang.union.test.rev220428.TopBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.yang.union.test.rev220428.UnionType;
-import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -70,8 +69,7 @@ public class UnionTypeWithMultipleIdentityrefsTest extends AbstractBindingCodecT
             .withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
             .withChild(ImmutableNodes.leafNode(NodeIdentifier.create(UNION_LEAF_QNAME), identityQname))
             .build();
-        final Map.Entry<InstanceIdentifier<?>, DataObject> translated =
-            codecContext.fromNormalizedNode(YangInstanceIdentifier.create(NodeIdentifier.create(TOP_QNAME)), top);
+        final var translated = codecContext.fromNormalizedNode(YangInstanceIdentifier.of(TOP_QNAME), top);
         assertNotNull(translated);
         assertNotNull(translated.getValue());
         assertTrue(translated.getValue() instanceof Top);
@@ -82,11 +80,11 @@ public class UnionTypeWithMultipleIdentityrefsTest extends AbstractBindingCodecT
         // create binding instance with identity
         final Top topContainer = new TopBuilder().setTestUnionLeaf(chosenIdentity).build();
         // translate via codec into NN
-        final Map.Entry<YangInstanceIdentifier, NormalizedNode> translated =
-            codecContext.toNormalizedNode(InstanceIdentifier.builder(Top.class).build(), topContainer);
+        final var translated = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.builder(Top.class).build(),
+            topContainer);
         assertNotNull(translated);
         // verify translation worked
-        final NormalizedNode translatedNN = translated.getValue();
+        final var translatedNN = translated.node();
         assertNotNull(translatedNN);
         // verify the union leaf is present
         // verify the leaf is the correct identity
index 8ace00fca759c534becf65d468300f778b98da0c..0ed542236f47c81d6071e18d21c81c0ee3e24365 100644 (file)
@@ -69,7 +69,7 @@ public final class ListsBindingUtils {
         return new TopLevelListBuilder().withKey(key).build();
     }
 
-    public static TopLevelList topLevelList(final TopLevelListKey key, final TreeComplexUsesAugment augment) {
+    public static TopLevelList topLevelList(final TopLevelListKey key, final Augmentation<TopLevelList> augment) {
         return new TopLevelListBuilder().withKey(key).addAugmentation(augment).build();
     }
 
index ea726bdc64e490c56e405568b44f182d99b93c76..f2bc6d2dec80cd6c246ae271ae247cbd9207aedd 100644 (file)
@@ -41,7 +41,8 @@ final class LegacyContentBuilder implements YangLibraryContentBuilderWithLegacy
 
     LegacyContentBuilder(final YangLibraryContentBuilderImpl delegate, final BindingCodecTree codecTree) {
         this.delegate = requireNonNull(delegate);
-        legacyCodec = verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)));
+        legacyCodec = (BindingDataObjectCodecTreeNode<ModulesState>)
+            verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)));
     }
 
     @Override
index 5f4c79f478b28a1f7410996907be092d4aff7621..a78455442f3955f92462293179bd63375f906e36 100644 (file)
@@ -49,7 +49,8 @@ final class YangLibraryContentBuilderImpl implements YangLibraryContentBuilder {
 
     YangLibraryContentBuilderImpl(final BindingCodecTree codecTree) {
         this.codecTree = Objects.requireNonNull(codecTree);
-        codec = verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)));
+        codec = (BindingDataObjectCodecTreeNode<YangLibrary>)
+            verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)));
     }
 
     @Override
index f1b6b7127fc803c91aa54a85263b1b6737816b86..794e7786af8d384c9b15184a6b5fdd2e9ff645b7 100644 (file)
@@ -67,8 +67,10 @@ public final class YangLibrarySupport implements YangLibSupport {
             generator.generateTypeMapping(context), snapshot));
 
         identityCodec = codecTree.getIdentityCodec();
-        codec = verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)));
-        legacyCodec = verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)));
+        codec = (BindingDataObjectCodecTreeNode<YangLibrary>)
+            verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)));
+        legacyCodec = (BindingDataObjectCodecTreeNode<ModulesState>)
+            verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)));
     }
 
     @Override
index 1dbff2f5144653f31b8a0cd6ed125f5e9a29d54d..e56460a03d152dcb3ef87a5c03e431737f8e4c2a 100644 (file)
@@ -31,8 +31,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 public class LegacyYangLibraryFormatTest extends AbstractYangLibraryTest {
     @Test
     public void testLegacyFormat() {
-        final BindingDataObjectCodecTreeNode<ModulesState> legacyCodec =
-                codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class));
+        final var legacyCodec = (BindingDataObjectCodecTreeNode<ModulesState>)
+            codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class));
 
         final Optional<ContainerNode> legacyContent = yangLib.newContentBuilder()
             .defaultContext(runtimeContext.getEffectiveModelContext())
index ef43cb7d2ee42ed6098b1b5aad1b6fef83f9c601..fce3e08d65d534ccb0c95e9690a3f272ada38eb3 100644 (file)
@@ -29,8 +29,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 public class YangLibrarySupportTest extends AbstractYangLibraryTest {
     @Test
     public void testFormatSchema() {
-        final BindingDataObjectCodecTreeNode<YangLibrary> codec =
-                codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class));
+        final var codec = (BindingDataObjectCodecTreeNode<YangLibrary>)
+            codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class));
 
         final ContainerNode nonLegacyContent = yangLib.newContentBuilder()
                 .defaultContext(runtimeContext.getEffectiveModelContext()).formatYangLibraryContent();