From: Robert Varga Date: Mon, 22 May 2023 12:04:24 +0000 (+0200) Subject: Update binding-dom adaptation to remove AugmentationNode X-Git-Tag: v12.0.0~110 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=5901ab384ee5e8c77c19a0e6fde35b7804973dbd;p=mdsal.git Update binding-dom adaptation to remove AugmentationNode 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 Signed-off-by: Robert Varga --- 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 index 0000000000..e0672ae9db --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractDataObjectModification.java @@ -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. + * + *

+ * This class is further specialized as {@link LazyAugmentationModification} and {@link LazyDataObjectModification}, as + * both use different serialization methods. + * + * @param Type of Binding {@link DataObject} + * @param Type of underlying {@link CommonDataObjectCodecTreeNode} + */ +abstract sealed class AbstractDataObjectModification> + implements DataObjectModification + 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> 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 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 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(); + 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> getModifiedChildren() { + var local = childNodesCache; + if (local == null) { + childNodesCache = local = createModifiedChilden(codec, domData, domChildNodes()); + } + return local; + } + + @Override + public final > List> getModifiedChildren( + final Class childType) { + return streamModifiedChildren(childType).collect(Collectors.toList()); + } + + @Override + public final & DataObject, C extends ChildOf> + List> getModifiedChildren(final Class caseType, final Class childType) { + return streamModifiedChildren(childType) + .filter(child -> caseType.equals(child.identifier.getCaseType().orElse(null))) + .collect(Collectors.toList()); + } + + @SuppressWarnings("unchecked") + private Stream> streamModifiedChildren( + final Class childType) { + return getModifiedChildren().stream() + .filter(child -> childType.isAssignableFrom(child.getDataType())) + .map(child -> (LazyDataObjectModification) child); + } + + @Override + @SuppressWarnings("unchecked") + public final & ChildOf, K extends Identifier> DataObjectModification + getModifiedChildListItem(final Class listItem, final K listKey) { + return (DataObjectModification) getModifiedChild(IdentifiableItem.of(listItem, listKey)); + } + + @Override + @SuppressWarnings("unchecked") + public final & DataObject, C extends Identifiable & ChildOf, + K extends Identifier> DataObjectModification getModifiedChildListItem(final Class caseType, + final Class listItem, final K listKey) { + return (DataObjectModification) getModifiedChild(IdentifiableItem.of(caseType, listItem, listKey)); + } + + @Override + @SuppressWarnings("unchecked") + public final > DataObjectModification getModifiedChildContainer( + final Class child) { + return (DataObjectModification) getModifiedChild(Item.of(child)); + } + + @Override + @SuppressWarnings("unchecked") + public final & DataObject, C extends ChildOf> DataObjectModification + getModifiedChildContainer(final Class caseType, final Class child) { + return (DataObjectModification) getModifiedChild(Item.of(caseType, child)); + } + + @Override + @SuppressWarnings("unchecked") + public final & DataObject> DataObjectModification getModifiedAugmentation( + final Class augmentation) { + return (DataObjectModification) 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 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> createModifiedChilden( + final CommonDataObjectCodecTreeNode parentCodec, final DataTreeCandidateNode parent, + final Collection children) { + final var result = ImmutableList.>builder(); + populateList(result, parentCodec, parent, children); + return result.build(); + } + + private static void populateList(final ImmutableList.Builder> result, + final CommonDataObjectCodecTreeNode parentCodec, final DataTreeCandidateNode parent, + final Collection children) { + final var augmentChildren = + ArrayListMultimap., 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> 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> result, + final BindingDataObjectCodecTreeNode codec, final Collection childNodes) { + for (var child : childNodes) { + if (child.getModificationType() != UNMODIFIED) { + result.add(new LazyDataObjectModification<>(codec, child)); + } + } + } +} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java index e7bbc922a2..79b9c2febb 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java @@ -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 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 im final InstanceIdentifier 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 Optional decodeRead(final CommonDataObjectCodecTreeNode 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 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 @NonNull FluentFuture> doExecute( diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingClusteredDOMDataTreeChangeListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingClusteredDOMDataTreeChangeListenerAdapter.java index cc031c0bdb..d3e9cfc9ac 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingClusteredDOMDataTreeChangeListenerAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingClusteredDOMDataTreeChangeListenerAdapter.java @@ -22,7 +22,8 @@ import org.opendaylight.yangtools.yang.binding.DataObject; final class BindingClusteredDOMDataTreeChangeListenerAdapter extends BindingDOMDataTreeChangeListenerAdapter implements ClusteredDOMDataTreeChangeListener { BindingClusteredDOMDataTreeChangeListenerAdapter(final AdapterContext codec, - final ClusteredDataTreeChangeListener listener, final LogicalDatastoreType store) { - super(codec, listener, store); + final ClusteredDataTreeChangeListener listener, final LogicalDatastoreType store, + final Class augment) { + super(codec, listener, store, augment); } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java index 915eeee350..349115e363 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataBrokerAdapter.java @@ -93,9 +93,9 @@ public class BindingDOMDataBrokerAdapter extends AbstractBindingAdapter<@NonNull } @Override - public > ListenerRegistration - registerDataTreeChangeListener( - final DataTreeIdentifier treeId, final L listener) { + public > + ListenerRegistration registerDataTreeChangeListener(final DataTreeIdentifier treeId, + final L listener) { if (treeChangeService == null) { throw new UnsupportedOperationException("Underlying data broker does not expose DOMDataTreeChangeService."); } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java index 61240d57f4..65de385c4b 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeListenerAdapter.java @@ -24,22 +24,32 @@ class BindingDOMDataTreeChangeListenerAdapter implements D private final AdapterContext adapterContext; private final DataTreeChangeListener listener; private final LogicalDatastoreType store; + private final Class augment; + + private boolean initialSyncDone; BindingDOMDataTreeChangeListenerAdapter(final AdapterContext adapterContext, - final DataTreeChangeListener listener, final LogicalDatastoreType store) { + final DataTreeChangeListener listener, final LogicalDatastoreType store, final Class augment) { this.adapterContext = requireNonNull(adapterContext); this.listener = requireNonNull(listener); this.store = requireNonNull(store); + this.augment = augment; } @Override public void onDataTreeChanged(final List domChanges) { - listener.onDataTreeChanged(LazyDataTreeModification.from(adapterContext.currentSerializer(), domChanges, - store)); + final var changes = LazyDataTreeModification.from(adapterContext.currentSerializer(), domChanges, store, + augment); + if (!changes.isEmpty()) { + listener.onDataTreeChanged(changes); + } else if (!initialSyncDone) { + onInitialData(); + } } @Override public void onInitialData() { + initialSyncDone = true; listener.onInitialData(); } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java index 1bde0b4b0c..ea84fdc514 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeChangeServiceAdapter.java @@ -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 treeId, final L listener) { final DOMDataTreeIdentifier domIdentifier = toDomTreeIdentifier(treeId); final LogicalDatastoreType storeType = treeId.getDatastoreType(); + final Class target = treeId.getRootIdentifier().getTargetType(); + final Class augment = Augmentation.class.isAssignableFrom(target) ? target : null; + final BindingDOMDataTreeChangeListenerAdapter domListener = listener instanceof ClusteredDataTreeChangeListener ? new BindingClusteredDOMDataTreeChangeListenerAdapter<>( - adapterContext(), (ClusteredDataTreeChangeListener) listener, storeType) - : new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), listener, storeType); + adapterContext(), (ClusteredDataTreeChangeListener) listener, storeType, augment) + : new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), listener, storeType, augment); final ListenerRegistration> domReg = getDelegate().registerDataTreeChangeListener(domIdentifier, domListener); diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapter.java index f3f15b7aa3..f2e6fb5093 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapter.java @@ -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 extends AbstractBindingAdapter> implements DOMDataTreeCommitCohort { - BindingDOMDataTreeCommitCohortAdapter(final AdapterContext codec, final DataTreeCommitCohort cohort) { + private final Class augment; + + BindingDOMDataTreeCommitCohortAdapter(final AdapterContext codec, final DataTreeCommitCohort cohort, + final Class augment) { super(codec, cohort); + this.augment = augment; } @Override public FluentFuture canCommit(final Object txId, final EffectiveModelContext ctx, final Collection candidates) { - final Collection> modifications = candidates.stream() - .map(candidate -> LazyDataTreeModification.create(currentSerializer(), candidate)) - .collect(Collectors.toList()); - return getDelegate().canCommit(txId, modifications); + return getDelegate().canCommit(txId, candidates.stream() + .map(candidate -> LazyDataTreeModification.from(currentSerializer(), candidate, augment)) + .filter(Objects::nonNull) + .collect(Collectors.toList())); } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortRegistryAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortRegistryAdapter.java index 3cf014857c..89624d4499 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortRegistryAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortRegistryAdapter.java @@ -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 > ObjectRegistration registerCommitCohort( final DataTreeIdentifier subtree, final T cohort) { - final BindingDOMDataTreeCommitCohortAdapter adapter = - new BindingDOMDataTreeCommitCohortAdapter<>(adapterContext(), cohort); + final Class target = subtree.getRootIdentifier().getTargetType(); + + final BindingDOMDataTreeCommitCohortAdapter 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<>() { diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMWriteTransactionAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMWriteTransactionAdapter.java index 0c6bef8814..a1dfc58457 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMWriteTransactionAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMWriteTransactionAdapter.java @@ -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 extends AbstractForwardedTransaction @@ -30,41 +35,92 @@ class BindingDOMWriteTransactionAdapter e @Override public final void put(final LogicalDatastoreType store, final InstanceIdentifier path, final U data) { - final Entry 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(); + 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 void mergeParentStructurePut(final LogicalDatastoreType store, final InstanceIdentifier path, final U data) { - final CurrentAdapterSerializer serializer = adapterContext().currentSerializer(); - final Entry 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 void merge(final LogicalDatastoreType store, final InstanceIdentifier path, final D data) { - final Entry 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 void mergeParentStructureMerge(final LogicalDatastoreType store, final InstanceIdentifier path, final U data) { - final CurrentAdapterSerializer serializer = adapterContext().currentSerializer(); - final Entry 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 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 Entry toNormalized(final String operation, + private @NonNull NormalizedResult toNormalized(final String operation, final InstanceIdentifier path, final U data) { return toNormalized(adapterContext().currentSerializer(), operation, path, data); } - private static Entry toNormalized( + private static @NonNull NormalizedResult toNormalized( final CurrentAdapterSerializer serializer, final String operation, final InstanceIdentifier path, final U data) { checkArgument(!path.isWildcarded(), "Cannot %s data into wildcarded path %s", operation, path); diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingStructuralType.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingStructuralType.java index 168d815ba1..5d9f7b3765 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingStructuralType.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingStructuralType.java @@ -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) { diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java index a6a8282a26..c5512ca321 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/CurrentAdapterSerializer.java @@ -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 index 0000000000..9b1d1e4ecc --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyAugmentationModification.java @@ -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> + extends AbstractDataObjectModification> { + private final @NonNull ImmutableList domChildNodes; + + private LazyAugmentationModification(final BindingAugmentationCodecTreeNode codec, + final DataTreeCandidateNode parent, final ImmutableList domChildNodes) { + super(parent, codec, codec.deserializePathArgument(null)); + this.domChildNodes = requireNonNull(domChildNodes); + } + + static > @Nullable LazyAugmentationModification forModifications( + final BindingAugmentationCodecTreeNode codec, final DataTreeCandidateNode parent, + final Collection 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 > @Nullable LazyAugmentationModification forParent( + final BindingAugmentationCodecTreeNode codec, final DataTreeCandidateNode parent) { + final var builder = ImmutableList.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 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); + } +} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataObjectModification.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataObjectModification.java index da5099ee15..b030e0ede3 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataObjectModification.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataObjectModification.java @@ -7,279 +7,52 @@ */ 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}. * *

- * {@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 Type of Binding Data Object */ -final class LazyDataObjectModification implements DataObjectModification { - private static final Logger LOG = LoggerFactory.getLogger(LazyDataObjectModification.class); - - private final BindingDataObjectCodecTreeNode codec; - private final DataTreeCandidateNode domData; - private final PathArgument identifier; - - private volatile List> childNodesCache; - private volatile ModificationType modificationType; - - private LazyDataObjectModification(final BindingDataObjectCodecTreeNode codec, - final DataTreeCandidateNode domData) { - this.codec = requireNonNull(codec); - this.domData = requireNonNull(domData); - identifier = codec.deserializePathArgument(domData.getIdentifier()); - } - - static LazyDataObjectModification create(final BindingDataObjectCodecTreeNode codec, - final DataTreeCandidateNode domData) { - return new LazyDataObjectModification<>(codec, domData); - } - - private static List> from(final BindingDataObjectCodecTreeNode parentCodec, - final Collection domChildNodes) { - final var result = new ArrayList>(domChildNodes.size()); - populateList(result, parentCodec, domChildNodes); - return result; - } - - private static void populateList(final List> result, - final BindingDataObjectCodecTreeNode parentCodec, - final Collection 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> 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> result, - final BindingDataObjectCodecTreeNode codec, final Collection 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 getDataType() { - return codec.getBindingClass(); - } - - @Override - public PathArgument getIdentifier() { - return identifier; +final class LazyDataObjectModification + extends AbstractDataObjectModification> { + LazyDataObjectModification(final BindingDataObjectCodecTreeNode 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> getModifiedChildren() { - var local = childNodesCache; - if (local == null) { - childNodesCache = local = from(codec, domData.getChildNodes()); - } - return local; - } - - @Override - public > List> getModifiedChildren( - final Class childType) { - return streamModifiedChildren(childType).collect(Collectors.toList()); - } - - @Override - public & DataObject, C extends ChildOf> - List> getModifiedChildren(final Class caseType, final Class childType) { - return streamModifiedChildren(childType) - .filter(child -> caseType.equals(child.identifier.getCaseType().orElse(null))) - .collect(Collectors.toList()); - } - - @SuppressWarnings("unchecked") - private Stream> streamModifiedChildren( - final Class childType) { - return getModifiedChildren().stream() - .filter(child -> childType.isAssignableFrom(child.getDataType())) - .map(child -> (LazyDataObjectModification) child); + Collection domChildNodes() { + return domData.getChildNodes(); } @Override - public DataObjectModification getModifiedChild(final PathArgument arg) { - final var domArgumentList = new ArrayList(); - 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 & ChildOf, K extends Identifier> DataObjectModification - getModifiedChildListItem(final Class listItem, final K listKey) { - return (DataObjectModification) getModifiedChild(IdentifiableItem.of(listItem, listKey)); + T deserialize(final NormalizedNode normalized) { + return codec.deserialize(normalized); } @Override - @SuppressWarnings("unchecked") - public & DataObject, C extends Identifiable & ChildOf, - K extends Identifier> DataObjectModification getModifiedChildListItem(final Class caseType, - final Class listItem, final K listKey) { - return (DataObjectModification) getModifiedChild(IdentifiableItem.of(caseType, listItem, listKey)); + DataTreeCandidateNode firstModifiedChild(final PathArgument arg) { + return domData.getModifiedChild(arg).orElse(null); } @Override - @SuppressWarnings("unchecked") - public > DataObjectModification getModifiedChildContainer(final Class child) { - return (DataObjectModification) getModifiedChild(Item.of(child)); - } - - @Override - @SuppressWarnings("unchecked") - public & DataObject, C extends ChildOf> DataObjectModification - getModifiedChildContainer(final Class caseType, final Class child) { - return (DataObjectModification) getModifiedChild(Item.of(caseType, child)); - } - - @Override - @SuppressWarnings("unchecked") - public & DataObject> DataObjectModification getModifiedAugmentation( - final Class augmentation) { - return (DataObjectModification) getModifiedChild(Item.of(augmentation)); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this).add("identifier", identifier).add("domData", domData).toString(); - } - - private T deserialize(final Optional dataAfter) { - return dataAfter.map(codec::deserialize).orElse(null); + ToStringHelper addToStringAttributes(final ToStringHelper helper) { + return super.addToStringAttributes(helper).add("domData", domData); } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java index 8c89a5a128..530b490649 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModification.java @@ -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 implements DataTreeMo private LazyDataTreeModification(final DataTreeIdentifier path, final DataObjectModification modification) { this.path = requireNonNull(path); - this.rootNode = requireNonNull(modification); + rootNode = requireNonNull(modification); } @SuppressWarnings({"unchecked", "rawtypes"}) - static DataTreeModification 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 @Nullable DataTreeModification from(final CurrentAdapterSerializer serializer, + final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType, final Class 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 DataTreeModification 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 @Nullable DataTreeModification from(final CurrentAdapterSerializer serializer, + final DOMDataTreeCandidate candidate, final Class 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 @NonNull List> from(final CurrentAdapterSerializer codec, - final List domChanges, final LogicalDatastoreType datastoreType) { - final List> result = new ArrayList<>(domChanges.size()); - for (final DataTreeCandidate domChange : domChanges) { - result.add(LazyDataTreeModification.create(codec, domChange, datastoreType)); + final List domChanges, final LogicalDatastoreType datastoreType, + final Class augment) { + final var result = new ArrayList>(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 getRootNode() { return rootNode; diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java index c57497c0de..65fd02ff53 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java @@ -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(); diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapterTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapterTest.java index 89f1148b4b..7777dd77bd 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapterTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeCommitCohortAdapterTest.java @@ -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(); diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java index 7383c4d037..cf739f471f 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingNormalizedCodecTest.java @@ -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 BA_TOP_LEVEL_LIST = InstanceIdentifier - .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).build(); - private static final InstanceIdentifier BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST - .augmentation(TreeLeafOnlyAugment.class); - private static final InstanceIdentifier 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) diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1418AugmentationTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1418AugmentationTest.java index 9977962b27..076ed09b58 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1418AugmentationTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1418AugmentationTest.java @@ -134,7 +134,9 @@ public class Bug1418AugmentationTest extends AbstractDataTreeChangeListenerTest final TestListener 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(); diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModificationTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModificationTest.java index 9a7b859184..5f968a95e8 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModificationTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LazyDataTreeModificationTest.java @@ -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 index 0000000000..9550c9399e --- /dev/null +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingAugmentationCodecTreeNode.java @@ -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> + extends CommonDataObjectCodecTreeNode { + /** + * 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 childPathArguments(); + + /** + * Returns the {@link Class}es of items contain in this {@link Augmentation}. + * + * @return A non-empty set of classes + */ + @NonNull ImmutableSet> childBindingClasses(); +} diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingCodecTree.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingCodecTree.java index 37ed59f5b1..b1f3a38cf3 100644 --- a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingCodecTree.java +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingCodecTree.java @@ -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 { + /** + * A DTO holding a {@link CommonDataObjectCodecTreeNode} and the corresponding {@link YangInstanceIdentifier}. + * + * @param {@link DataObject} type + */ + record CodecWithPath( + @NonNull CommonDataObjectCodecTreeNode 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 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 + */ + @NonNull CodecWithPath getSubtreeCodecWithPath(InstanceIdentifier path); + /** * Look up the codec for specified path. * @@ -32,10 +59,12 @@ public interface BindingCodecTree extends BindingDataObjectCodecTreeParent @NonNull BindingDataObjectCodecTreeNode getSubtreeCodec(InstanceIdentifier path); + @NonNull CommonDataObjectCodecTreeNode getSubtreeCodec(InstanceIdentifier path); + // FIXME: NonNull and throwing exception @Nullable BindingCodecTreeNode getSubtreeCodec(YangInstanceIdentifier path); + // FIXME: NonNull and throwing exception @Nullable BindingCodecTreeNode getSubtreeCodec(Absolute path); /** diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeNode.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeNode.java index 6804739b0a..f4d171401d 100644 --- a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeNode.java +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeNode.java @@ -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 - extends BindingDataObjectCodecTreeParent, BindingObjectCodecTreeNode, BindingNormalizedNodeCodec { - - /** - * Returns binding class of interface which represents API of current schema node. The result is same as invoking - * {@link DataObject#implementedInterface()} on instance of data. - * - * @return interface which defines API of binding representation of data. - */ - @Override - @NonNull Class getBindingClass(); - - /** - * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case, - * one must issue getChild(ChoiceClass).getChild(CaseClass). - * - *

- * This method differs from {@link #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. - */ - Optional> possibleStreamChild( - @NonNull Class 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 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, BindingNormalizedNodeCodec { /** * Returns codec which uses caches serialization / deserialization results. * @@ -124,22 +28,4 @@ public interface BindingDataObjectCodecTreeNode */ @NonNull BindingNormalizedNodeCachingCodec createCachingCodec( @NonNull ImmutableCollection> 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 - } } diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeParent.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeParent.java index 2e561e59a3..477140bb57 100644 --- a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeParent.java +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingDataObjectCodecTreeParent.java @@ -44,5 +44,5 @@ public interface BindingDataObjectCodecTreeParent { * @return Context of child * @throws IllegalArgumentException If supplied child class is not valid in specified context. */ - @NonNull BindingDataObjectCodecTreeNode streamChild(@NonNull Class childClass); + @NonNull CommonDataObjectCodecTreeNode streamChild(@NonNull Class childClass); } diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java index 773b177871..b026c7412b 100644 --- a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java @@ -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 possibleChildren, + @NonNull ImmutableList 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. */ - @NonNull Entry toNormalizedNode( - InstanceIdentifier path, T data); + @NonNull NormalizedResult toNormalizedNode(InstanceIdentifier 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 index 0000000000..1e3a25bf58 --- /dev/null +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/CommonDataObjectCodecTreeNode.java @@ -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 DataObject type + */ +@Beta +public interface CommonDataObjectCodecTreeNode + extends BindingDataObjectCodecTreeParent, BindingObjectCodecTreeNode { + /** + * Returns binding class of interface which represents API of current schema node. The result is same as invoking + * {@link DataObject#implementedInterface()} on instance of data. + * + * @return interface which defines API of binding representation of data. + */ + @Override + @NonNull Class getBindingClass(); + + /** + * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case, + * one must issue getChild(ChoiceClass).getChild(CaseClass). + * + *

+ * This method differs from {@link #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. + */ + Optional> possibleStreamChild( + @NonNull Class 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 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 + } +} diff --git a/binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java b/binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java index 39f8d6ede0..ac225ac44b 100644 --- a/binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java +++ b/binding/mdsal-binding-dom-codec-spi/src/main/java/org/opendaylight/mdsal/binding/dom/codec/spi/ForwardingBindingDOMCodecServices.java @@ -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 Entry toNormalizedNode( - final InstanceIdentifier path, final T data) { + public NormalizedResult toNormalizedNode(final InstanceIdentifier 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 rpcInputOrOutput, final NormalizedNodeStreamWriter streamWriter) { - return delegate().newRpcWriter(rpcInputOrOutput,streamWriter); + return delegate().newRpcWriter(rpcInputOrOutput, streamWriter); } @Override - public BindingDataObjectCodecTreeNode getSubtreeCodec(final InstanceIdentifier path) { + public CodecWithPath getSubtreeCodecWithPath(final InstanceIdentifier path) { + return delegate().getSubtreeCodecWithPath(path); + } + + @Override + public CommonDataObjectCodecTreeNode getSubtreeCodec(final InstanceIdentifier path) { return delegate().getSubtreeCodec(path); } @@ -195,7 +199,7 @@ public abstract class ForwardingBindingDOMCodecServices extends ForwardingObject } @Override - public BindingDataObjectCodecTreeNode streamChild(final Class childClass) { + public CommonDataObjectCodecTreeNode streamChild(final Class childClass) { return delegate().streamChild(childClass); } } diff --git a/binding/mdsal-binding-dom-codec/pom.xml b/binding/mdsal-binding-dom-codec/pom.xml index 59dcdf932f..71c4d0844d 100644 --- a/binding/mdsal-binding-dom-codec/pom.xml +++ b/binding/mdsal-binding-dom-codec/pom.xml @@ -48,10 +48,6 @@ org.opendaylight.yangtools yang-data-impl - - org.opendaylight.yangtools - yang-data-util - org.opendaylight.yangtools yang-model-api 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 index 0000000000..9fff1e6e42 --- /dev/null +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java @@ -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: + *

    + *
  1. DataObjectCodecContext has an exact DistinctNodeContainer and YangInstanceIdentifier mapping and can be the + * target of augmentations (i.e. can implement Augmentable contract)
  2. + *
  3. AugmentationNodeContext has neither of those traits and really is just a filter of its parent + * DistinctNodeContainer
  4. + *
+ * + *

+ * Unfortunately {@code Augmentation} is a also a {@link DataObject}, so things get a bit messy. + * + *

+ * 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 + extends DataContainerCodecContext { + private final ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; + private final ImmutableMap, DataContainerCodecPrototype> byStreamClass; + private final ImmutableMap byYang; + private final ImmutableMap leafChild; + private final MethodHandle proxyConstructor; + + AbstractDataObjectCodecContext(final DataContainerCodecPrototype prototype, + final CodecDataObjectAnalysis 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 DataContainerCodecContext streamChild(final Class childClass) { + return (DataContainerCodecContext) childNonNull(streamChildPrototype(childClass), childClass, + "Child %s is not valid child of %s", getBindingClass(), childClass).get(); + } + + @SuppressWarnings("unchecked") + @Override + public final Optional> possibleStreamChild( + final Class childClass) { + final var childProto = streamChildPrototype(childClass); + if (childProto != null) { + return Optional.of((DataContainerCodecContext) 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 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 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 byYangKeySet() { + return byYang.keySet(); + } + + final @NonNull ImmutableSet> byBindingArgClassKeySet() { + return byBindingArgClass.keySet(); + } + + abstract Map>, Augmentation> getAllAugmentationsFrom( + DistinctNodeContainer data); +} diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ActionCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ActionCodecContext.java index acd87508ea..b1ec0159aa 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ActionCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ActionCodecContext.java @@ -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 input; - private final DataContainerCodecContext output; + private final DataObjectCodecContext input; + private final DataObjectCodecContext output; - ActionCodecContext(final DataContainerCodecContext input, - final DataContainerCodecContext output) { + ActionCodecContext(final DataObjectCodecContext input, + final DataObjectCodecContext output) { this.input = requireNonNull(input); this.output = requireNonNull(output); } - DataContainerCodecContext input() { + DataObjectCodecContext input() { return input; } - DataContainerCodecContext output() { + DataObjectCodecContext output() { return output; } } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentableCodecDataObject.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentableCodecDataObject.java index dcee340dd5..25eadbf195 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentableCodecDataObject.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentableCodecDataObject.java @@ -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>, Augmentation> cachedAugmentations; - protected AugmentableCodecDataObject(final DataObjectCodecContext context, + protected AugmentableCodecDataObject(final AbstractDataObjectCodecContext context, final DistinctNodeContainer data) { super(context, data); } @@ -54,25 +54,24 @@ public abstract class AugmentableCodecDataObject> @Nullable A augmentation(final Class augmentationType) { requireNonNull(augmentationType, "Supplied augmentation must not be null."); - final ImmutableMap>, Augmentation> aug = acquireAugmentations(); + final var aug = acquireAugmentations(); if (aug != null) { return (A) aug.get(augmentationType); } @SuppressWarnings("rawtypes") - final Optional> optAugCtx = codecContext().possibleStreamChild( - (Class) augmentationType); + final var optAugCtx = codecContext().possibleStreamChild((Class) augmentationType); if (optAugCtx.isPresent()) { - final DataContainerCodecContext augCtx = optAugCtx.orElseThrow(); + final var augCtx = (AugmentationNodeContext) 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>, Augmentation> loadAugmentations() { - final ImmutableMap>, Augmentation> ret = ImmutableMap.copyOf( - codecContext().getAllAugmentationsFrom(codecData())); + private @NonNull ImmutableMap>, Augmentation> loadAugmentations() { + final var ret = ImmutableMap.copyOf(codecContext().getAllAugmentationsFrom(codecData())); final Object witness = CACHED_AUGMENTATIONS.compareAndExchangeRelease(this, null, ret); return witness == null ? ret : (ImmutableMap>, Augmentation>) witness; } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationNodeContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationNodeContext.java index 59f2e226e8..50573245c4 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationNodeContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationNodeContext.java @@ -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> - extends DataObjectCodecContext { - AugmentationNodeContext(final DataContainerCodecPrototype prototype) { - super(prototype); + extends AbstractDataObjectCodecContext implements BindingAugmentationCodecTreeNode { + 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 childPathArguments() { + return byYangKeySet(); + } + + @Override + public ImmutableSet> 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>, Augmentation> getAllAugmentationsFrom( + final DistinctNodeContainer data) { + return Map.of(); } -} \ No newline at end of file +} diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java index 4989f809a2..6519377167 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java @@ -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 BindingDataObjectCodecTreeNode streamChild(final Class childClass) { + public CommonDataObjectCodecTreeNode streamChild(final Class childClass) { return root.streamChild(childClass); } @Override @SuppressWarnings("unchecked") - public BindingDataObjectCodecTreeNode getSubtreeCodec(final InstanceIdentifier path) { + public CodecWithPath getSubtreeCodecWithPath(final InstanceIdentifier path) { + final var yangArgs = new ArrayList(); + final var codecContext = getCodecContextNode(path, yangArgs); + + // TODO Do we need defensive check here? + return new CodecWithPath<>((CommonDataObjectCodecTreeNode) codecContext, + YangInstanceIdentifier.create(yangArgs)); + } + + @Override + @SuppressWarnings("unchecked") + public CommonDataObjectCodecTreeNode getSubtreeCodec(final InstanceIdentifier path) { // TODO Do we need defensive check here? - return (BindingDataObjectCodecTreeNode) getCodecContextNode(path, null); + return (CommonDataObjectCodecTreeNode) getCodecContextNode(path, null); } @Override @@ -519,24 +547,43 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri } @Override - public Entry toNormalizedNode( - final InstanceIdentifier 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 NormalizedResult toNormalizedNode(final InstanceIdentifier path, final T data) { // We create Binding Stream Writer which translates from Binding to Normalized Nodes - final Entry writeCtx = newWriterAndIdentifier(path, - domWriter); + final var yangArgs = new ArrayList(); + 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 diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingToNormalizedStreamWriter.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingToNormalizedStreamWriter.java index 30fd3282dd..f5bdc9e795 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingToNormalizedStreamWriter.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingToNormalizedStreamWriter.java @@ -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 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> augmentationType) - throws IOException { - delegate.startAugmentationNode(enter(augmentationType, AugmentationIdentifier.class)); + public void startAugmentationNode(final Class> augmentationType) throws IOException { + enter(augmentationType, NodeIdentifier.class); } @Override diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java index 6f2e3a60ba..893938dfb2 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java @@ -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 extends AbstractBindingNormalizedNodeCacheHolder implements - BindingNormalizedNodeCachingCodec { - private final DataContainerCodecContext context; +class CachingNormalizedNodeCodec & BindingNormalizedNodeCodec> + extends AbstractBindingNormalizedNodeCacheHolder implements BindingNormalizedNodeCachingCodec { + private final @NonNull C context; - CachingNormalizedNodeCodec(final DataContainerCodecContext subtreeRoot, - final ImmutableSet> cacheSpec) { + CachingNormalizedNodeCodec(final C context, final ImmutableSet> cacheSpec) { super(cacheSpec); - this.context = requireNonNull(subtreeRoot); + this.context = requireNonNull(context); } @Override diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceNodeCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceNodeCodecContext.java index 46992c260f..39dd3f7c15 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceNodeCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceNodeCodecContext.java @@ -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 extends DataContainerCodecContext { +final class ChoiceNodeCodecContext extends DataContainerCodecContext + implements BindingDataObjectCodecTreeNode { private static final Logger LOG = LoggerFactory.getLogger(ChoiceNodeCodecContext.class); private final ImmutableMap> byYangCaseChild; @@ -134,16 +136,6 @@ final class ChoiceNodeCodecContext 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 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 extends DataContainerCo return getDomPathArgument(); } + @Override + public BindingNormalizedNodeCachingCodec createCachingCodec( + final ImmutableCollection> cacheSpecifier) { + return createCachingCodec(this, cacheSpecifier); + } + DataContainerCodecContext getCaseByChildClass(final @NonNull Class type) { DataContainerCodecPrototype result = byCaseChildClass.get(type); if (result == null) { diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.java index 1d7b2168cc..a7d01e51ea 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObject.java @@ -41,7 +41,7 @@ public abstract class CodecDataObject implements DataObjec } } - private final @NonNull DataObjectCodecContext context; + private final @NonNull AbstractDataObjectCodecContext context; @SuppressWarnings("rawtypes") private final @NonNull DistinctNodeContainer data; @@ -50,7 +50,8 @@ public abstract class CodecDataObject implements DataObjec // FIXME: consider using a primitive int-based cache (with 0 being uninit) private volatile Integer cachedHashcode; - protected CodecDataObject(final DataObjectCodecContext context, final DistinctNodeContainer data) { + protected CodecDataObject(final AbstractDataObjectCodecContext 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 implements DataObjec protected abstract boolean codecEquals(Object obj); - final @NonNull DataObjectCodecContext codecContext() { + final @NonNull AbstractDataObjectCodecContext codecContext() { return context; } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java index 71dd5a3ca3..455f2b4223 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java @@ -42,9 +42,9 @@ import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer; */ final class CodecDataObjectAnalysis { 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, DataContainerCodecPrototype> byStreamClass; final @NonNull ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java index 27a565652f..addd780649 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java @@ -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 extends NodeCodecContext - implements BindingDataObjectCodecTreeNode { + implements CommonDataObjectCodecTreeNode { private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecContext.class); private static final VarHandle EVENT_STREAM_SERIALIZER; @@ -70,7 +69,7 @@ abstract class DataContainerCodecContext prototype; + final @NonNull DataContainerCodecPrototype prototype; // Accessed via a VarHandle @SuppressWarnings("unused") @@ -98,7 +97,7 @@ abstract class DataContainerCodecContext builder) { if (builder != null) { - builder.add(getDomPathArgument()); + final var yangArg = getDomPathArgument(); + if (yangArg != null) { + builder.add(yangArg); + } } } @@ -178,19 +180,17 @@ abstract class DataContainerCodecContext createCachingCodec( - final ImmutableCollection> cacheSpecifier) { - if (cacheSpecifier.isEmpty()) { - return new NonCachingCodec<>(this); - } - return new CachingNormalizedNodeCodec<>(this, ImmutableSet.copyOf(cacheSpecifier)); + static final & BindingNormalizedNodeCodec> + @NonNull BindingNormalizedNodeCachingCodec createCachingCodec(final C context, + final ImmutableCollection> cacheSpecifier) { + return cacheSpecifier.isEmpty() ? new NonCachingCodec<>(context) + : new CachingNormalizedNodeCodec<>(context, ImmutableSet.copyOf(cacheSpecifier)); } protected final @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 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 implements NodeContextSupplier { +abstract sealed class DataContainerCodecPrototype implements NodeContextSupplier { + static final class Augmentation extends DataContainerCodecPrototype { + private final @NonNull ImmutableSet childArgs; + + @SuppressWarnings("unchecked") + Augmentation(final Class cls, final QNameModule namespace, final AugmentRuntimeType type, + final CodecContextFactory factory, final ImmutableSet childArgs) { + super(Item.of((Class) 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 getChildArgs() { + return childArgs; + } + } + + static final class Regular extends DataContainerCodecPrototype { + 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) 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 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) 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 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 instance; - @SuppressWarnings("unchecked") - private DataContainerCodecPrototype(final Class cls, final PathArgument yangArg, final T type, - final CodecContextFactory factory) { - this(Item.of((Class) 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 implemen } static DataContainerCodecPrototype 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 DataContainerCodecPrototype 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 DataContainerCodecPrototype from(final Item bindingArg, final T type, final CodecContextFactory factory) { - return new DataContainerCodecPrototype<>(bindingArg, createIdentifier(type), type, factory); - } - - static DataContainerCodecPrototype 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 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 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 get() { - final DataContainerCodecContext existing = (DataContainerCodecContext) INSTANCE.getAcquire(this); + public final DataContainerCodecContext get() { + final var existing = (DataContainerCodecContext) INSTANCE.getAcquire(this); return existing != null ? existing : loadInstance(); } + @SuppressWarnings("unchecked") + final DataObjectCodecContext getDataObject() { + final var context = get(); + verify(context instanceof DataObjectCodecContext, "Unexpected instance %s", context); + return (DataObjectCodecContext) context; + } + private @NonNull DataContainerCodecContext loadInstance() { final var tmp = createInstance(); final var witness = (DataContainerCodecContext) INSTANCE.compareAndExchangeRelease(this, null, tmp); @@ -250,25 +307,5 @@ final class DataContainerCodecPrototype 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 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) 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 createInstance(); } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java index 9c941089fd..44404ad346 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java @@ -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 - extends DataContainerCodecContext { + extends AbstractDataObjectCodecContext implements BindingDataObjectCodecTreeNode { private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class); private static final VarHandle MISMATCHED_AUGMENTED; @@ -67,14 +62,9 @@ public abstract class DataObjectCodecContext leafChild; - private final ImmutableMap byYang; - private final ImmutableMap, DataContainerCodecPrototype> byStreamClass; - private final ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; - private final ImmutableMap> augmentationByYang; - private final ImmutableMap, DataContainerCodecPrototype> augmentationByStream; + private final ImmutableMap, DataContainerCodecPrototype.Augmentation> augmentToPrototype; + private final ImmutableMap> yangToAugmentClass; private final @NonNull Class> 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 prototype, final CodecDataObjectAnalysis 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>(); - final var augByStream = new HashMap, DataContainerCodecPrototype>(); + final var augPathToBinding = new HashMap>(); + final var augClassToProto = new HashMap, 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 DataContainerCodecContext streamChild(final Class childClass) { - return (DataContainerCodecContext) childNonNull(streamChildPrototype(childClass), childClass, - "Child %s is not valid child of %s", getBindingClass(), childClass).get(); + final DataContainerCodecPrototype pathChildPrototype(final Class 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 Optional> possibleStreamChild( - final Class childClass) { - final DataContainerCodecPrototype childProto = streamChildPrototype(childClass); - if (childProto != null) { - return Optional.of((DataContainerCodecContext) 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 builder) { - - final Class 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> caseType = arg.getCaseType(); - final Class 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, DataContainerCodecPrototype> local = - (ImmutableMap, DataContainerCodecPrototype>) MISMATCHED_AUGMENTED.getAcquire(this); - final DataContainerCodecPrototype mismatched = local.get(childClass); + final var local = + (ImmutableMap, 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, DataContainerCodecPrototype> oldMismatched, + private DataContainerCodecPrototype.@Nullable Augmentation loadMismatchedAugmentation( + final ImmutableMap, 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 cacheMismatched( - final @NonNull ImmutableMap, DataContainerCodecPrototype> oldMismatched, - final @NonNull Class childClass, final @NonNull DataContainerCodecPrototype prototype) { + private DataContainerCodecPrototype.@NonNull Augmentation cacheMismatched( + final @NonNull ImmutableMap, DataContainerCodecPrototype.Augmentation> oldMismatched, + final @NonNull Class childClass, final DataContainerCodecPrototype.@NonNull Augmentation prototype) { - ImmutableMap, DataContainerCodecPrototype> expected = oldMismatched; + var expected = oldMismatched; while (true) { - final Map, DataContainerCodecPrototype> newMismatched = - ImmutableMap., DataContainerCodecPrototype>builderWithExpectedSize(expected.size() + 1) - .putAll(expected) - .put(childClass, prototype) - .build(); + final var newMismatched = + ImmutableMap., DataContainerCodecPrototype>builderWithExpectedSize(expected.size() + 1) + .putAll(expected) + .put(childClass, prototype) + .build(); - final var witness = (ImmutableMap, DataContainerCodecPrototype>) + final var witness = (ImmutableMap, 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 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 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 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>, Augmentation> getAllAugmentationsFrom( final DistinctNodeContainer 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, 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 createCachingCodec( + final ImmutableCollection> cacheSpecifier) { + return createCachingCodec(this, cacheSpecifier); + } } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java index a964068d52..64615d607f 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java @@ -130,7 +130,7 @@ final class NotificationCodecContext 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); diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java index b93ec3f2ad..220f78a24d 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java @@ -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 extends DataContainerCodecContext { +final class SchemaRootCodecContext extends DataContainerCodecContext + implements BindingDataObjectCodecTreeNode { private final LoadingCache, DataContainerCodecContext> childrenByClass = CacheBuilder.newBuilder().build(new CacheLoader<>() { @@ -231,6 +236,11 @@ final class SchemaRootCodecContext 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> action) { return getOrRethrow(actionsByClass, action); } @@ -284,9 +294,9 @@ final class SchemaRootCodecContext 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 Class asClass(final Type type, final Class target) { @@ -361,6 +371,12 @@ final class SchemaRootCodecContext extends DataContainerCo return super.bindingPathArgumentChild(arg, builder); } + @Override + public BindingNormalizedNodeCachingCodec createCachingCodec( + final ImmutableCollection> cacheSpecifier) { + return createCachingCodec(this, cacheSpecifier); + } + private static Class findCaseChoice(final Class caseClass) { for (var type : caseClass.getGenericInterfaces()) { if (type instanceof Class typeClass && ChoiceIn.class.isAssignableFrom(typeClass)) { diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingCodecTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingCodecTest.java index 9a71137607..78bc227483 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingCodecTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingCodecTest.java @@ -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 thereAndBackAgain(final InstanceIdentifier path, final T data) { - final Entry there = codecContext.toNormalizedNode(path, data); - final Entry, 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(); } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnydataLeafTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnydataLeafTest.java index c5be2078b6..982d1deaa4 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnydataLeafTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnydataLeafTest.java @@ -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 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 { diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnyxmlLeafTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnyxmlLeafTest.java index c78e7e4787..584c4fa11f 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnyxmlLeafTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AnyxmlLeafTest.java @@ -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 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 { diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationSubstitutionTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationSubstitutionTest.java index 73495710de..314fc5a463 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationSubstitutionTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationSubstitutionTest.java @@ -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 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); diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/BinaryKeyTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/BinaryKeyTest.java index 29876e1700..250320fb4f 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/BinaryKeyTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/BinaryKeyTest.java @@ -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 instanceIdentifier = InstanceIdentifier.create(BinaryList.class); @@ -46,8 +44,7 @@ public class BinaryKeyTest extends AbstractBindingCodecTest { } private BinaryList process(final BinaryList binaryList) { - final Entry 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(); } } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5524augmentUses.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5524augmentUses.java index 242894b3f6..37792e83e6 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5524augmentUses.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5524augmentUses.java @@ -69,7 +69,7 @@ public class Bug5524augmentUses extends AbstractBindingCodecTest { .build()) .build(); - final BindingDataObjectCodecTreeNode subtreeCodec = codecContext.getSubtreeCodec( + final var subtreeCodec = (BindingDataObjectCodecTreeNode) codecContext.getSubtreeCodec( InstanceIdentifier.create(Module4Main.class)); final NormalizedNode serialized = subtreeCodec.serialize(module4Main); final NormalizedNode manualSerialized = subtreeCodec.serialize(manualModule4Main); diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5845booleanKeyTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5845booleanKeyTest.java index 99d5ba6cbe..6fe7a53f27 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5845booleanKeyTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Bug5845booleanKeyTest.java @@ -41,7 +41,7 @@ public class Bug5845booleanKeyTest extends AbstractBindingCodecTest { .build())) .build(); - final BindingDataObjectCodecTreeNode subtreeCodec = codecContext.getSubtreeCodec( + final var subtreeCodec = (BindingDataObjectCodecTreeNode) codecContext.getSubtreeCodec( InstanceIdentifier.create(BooleanContainer.class)); final NormalizedNode serializedInt = subtreeCodec.serialize(booleanContainerInt); assertNotNull(serializedInt); diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingCodecTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingCodecTest.java index 3cf587f867..adaa456dc0 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingCodecTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingCodecTest.java @@ -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) codecContext.getSubtreeCodec(TOP_PATH); + contNode = (BindingDataObjectCodecTreeNode) codecContext.getSubtreeCodec(CONT_PATH); } private static Map createList(final int num) { diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CaseSubstitutionTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CaseSubstitutionTest.java index 5af771e14f..b8b4f84b22 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CaseSubstitutionTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/CaseSubstitutionTest.java @@ -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); } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/EmptyLeafTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/EmptyLeafTest.java index 3a7594a616..467d7244c1 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/EmptyLeafTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/EmptyLeafTest.java @@ -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 BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class) .child(TopLevelList.class, TOP_FOO_KEY).build(); - private static final InstanceIdentifier BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST - .augmentation(TreeLeafOnlyAugment.class); - private static final InstanceIdentifier 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 dom = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, + final var dom = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, withEmptyCase); - final Entry, 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(); } } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierSerializeDeserializeTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierSerializeDeserializeTest.java index 941b86c6c7..2d63758e26 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierSerializeDeserializeTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierSerializeDeserializeTest.java @@ -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 BA_TOP_LEVEL_LIST = InstanceIdentifier .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).build(); - private static final InstanceIdentifier BA_TREE_LEAF_ONLY = - BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class); - private static final InstanceIdentifier 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( diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierTest.java index b7ebc32d73..4ce746aec5 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/InstanceIdentifierTest.java @@ -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 BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST .augmentation(TreeLeafOnlyAugment.class); - private static final InstanceIdentifier 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 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 diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/KeyInheritenceTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/KeyInheritenceTest.java index 18bf6c92e6..ec9bfe4cc1 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/KeyInheritenceTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/KeyInheritenceTest.java @@ -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 domDef = codecContext.toNormalizedNode(DEF_IID, DEF); - Entry, DataObject> entry = codecContext.fromNormalizedNode(domDef.getKey(), - domDef.getValue()); + final var domDef = (NodeResult) codecContext.toNormalizedNode(DEF_IID, DEF); + Entry, DataObject> entry = codecContext.fromNormalizedNode(domDef.path(), domDef.node()); assertEquals(DEF_IID, entry.getKey()); final Def codecDef = (Def) entry.getValue(); - final Entry 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()); } } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java index 25015fae3d..099eb0aa42 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java @@ -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 BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class) - .child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeComplexLeaves.class).build(); + private static final InstanceIdentifier 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 dom = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, - binding); - final Entry, 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); } } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafrefSerializeDeserializeTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafrefSerializeDeserializeTest.java index d9207dff62..7fecd0b8ba 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafrefSerializeDeserializeTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafrefSerializeDeserializeTest.java @@ -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 BA_II_CONT = InstanceIdentifier.builder(Cont.class).build(); final Ref refVal = new Ref("myvalue"); final Cont data = new ContBuilder().setRef(refVal).build(); - final Entry normalizedNode = - this.codecContext.toNormalizedNode(BA_II_CONT, data); + final var normalizedNode = (NodeResult) codecContext.toNormalizedNode(BA_II_CONT, data); assertNotNull(normalizedNode); - final Entry, 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 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 normalizedNode = - this.codecContext.toNormalizedNode(BA_II_CONT, data); + final var normalizedNode = (NodeResult) codecContext.toNormalizedNode(BA_II_CONT, data); assertNotNull(normalizedNode); - final Entry, 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()); diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Mdsal668Test.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Mdsal668Test.java index 5d8580297e..5a6d826a1a 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Mdsal668Test.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/Mdsal668Test.java @@ -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()); } } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NormalizedNodeSerializeDeserializeTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NormalizedNodeSerializeDeserializeTest.java index e79e3b7725..5e7c032507 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NormalizedNodeSerializeDeserializeTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/NormalizedNodeSerializeDeserializeTest.java @@ -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 BA_TOP_LEVEL_LIST = InstanceIdentifier .builder(Top.class).child(TopLevelList.class, TOP_LEVEL_LIST_FOO_KEY).build(); - private static final InstanceIdentifier BA_TREE_LEAF_ONLY = - BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class); - private static final InstanceIdentifier 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 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, 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, 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, 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 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, 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 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, 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 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 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, 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, 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 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 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, 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 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, 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()); diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/SpecializingLeafrefTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/SpecializingLeafrefTest.java index f4d9c1e847..e09f5d9cf0 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/SpecializingLeafrefTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/SpecializingLeafrefTest.java @@ -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 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 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 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 testSet = Set.of("test"); final BarCont barCont = new BarContBuilder().setLeafList1(testSet).build(); - final Map.Entry 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); } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/TypedefTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/TypedefTest.java index 28c89be391..89762a0721 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/TypedefTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/TypedefTest.java @@ -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 BA_DEFAULT_POLICY = InstanceIdentifier.builder(DefaultPolicy.class).build(); private static final InstanceIdentifier BA_TEST_CONT = @@ -38,13 +33,10 @@ public class TypedefTest extends AbstractBindingCodecTest { .setAction2(new PolicyLoggingFlag(false)) .setAction3(true) .build(); - final Entry dom = - codecContext.toNormalizedNode(BA_DEFAULT_POLICY, binding); - final Entry, 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 dom = - codecContext.toNormalizedNode(BA_TEST_CONT, binding); - final Entry, 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()); } } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/UnionTypeWithMultipleIdentityrefsTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/UnionTypeWithMultipleIdentityrefsTest.java index e2eef29f93..5be7477d55 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/UnionTypeWithMultipleIdentityrefsTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/UnionTypeWithMultipleIdentityrefsTest.java @@ -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, 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 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 diff --git a/binding/mdsal-binding-test-model/src/main/java/org/opendaylight/mdsal/binding/test/model/util/ListsBindingUtils.java b/binding/mdsal-binding-test-model/src/main/java/org/opendaylight/mdsal/binding/test/model/util/ListsBindingUtils.java index 8ace00fca7..0ed542236f 100644 --- a/binding/mdsal-binding-test-model/src/main/java/org/opendaylight/mdsal/binding/test/model/util/ListsBindingUtils.java +++ b/binding/mdsal-binding-test-model/src/main/java/org/opendaylight/mdsal/binding/test/model/util/ListsBindingUtils.java @@ -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 augment) { return new TopLevelListBuilder().withKey(key).addAugmentation(augment).build(); } diff --git a/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyContentBuilder.java b/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyContentBuilder.java index ea726bdc64..f2bc6d2dec 100644 --- a/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyContentBuilder.java +++ b/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyContentBuilder.java @@ -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) + verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class))); } @Override diff --git a/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibraryContentBuilderImpl.java b/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibraryContentBuilderImpl.java index 5f4c79f478..a78455442f 100644 --- a/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibraryContentBuilderImpl.java +++ b/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibraryContentBuilderImpl.java @@ -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) + verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class))); } @Override diff --git a/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupport.java b/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupport.java index f1b6b7127f..794e7786af 100644 --- a/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupport.java +++ b/yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupport.java @@ -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) + verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class))); + legacyCodec = (BindingDataObjectCodecTreeNode) + verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class))); } @Override diff --git a/yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyYangLibraryFormatTest.java b/yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyYangLibraryFormatTest.java index 1dbff2f514..e56460a03d 100644 --- a/yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyYangLibraryFormatTest.java +++ b/yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/LegacyYangLibraryFormatTest.java @@ -31,8 +31,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; public class LegacyYangLibraryFormatTest extends AbstractYangLibraryTest { @Test public void testLegacyFormat() { - final BindingDataObjectCodecTreeNode legacyCodec = - codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)); + final var legacyCodec = (BindingDataObjectCodecTreeNode) + codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)); final Optional legacyContent = yangLib.newContentBuilder() .defaultContext(runtimeContext.getEffectiveModelContext()) diff --git a/yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupportTest.java b/yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupportTest.java index ef43cb7d2e..fce3e08d65 100644 --- a/yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupportTest.java +++ b/yanglib/mdsal-yanglib-rfc8525/src/test/java/org/opendaylight/mdsal/yanglib/rfc8525/YangLibrarySupportTest.java @@ -29,8 +29,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; public class YangLibrarySupportTest extends AbstractYangLibraryTest { @Test public void testFormatSchema() { - final BindingDataObjectCodecTreeNode codec = - codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)); + final var codec = (BindingDataObjectCodecTreeNode) + codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)); final ContainerNode nonLegacyContent = yangLib.newContentBuilder() .defaultContext(runtimeContext.getEffectiveModelContext()).formatYangLibraryContent();