--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.adapter;
+
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.tree.api.ModificationType.UNMODIFIED;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.VerifyException;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lazily translated {@link DataObjectModification} based on {@link DataTreeCandidateNode}.
+ * {@link AbstractDataObjectModification} represents Data tree change event, but whole tree is not translated or
+ * resolved eagerly, but only child nodes which are directly accessed by user of data object modification.
+ *
+ * <p>
+ * This class is further specialized as {@link LazyAugmentationModification} and {@link LazyDataObjectModification}, as
+ * both use different serialization methods.
+ *
+ * @param <T> Type of Binding {@link DataObject}
+ * @param <N> Type of underlying {@link CommonDataObjectCodecTreeNode}
+ */
+abstract sealed class AbstractDataObjectModification<T extends DataObject, N extends CommonDataObjectCodecTreeNode<T>>
+ implements DataObjectModification<T>
+ permits LazyAugmentationModification, LazyDataObjectModification {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractDataObjectModification.class);
+
+ final @NonNull DataTreeCandidateNode domData;
+ final @NonNull PathArgument identifier;
+ final @NonNull N codec;
+
+ private volatile ImmutableList<AbstractDataObjectModification<?, ?>> childNodesCache;
+ private volatile ModificationType modificationType;
+ private volatile @Nullable T dataBefore;
+ private volatile @Nullable T dataAfter;
+
+ AbstractDataObjectModification(final DataTreeCandidateNode domData, final N codec, final PathArgument identifier) {
+ this.domData = requireNonNull(domData);
+ this.identifier = requireNonNull(identifier);
+ this.codec = requireNonNull(codec);
+ }
+
+ static @Nullable AbstractDataObjectModification<?, ?> from(final CommonDataObjectCodecTreeNode<?> codec,
+ final @NonNull DataTreeCandidateNode current) {
+ if (codec instanceof BindingDataObjectCodecTreeNode<?> childDataObjectCodec) {
+ return new LazyDataObjectModification<>(childDataObjectCodec, current);
+ } else if (codec instanceof BindingAugmentationCodecTreeNode<?> childAugmentationCodec) {
+ return LazyAugmentationModification.forParent(childAugmentationCodec, current);
+ } else {
+ throw new VerifyException("Unhandled codec " + codec);
+ }
+ }
+
+ @Override
+ public final Class<T> getDataType() {
+ return codec.getBindingClass();
+ }
+
+ @Override
+ public final PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public final ModificationType getModificationType() {
+ var localType = modificationType;
+ if (localType != null) {
+ return localType;
+ }
+
+ final var domModificationType = domModificationType();
+ modificationType = localType = switch (domModificationType) {
+ case APPEARED, WRITE -> ModificationType.WRITE;
+ case DISAPPEARED, DELETE -> ModificationType.DELETE;
+ case SUBTREE_MODIFIED -> resolveSubtreeModificationType();
+ default ->
+ // TODO: Should we lie about modification type instead of exception?
+ throw new IllegalStateException("Unsupported DOM Modification type " + domModificationType);
+ };
+ return localType;
+ }
+
+ @Override
+ public final T getDataBefore() {
+ var local = dataBefore;
+ if (local == null) {
+ dataBefore = local = deserialize(domData.getDataBefore());
+ }
+ return local;
+ }
+
+ @Override
+ public final T getDataAfter() {
+ var local = dataAfter;
+ if (local == null) {
+ dataAfter = local = deserialize(domData.getDataAfter());
+ }
+ return local;
+ }
+
+ private @Nullable T deserialize(final Optional<NormalizedNode> normalized) {
+ return normalized.isEmpty() ? null : deserialize(normalized.orElseThrow());
+ }
+
+ abstract @Nullable T deserialize(@NonNull NormalizedNode normalized);
+
+ @Override
+ public final DataObjectModification<?> getModifiedChild(final PathArgument arg) {
+ final var domArgumentList = new ArrayList<YangInstanceIdentifier.PathArgument>();
+ final var childCodec = codec.bindingPathArgumentChild(arg, domArgumentList);
+ final var toEnter = domArgumentList.iterator();
+
+ // Careful now: we need to validated the first item against subclass
+ var current = toEnter.hasNext() ? firstModifiedChild(toEnter.next()) : domData;
+ // ... and for everything else we can just go wild
+ while (toEnter.hasNext() && current != null) {
+ current = current.getModifiedChild(toEnter.next()).orElse(null);
+ }
+
+ if (current == null || current.getModificationType() == UNMODIFIED) {
+ return null;
+ }
+ return from(childCodec, current);
+ }
+
+ abstract @Nullable DataTreeCandidateNode firstModifiedChild(YangInstanceIdentifier.PathArgument arg);
+
+ @Override
+ public final ImmutableList<AbstractDataObjectModification<?, ?>> getModifiedChildren() {
+ var local = childNodesCache;
+ if (local == null) {
+ childNodesCache = local = createModifiedChilden(codec, domData, domChildNodes());
+ }
+ return local;
+ }
+
+ @Override
+ public final <C extends ChildOf<? super T>> List<DataObjectModification<C>> getModifiedChildren(
+ final Class<C> childType) {
+ return streamModifiedChildren(childType).collect(Collectors.toList());
+ }
+
+ @Override
+ public final <H extends ChoiceIn<? super T> & DataObject, C extends ChildOf<? super H>>
+ List<DataObjectModification<C>> getModifiedChildren(final Class<H> caseType, final Class<C> childType) {
+ return streamModifiedChildren(childType)
+ .filter(child -> caseType.equals(child.identifier.getCaseType().orElse(null)))
+ .collect(Collectors.toList());
+ }
+
+ @SuppressWarnings("unchecked")
+ private <C extends DataObject> Stream<LazyDataObjectModification<C>> streamModifiedChildren(
+ final Class<C> childType) {
+ return getModifiedChildren().stream()
+ .filter(child -> childType.isAssignableFrom(child.getDataType()))
+ .map(child -> (LazyDataObjectModification<C>) child);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C>
+ getModifiedChildListItem(final Class<C> listItem, final K listKey) {
+ return (DataObjectModification<C>) getModifiedChild(IdentifiableItem.of(listItem, listKey));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <H extends ChoiceIn<? super T> & DataObject, C extends Identifiable<K> & ChildOf<? super H>,
+ K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(final Class<H> caseType,
+ final Class<C> listItem, final K listKey) {
+ return (DataObjectModification<C>) getModifiedChild(IdentifiableItem.of(caseType, listItem, listKey));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(
+ final Class<C> child) {
+ return (DataObjectModification<C>) getModifiedChild(Item.of(child));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <H extends ChoiceIn<? super T> & DataObject, C extends ChildOf<? super H>> DataObjectModification<C>
+ getModifiedChildContainer(final Class<H> caseType, final Class<C> child) {
+ return (DataObjectModification<C>) getModifiedChild(Item.of(caseType, child));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(
+ final Class<C> augmentation) {
+ return (DataObjectModification<C>) getModifiedChild(Item.of(augmentation));
+ }
+
+ @Override
+ public final String toString() {
+ return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+ }
+
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return helper.add("identifier", identifier).add("domData", domData);
+ }
+
+ abstract @NonNull Collection<DataTreeCandidateNode> domChildNodes();
+
+ abstract org.opendaylight.yangtools.yang.data.tree.api.@NonNull ModificationType domModificationType();
+
+ private @NonNull ModificationType resolveSubtreeModificationType() {
+ return switch (codec.getChildAddressabilitySummary()) {
+ case ADDRESSABLE ->
+ // All children are addressable, it is safe to report SUBTREE_MODIFIED
+ ModificationType.SUBTREE_MODIFIED;
+ case UNADDRESSABLE ->
+ // All children are non-addressable, report WRITE
+ ModificationType.WRITE;
+ case MIXED -> {
+ // This case is not completely trivial, as we may have NOT_ADDRESSABLE nodes underneath us. If that
+ // is the case, we need to turn this modification into a WRITE operation, so that the user is able
+ // to observe those nodes being introduced. This is not efficient, but unfortunately unavoidable,
+ // as we cannot accurately represent such changes.
+ for (DataTreeCandidateNode child : domChildNodes()) {
+ if (BindingStructuralType.recursiveFrom(child) == BindingStructuralType.NOT_ADDRESSABLE) {
+ // We have a non-addressable child, turn this modification into a write
+ yield ModificationType.WRITE;
+ }
+ }
+
+ // No unaddressable children found, proceed in addressed mode
+ yield ModificationType.SUBTREE_MODIFIED;
+ }
+ };
+ }
+
+ private static @NonNull ImmutableList<AbstractDataObjectModification<?, ?>> createModifiedChilden(
+ final CommonDataObjectCodecTreeNode<?> parentCodec, final DataTreeCandidateNode parent,
+ final Collection<DataTreeCandidateNode> children) {
+ final var result = ImmutableList.<AbstractDataObjectModification<?, ?>>builder();
+ populateList(result, parentCodec, parent, children);
+ return result.build();
+ }
+
+ private static void populateList(final ImmutableList.Builder<AbstractDataObjectModification<?, ?>> result,
+ final CommonDataObjectCodecTreeNode<?> parentCodec, final DataTreeCandidateNode parent,
+ final Collection<DataTreeCandidateNode> children) {
+ final var augmentChildren =
+ ArrayListMultimap.<BindingAugmentationCodecTreeNode<?>, DataTreeCandidateNode>create();
+
+ for (var domChildNode : parent.getChildNodes()) {
+ if (domChildNode.getModificationType() != UNMODIFIED) {
+ final var type = BindingStructuralType.from(domChildNode);
+ if (type != BindingStructuralType.NOT_ADDRESSABLE) {
+ /*
+ * Even if type is UNKNOWN, from perspective of BindingStructuralType we try to load codec for it.
+ * We will use that type to further specify debug log.
+ */
+ try {
+ final var childCodec = parentCodec.yangPathArgumentChild(domChildNode.getIdentifier());
+ if (childCodec instanceof BindingDataObjectCodecTreeNode<?> childDataObjectCodec) {
+ populateList(result, type, childDataObjectCodec, domChildNode);
+ } else if (childCodec instanceof BindingAugmentationCodecTreeNode<?> childAugmentationCodec) {
+ // Defer creation once we have collected all modified children
+ augmentChildren.put(childAugmentationCodec, domChildNode);
+ } else {
+ throw new VerifyException("Unhandled codec %s for type %s".formatted(childCodec, type));
+ }
+ } catch (final IllegalArgumentException e) {
+ if (type == BindingStructuralType.UNKNOWN) {
+ LOG.debug("Unable to deserialize unknown DOM node {}", domChildNode, e);
+ } else {
+ LOG.debug("Binding representation for DOM node {} was not found", domChildNode, e);
+ }
+ }
+ }
+ }
+ }
+
+ for (var entry : augmentChildren.asMap().entrySet()) {
+ final var modification = LazyAugmentationModification.forModifications(entry.getKey(), parent,
+ entry.getValue());
+ if (modification != null) {
+ result.add(modification);
+ }
+ }
+ }
+
+ private static void populateList(final ImmutableList.Builder<AbstractDataObjectModification<?, ?>> result,
+ final BindingStructuralType type, final BindingDataObjectCodecTreeNode<?> childCodec,
+ final DataTreeCandidateNode domChildNode) {
+ switch (type) {
+ case INVISIBLE_LIST:
+ // We use parent codec intentionally.
+ populateListWithSingleCodec(result, childCodec, domChildNode.getChildNodes());
+ break;
+ case INVISIBLE_CONTAINER:
+ populateList(result, childCodec, domChildNode, domChildNode.getChildNodes());
+ break;
+ case UNKNOWN:
+ case VISIBLE_CONTAINER:
+ result.add(new LazyDataObjectModification<>(childCodec, domChildNode));
+ break;
+ default:
+ }
+ }
+
+ private static void populateListWithSingleCodec(
+ final ImmutableList.Builder<AbstractDataObjectModification<?, ?>> result,
+ final BindingDataObjectCodecTreeNode<?> codec, final Collection<DataTreeCandidateNode> childNodes) {
+ for (var child : childNodes) {
+ if (child.getModificationType() != UNMODIFIED) {
+ result.add(new LazyDataObjectModification<>(codec, child));
+ }
+ }
+ }
+}
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;
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;
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
final InstanceIdentifier<D> path) {
checkArgument(!path.isWildcarded(), "Invalid read of wildcarded path %s", path);
- final CurrentAdapterSerializer codec = adapterContext.currentSerializer();
- final YangInstanceIdentifier domPath = codec.toYangInstanceIdentifier(path);
-
+ final var codecWithPath = adapterContext.currentSerializer().getSubtreeCodecWithPath(path);
+ final var domPath = codecWithPath.path();
+ final var codec = codecWithPath.codec();
return readOps.read(store, domPath)
- .transform(optData -> optData.map(domData -> (D) codec.fromNormalizedNode(domPath, domData).getValue()),
- MoreExecutors.directExecutor());
+ .transform(optData -> optData.flatMap(data -> decodeRead(codec, data)), MoreExecutors.directExecutor());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <D extends DataObject> Optional<D> decodeRead(final CommonDataObjectCodecTreeNode<D> codec,
+ final @NonNull NormalizedNode data) {
+ if (codec instanceof BindingDataObjectCodecTreeNode<?> dataObject) {
+ return Optional.of((D) dataObject.deserialize(data));
+ } else if (codec instanceof BindingAugmentationCodecTreeNode<?> augment) {
+ return Optional.ofNullable((D) augment.filterFrom(data));
+ } else {
+ throw new VerifyException("Unhandled codec " + codec);
+ }
}
protected final @NonNull FluentFuture<Boolean> doExists(final DOMDataTreeReadOperations readOps,
final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
checkArgument(!path.isWildcarded(), "Invalid exists of wildcarded path %s", path);
- return readOps.exists(store, adapterContext.currentSerializer().toYangInstanceIdentifier(path));
+
+ final var codecWithPath = adapterContext.currentSerializer().getSubtreeCodecWithPath(path);
+ final var domPath = codecWithPath.path();
+ if (codecWithPath.codec() instanceof BindingAugmentationCodecTreeNode<?> augment) {
+ // Complicated case: we need to check if any of the children exist, as DOM layer just does not know about
+ // this indirection
+ return FluentFuture.from(Futures.transform(
+ Futures.allAsList(augment.childPathArguments().stream()
+ .map(child -> readOps.exists(store, domPath.node(child)))
+ .collect(ImmutableList.toImmutableList())),
+ children -> children.contains(Boolean.TRUE),
+ MoreExecutors.directExecutor()));
+ } else {
+ return readOps.exists(store, domPath);
+ }
}
protected static final <T extends @NonNull DataObject> @NonNull FluentFuture<QueryResult<T>> doExecute(
final class BindingClusteredDOMDataTreeChangeListenerAdapter<T extends DataObject>
extends BindingDOMDataTreeChangeListenerAdapter<T> implements ClusteredDOMDataTreeChangeListener {
BindingClusteredDOMDataTreeChangeListenerAdapter(final AdapterContext codec,
- final ClusteredDataTreeChangeListener<T> listener, final LogicalDatastoreType store) {
- super(codec, listener, store);
+ final ClusteredDataTreeChangeListener<T> listener, final LogicalDatastoreType store,
+ final Class<T> augment) {
+ super(codec, listener, store, augment);
}
}
}
@Override
- public <T extends DataObject, L extends DataTreeChangeListener<T>> ListenerRegistration<L>
- registerDataTreeChangeListener(
- final DataTreeIdentifier<T> treeId, final L listener) {
+ public <T extends DataObject, L extends DataTreeChangeListener<T>>
+ ListenerRegistration<L> registerDataTreeChangeListener(final DataTreeIdentifier<T> treeId,
+ final L listener) {
if (treeChangeService == null) {
throw new UnsupportedOperationException("Underlying data broker does not expose DOMDataTreeChangeService.");
}
private final AdapterContext adapterContext;
private final DataTreeChangeListener<T> listener;
private final LogicalDatastoreType store;
+ private final Class<T> augment;
+
+ private boolean initialSyncDone;
BindingDOMDataTreeChangeListenerAdapter(final AdapterContext adapterContext,
- final DataTreeChangeListener<T> listener, final LogicalDatastoreType store) {
+ final DataTreeChangeListener<T> listener, final LogicalDatastoreType store, final Class<T> augment) {
this.adapterContext = requireNonNull(adapterContext);
this.listener = requireNonNull(listener);
this.store = requireNonNull(store);
+ this.augment = augment;
}
@Override
public void onDataTreeChanged(final List<DataTreeCandidate> domChanges) {
- listener.onDataTreeChanged(LazyDataTreeModification.from(adapterContext.currentSerializer(), domChanges,
- store));
+ final var changes = LazyDataTreeModification.<T>from(adapterContext.currentSerializer(), domChanges, store,
+ augment);
+ if (!changes.isEmpty()) {
+ listener.onDataTreeChanged(changes);
+ } else if (!initialSyncDone) {
+ onInitialData();
+ }
}
@Override
public void onInitialData() {
+ initialSyncDone = true;
listener.onInitialData();
}
}
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;
/**
registerDataTreeChangeListener(final DataTreeIdentifier<T> treeId, final L listener) {
final DOMDataTreeIdentifier domIdentifier = toDomTreeIdentifier(treeId);
final LogicalDatastoreType storeType = treeId.getDatastoreType();
+ final Class<T> target = treeId.getRootIdentifier().getTargetType();
+ final Class<T> augment = Augmentation.class.isAssignableFrom(target) ? target : null;
+
final BindingDOMDataTreeChangeListenerAdapter<T> domListener =
listener instanceof ClusteredDataTreeChangeListener
? new BindingClusteredDOMDataTreeChangeListenerAdapter<>(
- adapterContext(), (ClusteredDataTreeChangeListener<T>) listener, storeType)
- : new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), listener, storeType);
+ adapterContext(), (ClusteredDataTreeChangeListener<T>) listener, storeType, augment)
+ : new BindingDOMDataTreeChangeListenerAdapter<>(adapterContext(), listener, storeType, augment);
final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter<T>> domReg =
getDelegate().registerDataTreeChangeListener(domIdentifier, domListener);
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;
final class BindingDOMDataTreeCommitCohortAdapter<T extends DataObject>
extends AbstractBindingAdapter<DataTreeCommitCohort<T>> implements DOMDataTreeCommitCohort {
- BindingDOMDataTreeCommitCohortAdapter(final AdapterContext codec, final DataTreeCommitCohort<T> cohort) {
+ private final Class<T> augment;
+
+ BindingDOMDataTreeCommitCohortAdapter(final AdapterContext codec, final DataTreeCommitCohort<T> cohort,
+ final Class<T> augment) {
super(codec, cohort);
+ this.augment = augment;
}
@Override
public FluentFuture<PostCanCommitStep> canCommit(final Object txId,
final EffectiveModelContext ctx, final Collection<DOMDataTreeCandidate> candidates) {
- final Collection<DataTreeModification<T>> modifications = candidates.stream()
- .map(candidate -> LazyDataTreeModification.<T>create(currentSerializer(), candidate))
- .collect(Collectors.toList());
- return getDelegate().canCommit(txId, modifications);
+ return getDelegate().canCommit(txId, candidates.stream()
+ .map(candidate -> LazyDataTreeModification.<T>from(currentSerializer(), candidate, augment))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList()));
}
}
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
@Override
public <D extends DataObject, T extends DataTreeCommitCohort<D>> ObjectRegistration<T> registerCommitCohort(
final DataTreeIdentifier<D> subtree, final T cohort) {
- final BindingDOMDataTreeCommitCohortAdapter<D> adapter =
- new BindingDOMDataTreeCommitCohortAdapter<>(adapterContext(), cohort);
+ final Class<D> target = subtree.getRootIdentifier().getTargetType();
+
+ final BindingDOMDataTreeCommitCohortAdapter<D> adapter = new BindingDOMDataTreeCommitCohortAdapter<>(
+ adapterContext(), cohort, Augmentation.class.isAssignableFrom(target) ? target : null);
final DOMDataTreeIdentifier domPath = currentSerializer().toDOMDataTreeIdentifier(subtree);
final DOMDataTreeCommitCohortRegistration<?> domReg = getDelegate().registerCommitCohort(domPath, adapter);
return new ObjectRegistration<>() {
import static com.google.common.base.Preconditions.checkArgument;
+import com.google.common.base.VerifyException;
import com.google.common.util.concurrent.FluentFuture;
-import java.util.Map.Entry;
+import java.util.HashSet;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.AugmentationResult;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NormalizedResult;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
class BindingDOMWriteTransactionAdapter<T extends DOMDataTreeWriteTransaction> extends AbstractForwardedTransaction<T>
@Override
public final <U extends DataObject> void put(final LogicalDatastoreType store, final InstanceIdentifier<U> path,
final U data) {
- final Entry<YangInstanceIdentifier, NormalizedNode> normalized = toNormalized("put", path, data);
- getDelegate().put(store, normalized.getKey(), normalized.getValue());
+ put(store, toNormalized("put", path, data));
+ }
+
+ private void put(final LogicalDatastoreType store, final NormalizedResult normalized) {
+ final var delegate = getDelegate();
+ final var domPath = normalized.path();
+
+ if (normalized instanceof AugmentationResult augment) {
+ // Augmentation: put() child nodes provided with augmentation, delete() those having no data
+ final var putIds = new HashSet<YangInstanceIdentifier.PathArgument>();
+ for (var child : augment.children()) {
+ final var childId = child.getIdentifier();
+ delegate.put(store, domPath.node(childId), child);
+ putIds.add(childId);
+ }
+ for (var childId : augment.possibleChildren()) {
+ if (!putIds.contains(childId)) {
+ delegate.delete(store, domPath.node(childId));
+ }
+ }
+ } else if (normalized instanceof NodeResult node) {
+ delegate.put(store, domPath, node.node());
+ } else {
+ throw new VerifyException("Unhandled result " + normalized);
+ }
}
@Deprecated
@Override
public final <U extends DataObject> void mergeParentStructurePut(final LogicalDatastoreType store,
final InstanceIdentifier<U> path, final U data) {
- final CurrentAdapterSerializer serializer = adapterContext().currentSerializer();
- final Entry<YangInstanceIdentifier, NormalizedNode> normalized = toNormalized(serializer, "put", path, data);
- ensureParentsByMerge(serializer, store, normalized.getKey(), path);
- getDelegate().put(store, normalized.getKey(), normalized.getValue());
+ final var serializer = adapterContext().currentSerializer();
+ final var normalized = toNormalized(serializer, "put", path, data);
+ ensureParentsByMerge(serializer, store, normalized.path());
+ put(store, normalized);
}
@Override
public final <D extends DataObject> void merge(final LogicalDatastoreType store, final InstanceIdentifier<D> path,
final D data) {
- final Entry<YangInstanceIdentifier, NormalizedNode> normalized = toNormalized("merge", path, data);
- getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+ merge(store, toNormalized("merge", path, data));
+ }
+
+ private void merge(final LogicalDatastoreType store, final NormalizedResult normalized) {
+ final var delegate = getDelegate();
+ final var domPath = normalized.path();
+
+ if (normalized instanceof AugmentationResult augment) {
+ // Augmentation: merge individual children
+ for (var child : augment.children()) {
+ delegate.merge(store, domPath.node(child.getIdentifier()), child);
+ }
+ } else if (normalized instanceof NodeResult node) {
+ delegate.merge(store, domPath, node.node());
+ } else {
+ throw new VerifyException("Unhandled result " + normalized);
+ }
}
@Deprecated
@Override
public final <U extends DataObject> void mergeParentStructureMerge(final LogicalDatastoreType store,
final InstanceIdentifier<U> path, final U data) {
- final CurrentAdapterSerializer serializer = adapterContext().currentSerializer();
- final Entry<YangInstanceIdentifier, NormalizedNode> normalized = toNormalized(serializer, "merge", path, data);
- ensureParentsByMerge(serializer, store, normalized.getKey(), path);
- getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+ final var serializer = adapterContext().currentSerializer();
+ final var normalized = toNormalized(serializer, "merge", path, data);
+ ensureParentsByMerge(serializer, store, normalized.path());
+ merge(store, normalized);
}
@Override
public final void delete(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
checkArgument(!path.isWildcarded(), "Cannot delete wildcarded path %s", path);
- getDelegate().delete(store, adapterContext().currentSerializer().toYangInstanceIdentifier(path));
+ final var serializer = adapterContext().currentSerializer();
+
+ // Lookup the codec and the corresponding path
+ final var codecWithPath = serializer.getSubtreeCodecWithPath(path);
+ final var domPath = codecWithPath.path();
+ final var delegate = getDelegate();
+ if (codecWithPath.codec() instanceof BindingAugmentationCodecTreeNode<?> augmentCodec) {
+ // Deletion of an augmentation: issue a delete on all potential children of the augmentation
+ for (var childPath : augmentCodec.childPathArguments()) {
+ delegate.delete(store, domPath.node(childPath));
+ }
+ } else {
+ delegate.delete(store, domPath);
+ }
}
@Override
*
* @param store an instance of LogicalDatastoreType
* @param domPath an instance of YangInstanceIdentifier
- * @param path an instance of InstanceIdentifier
*/
private void ensureParentsByMerge(final CurrentAdapterSerializer serializer, final LogicalDatastoreType store,
- final YangInstanceIdentifier domPath, final InstanceIdentifier<?> path) {
- final YangInstanceIdentifier parentPath = domPath.getParent();
+ final YangInstanceIdentifier domPath) {
+ final var parentPath = domPath.getParent();
if (parentPath != null && !parentPath.isEmpty()) {
- final NormalizedNode parentNode = ImmutableNodes.fromInstanceId(
+ final var parentNode = ImmutableNodes.fromInstanceId(
serializer.getRuntimeContext().getEffectiveModelContext(), parentPath);
getDelegate().merge(store, YangInstanceIdentifier.create(parentNode.getIdentifier()), parentNode);
}
}
- private <U extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode> toNormalized(final String operation,
+ private <U extends DataObject> @NonNull NormalizedResult toNormalized(final String operation,
final InstanceIdentifier<U> path, final U data) {
return toNormalized(adapterContext().currentSerializer(), operation, path, data);
}
- private static <U extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode> toNormalized(
+ private static <U extends DataObject> @NonNull NormalizedResult toNormalized(
final CurrentAdapterSerializer serializer, final String operation, final InstanceIdentifier<U> path,
final U data) {
checkArgument(!path.isWildcarded(), "Cannot %s data into wildcarded path %s", operation, path);
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;
}
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) {
}
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) {
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;
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();
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.adapter;
+
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.data.tree.api.ModificationType.UNMODIFIED;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
+
+/**
+ * Specialization of {@link AbstractDataObjectModification} for {@link Augmentation}s. Is based on a parent
+ * {@link DataTreeCandidateNode}, but contains only a subset of its modifications.
+ */
+final class LazyAugmentationModification<A extends Augmentation<?>>
+ extends AbstractDataObjectModification<A, BindingAugmentationCodecTreeNode<A>> {
+ private final @NonNull ImmutableList<DataTreeCandidateNode> domChildNodes;
+
+ private LazyAugmentationModification(final BindingAugmentationCodecTreeNode<A> codec,
+ final DataTreeCandidateNode parent, final ImmutableList<DataTreeCandidateNode> domChildNodes) {
+ super(parent, codec, codec.deserializePathArgument(null));
+ this.domChildNodes = requireNonNull(domChildNodes);
+ }
+
+ static <A extends Augmentation<?>> @Nullable LazyAugmentationModification<A> forModifications(
+ final BindingAugmentationCodecTreeNode<A> codec, final DataTreeCandidateNode parent,
+ final Collection<DataTreeCandidateNode> children) {
+ // Filter out any unmodified children first
+ final var domChildren = children.stream()
+ .filter(childMod -> childMod.getModificationType() != UNMODIFIED)
+ .collect(ImmutableList.toImmutableList());
+ // Only return a modification if there is something left
+ return domChildren.isEmpty() ? null : new LazyAugmentationModification<>(codec, parent, domChildren);
+ }
+
+ static <A extends Augmentation<?>> @Nullable LazyAugmentationModification<A> forParent(
+ final BindingAugmentationCodecTreeNode<A> codec, final DataTreeCandidateNode parent) {
+ final var builder = ImmutableList.<DataTreeCandidateNode>builder();
+ for (var pathArg : codec.childPathArguments()) {
+ parent.getModifiedChild(pathArg).ifPresent(builder::add);
+ }
+ final var domChildren = builder.build();
+ return domChildren.isEmpty() ? null : new LazyAugmentationModification<>(codec, parent, domChildren);
+ }
+
+ @Override
+ ImmutableList<DataTreeCandidateNode> domChildNodes() {
+ return domChildNodes;
+ }
+
+ @Override
+ org.opendaylight.yangtools.yang.data.tree.api.ModificationType domModificationType() {
+ final var before = getDataBefore();
+ final var after = getDataAfter();
+ if (before == null) {
+ return after == null ? org.opendaylight.yangtools.yang.data.tree.api.ModificationType.UNMODIFIED
+ : org.opendaylight.yangtools.yang.data.tree.api.ModificationType.APPEARED;
+ }
+ return after == null ? org.opendaylight.yangtools.yang.data.tree.api.ModificationType.DISAPPEARED
+ : org.opendaylight.yangtools.yang.data.tree.api.ModificationType.SUBTREE_MODIFIED;
+ }
+
+
+ @Override
+ A deserialize(final NormalizedNode normalized) {
+ return codec.filterFrom(normalized);
+ }
+
+ @Override
+ DataTreeCandidateNode firstModifiedChild(final PathArgument arg) {
+ // Not entirely efficient linear search, but otherwise we'd have to index, which is even slower
+ return domChildNodes.stream()
+ .filter(child -> arg.equals(child.getIdentifier()))
+ .findFirst()
+ .orElse(null);
+ }
+
+ @Override
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return super.addToStringAttributes(helper)
+ .add("domType", domData.getModificationType())
+ .add("domChildren", domChildNodes);
+ }
+}
*/
package org.opendaylight.mdsal.binding.dom.adapter;
-import static com.google.common.base.Verify.verify;
-import static java.util.Objects.requireNonNull;
-import static org.opendaylight.yangtools.yang.data.tree.api.ModificationType.UNMODIFIED;
-
-import com.google.common.base.MoreObjects;
-import java.util.ArrayList;
+import com.google.common.base.MoreObjects.ToStringHelper;
import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.api.DataObjectModification;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
-import org.opendaylight.yangtools.yang.binding.ChoiceIn;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.Identifiable;
-import org.opendaylight.yangtools.yang.binding.Identifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Lazily translated {@link DataObjectModification} based on {@link DataTreeCandidateNode}.
*
* <p>
- * {@link LazyDataObjectModification} represents Data tree change event,
- * but whole tree is not translated or resolved eagerly, but only child nodes
- * which are directly accessed by user of data object modification.
+ * {@link LazyDataObjectModification} represents Data tree change event, but whole tree is not translated or resolved
+ * eagerly, but only child nodes which are directly accessed by user of data object modification.
*
* @param <T> Type of Binding Data Object
*/
-final class LazyDataObjectModification<T extends DataObject> implements DataObjectModification<T> {
- private static final Logger LOG = LoggerFactory.getLogger(LazyDataObjectModification.class);
-
- private final BindingDataObjectCodecTreeNode<T> codec;
- private final DataTreeCandidateNode domData;
- private final PathArgument identifier;
-
- private volatile List<LazyDataObjectModification<?>> childNodesCache;
- private volatile ModificationType modificationType;
-
- private LazyDataObjectModification(final BindingDataObjectCodecTreeNode<T> codec,
- final DataTreeCandidateNode domData) {
- this.codec = requireNonNull(codec);
- this.domData = requireNonNull(domData);
- identifier = codec.deserializePathArgument(domData.getIdentifier());
- }
-
- static <T extends DataObject> LazyDataObjectModification<T> create(final BindingDataObjectCodecTreeNode<T> codec,
- final DataTreeCandidateNode domData) {
- return new LazyDataObjectModification<>(codec, domData);
- }
-
- private static List<LazyDataObjectModification<?>> from(final BindingDataObjectCodecTreeNode<?> parentCodec,
- final Collection<DataTreeCandidateNode> domChildNodes) {
- final var result = new ArrayList<LazyDataObjectModification<?>>(domChildNodes.size());
- populateList(result, parentCodec, domChildNodes);
- return result;
- }
-
- private static void populateList(final List<LazyDataObjectModification<?>> result,
- final BindingDataObjectCodecTreeNode<?> parentCodec,
- final Collection<DataTreeCandidateNode> domChildNodes) {
- for (var domChildNode : domChildNodes) {
- if (domChildNode.getModificationType() != UNMODIFIED) {
- final var type = BindingStructuralType.from(domChildNode);
- if (type != BindingStructuralType.NOT_ADDRESSABLE) {
- /*
- * Even if type is UNKNOWN, from perspective of BindingStructuralType we try to load codec for it.
- * We will use that type to further specify debug log.
- */
- try {
- final var childCodec = parentCodec.yangPathArgumentChild(domChildNode.getIdentifier());
- // FIXME: MDSAL-820: this no longer holds
- verify(childCodec instanceof BindingDataObjectCodecTreeNode, "Unhandled codec %s for type %s",
- childCodec, type);
- populateList(result, type, (BindingDataObjectCodecTreeNode<?>) childCodec, domChildNode);
- } catch (final IllegalArgumentException e) {
- if (type == BindingStructuralType.UNKNOWN) {
- LOG.debug("Unable to deserialize unknown DOM node {}", domChildNode, e);
- } else {
- LOG.debug("Binding representation for DOM node {} was not found", domChildNode, e);
- }
- }
- }
- }
- }
- }
-
- private static void populateList(final List<LazyDataObjectModification<? extends DataObject>> result,
- final BindingStructuralType type, final BindingDataObjectCodecTreeNode<?> childCodec,
- final DataTreeCandidateNode domChildNode) {
- switch (type) {
- case INVISIBLE_LIST:
- // We use parent codec intentionally.
- populateListWithSingleCodec(result, childCodec, domChildNode.getChildNodes());
- break;
- case INVISIBLE_CONTAINER:
- populateList(result, childCodec, domChildNode.getChildNodes());
- break;
- case UNKNOWN:
- case VISIBLE_CONTAINER:
- result.add(create(childCodec, domChildNode));
- break;
- default:
- }
- }
-
- private static void populateListWithSingleCodec(final List<LazyDataObjectModification<? extends DataObject>> result,
- final BindingDataObjectCodecTreeNode<?> codec, final Collection<DataTreeCandidateNode> childNodes) {
- for (var child : childNodes) {
- if (child.getModificationType() != UNMODIFIED) {
- result.add(create(codec, child));
- }
- }
- }
-
- @Override
- public T getDataBefore() {
- return deserialize(domData.getDataBefore());
- }
-
- @Override
- public T getDataAfter() {
- return deserialize(domData.getDataAfter());
- }
-
- @Override
- public Class<T> getDataType() {
- return codec.getBindingClass();
- }
-
- @Override
- public PathArgument getIdentifier() {
- return identifier;
+final class LazyDataObjectModification<T extends DataObject>
+ extends AbstractDataObjectModification<T, BindingDataObjectCodecTreeNode<T>> {
+ LazyDataObjectModification(final BindingDataObjectCodecTreeNode<T> codec, final DataTreeCandidateNode domData) {
+ super(domData, codec, codec.deserializePathArgument(domData.getIdentifier()));
}
@Override
- public ModificationType getModificationType() {
- var localType = modificationType;
- if (localType != null) {
- return localType;
- }
-
- modificationType = localType = switch (domData.getModificationType()) {
- case APPEARED, WRITE -> ModificationType.WRITE;
- case DISAPPEARED, DELETE -> ModificationType.DELETE;
- case SUBTREE_MODIFIED -> resolveSubtreeModificationType();
- default ->
- // TODO: Should we lie about modification type instead of exception?
- throw new IllegalStateException("Unsupported DOM Modification type " + domData.getModificationType());
- };
- return localType;
- }
-
- private @NonNull ModificationType resolveSubtreeModificationType() {
- return switch (codec.getChildAddressabilitySummary()) {
- case ADDRESSABLE ->
- // All children are addressable, it is safe to report SUBTREE_MODIFIED
- ModificationType.SUBTREE_MODIFIED;
- case UNADDRESSABLE ->
- // All children are non-addressable, report WRITE
- ModificationType.WRITE;
- case MIXED -> {
- // This case is not completely trivial, as we may have NOT_ADDRESSABLE nodes underneath us. If that
- // is the case, we need to turn this modification into a WRITE operation, so that the user is able
- // to observe those nodes being introduced. This is not efficient, but unfortunately unavoidable,
- // as we cannot accurately represent such changes.
- for (DataTreeCandidateNode child : domData.getChildNodes()) {
- if (BindingStructuralType.recursiveFrom(child) == BindingStructuralType.NOT_ADDRESSABLE) {
- // We have a non-addressable child, turn this modification into a write
- yield ModificationType.WRITE;
- }
- }
-
- // No unaddressable children found, proceed in addressed mode
- yield ModificationType.SUBTREE_MODIFIED;
- }
- };
- }
-
- @Override
- public List<LazyDataObjectModification<?>> getModifiedChildren() {
- var local = childNodesCache;
- if (local == null) {
- childNodesCache = local = from(codec, domData.getChildNodes());
- }
- return local;
- }
-
- @Override
- public <C extends ChildOf<? super T>> List<DataObjectModification<C>> getModifiedChildren(
- final Class<C> childType) {
- return streamModifiedChildren(childType).collect(Collectors.toList());
- }
-
- @Override
- public <H extends ChoiceIn<? super T> & DataObject, C extends ChildOf<? super H>>
- List<DataObjectModification<C>> getModifiedChildren(final Class<H> caseType, final Class<C> childType) {
- return streamModifiedChildren(childType)
- .filter(child -> caseType.equals(child.identifier.getCaseType().orElse(null)))
- .collect(Collectors.toList());
- }
-
- @SuppressWarnings("unchecked")
- private <C extends DataObject> Stream<LazyDataObjectModification<C>> streamModifiedChildren(
- final Class<C> childType) {
- return getModifiedChildren().stream()
- .filter(child -> childType.isAssignableFrom(child.getDataType()))
- .map(child -> (LazyDataObjectModification<C>) child);
+ Collection<DataTreeCandidateNode> domChildNodes() {
+ return domData.getChildNodes();
}
@Override
- public DataObjectModification<?> getModifiedChild(final PathArgument arg) {
- final var domArgumentList = new ArrayList<YangInstanceIdentifier.PathArgument>();
- final var childCodec = codec.bindingPathArgumentChild(arg, domArgumentList);
- final var toEnter = domArgumentList.iterator();
- var current = domData;
- while (toEnter.hasNext() && current != null) {
- current = current.getModifiedChild(toEnter.next()).orElse(null);
- }
- return current != null && current.getModificationType() != UNMODIFIED ? create(childCodec, current) : null;
+ org.opendaylight.yangtools.yang.data.tree.api.ModificationType domModificationType() {
+ return domData.getModificationType();
}
@Override
- @SuppressWarnings("unchecked")
- public <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C>
- getModifiedChildListItem(final Class<C> listItem, final K listKey) {
- return (DataObjectModification<C>) getModifiedChild(IdentifiableItem.of(listItem, listKey));
+ T deserialize(final NormalizedNode normalized) {
+ return codec.deserialize(normalized);
}
@Override
- @SuppressWarnings("unchecked")
- public <H extends ChoiceIn<? super T> & DataObject, C extends Identifiable<K> & ChildOf<? super H>,
- K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(final Class<H> caseType,
- final Class<C> listItem, final K listKey) {
- return (DataObjectModification<C>) getModifiedChild(IdentifiableItem.of(caseType, listItem, listKey));
+ DataTreeCandidateNode firstModifiedChild(final PathArgument arg) {
+ return domData.getModifiedChild(arg).orElse(null);
}
@Override
- @SuppressWarnings("unchecked")
- public <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(final Class<C> child) {
- return (DataObjectModification<C>) getModifiedChild(Item.of(child));
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public <H extends ChoiceIn<? super T> & DataObject, C extends ChildOf<? super H>> DataObjectModification<C>
- getModifiedChildContainer(final Class<H> caseType, final Class<C> child) {
- return (DataObjectModification<C>) getModifiedChild(Item.of(caseType, child));
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(
- final Class<C> augmentation) {
- return (DataObjectModification<C>) getModifiedChild(Item.of(augmentation));
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this).add("identifier", identifier).add("domData", domData).toString();
- }
-
- private T deserialize(final Optional<NormalizedNode> dataAfter) {
- return dataAfter.map(codec::deserialize).orElse(null);
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return super.addToStringAttributes(helper).add("domData", domData);
}
}
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;
/**
private LazyDataTreeModification(final DataTreeIdentifier<T> path, final DataObjectModification<T> modification) {
this.path = requireNonNull(path);
- this.rootNode = requireNonNull(modification);
+ rootNode = requireNonNull(modification);
}
@SuppressWarnings({"unchecked", "rawtypes"})
- static <T extends DataObject> DataTreeModification<T> create(final CurrentAdapterSerializer serializer,
- final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType) {
- final InstanceIdentifier<?> bindingPath = serializer.coerceInstanceIdentifier(domChange.getRootPath());
- final BindingDataObjectCodecTreeNode<?> codec = serializer.getSubtreeCodec(bindingPath);
- final DataTreeIdentifier<?> path = DataTreeIdentifier.create(datastoreType, bindingPath);
- return new LazyDataTreeModification(path, LazyDataObjectModification.create(codec, domChange.getRootNode()));
+ static <T extends DataObject> @Nullable DataTreeModification<T> from(final CurrentAdapterSerializer serializer,
+ final DataTreeCandidate domChange, final LogicalDatastoreType datastoreType, final Class<T> augment) {
+ final var bindingPath = createBindingPath(serializer, domChange.getRootPath(), augment);
+ final var codec = serializer.getSubtreeCodec(bindingPath);
+ final var modification = LazyDataObjectModification.from(codec, domChange.getRootNode());
+ return modification == null ? null
+ : new LazyDataTreeModification(DataTreeIdentifier.create(datastoreType, bindingPath), modification);
}
@SuppressWarnings({"unchecked", "rawtypes"})
- static <T extends DataObject> DataTreeModification<T> create(final CurrentAdapterSerializer serializer,
- final DOMDataTreeCandidate candidate) {
- final DOMDataTreeIdentifier domRootPath = candidate.getRootPath();
- final InstanceIdentifier<?> bindingPath = serializer.coerceInstanceIdentifier(domRootPath.getRootIdentifier());
- final BindingDataObjectCodecTreeNode<?> codec = serializer.getSubtreeCodec(bindingPath);
- return new LazyDataTreeModification(DataTreeIdentifier.create(domRootPath.getDatastoreType(), bindingPath),
- LazyDataObjectModification.create(codec, candidate.getRootNode()));
+ static <T extends DataObject> @Nullable DataTreeModification<T> from(final CurrentAdapterSerializer serializer,
+ final DOMDataTreeCandidate candidate, final Class<T> augment) {
+ final var domRootPath = candidate.getRootPath();
+ final var bindingPath = createBindingPath(serializer, domRootPath.getRootIdentifier(), augment);
+ final var codec = serializer.getSubtreeCodec(bindingPath);
+ final var modification = LazyDataObjectModification.from(codec, candidate.getRootNode());
+ return modification == null ? null
+ : new LazyDataTreeModification(DataTreeIdentifier.create(domRootPath.getDatastoreType(), bindingPath),
+ modification);
}
static <T extends DataObject> @NonNull List<DataTreeModification<T>> from(final CurrentAdapterSerializer codec,
- final List<DataTreeCandidate> domChanges, final LogicalDatastoreType datastoreType) {
- final List<DataTreeModification<T>> result = new ArrayList<>(domChanges.size());
- for (final DataTreeCandidate domChange : domChanges) {
- result.add(LazyDataTreeModification.create(codec, domChange, datastoreType));
+ final List<DataTreeCandidate> domChanges, final LogicalDatastoreType datastoreType,
+ final Class<T> augment) {
+ final var result = new ArrayList<DataTreeModification<T>>(domChanges.size());
+ for (var domChange : domChanges) {
+ final var bindingChange = from(codec, domChange, datastoreType, augment);
+ if (bindingChange != null) {
+ result.add(bindingChange);
+ }
}
return result;
}
+ // We are given a DOM path, which does not reflect augmentations, as those are not representable in NormalizedNode
+ // world. This method takes care of reconstructing the InstanceIdentifier, appending the missing Augmentation. This
+ // important to get the correct codec into the mix -- otherwise we would be operating on the parent container's
+ // codec and mis-report what is actually going on.
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static @NonNull InstanceIdentifier<?> createBindingPath(final CurrentAdapterSerializer serializer,
+ final YangInstanceIdentifier domPath, final Class<?> augment) {
+ final var bindingPath = serializer.coerceInstanceIdentifier(domPath);
+ return augment == null ? bindingPath : bindingPath.augmentation((Class) augment.asSubclass(Augmentation.class));
+ }
+
@Override
public DataObjectModification<T> getRootNode() {
return rootNode;
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;
private YangInstanceIdentifier relativeSelect;
QueryBuilderState(final DefaultQueryFactory factory, final InstanceIdentifier<?> root) {
- this.codec = factory.codec();
+ codec = factory.codec();
this.factory = factory;
this.root = fromBinding(root);
}
@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();
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();
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;
public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest {
- private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
- private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
- .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).build();
- private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
- .augmentation(TreeLeafOnlyAugment.class);
- private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
- .augmentation(TreeComplexUsesAugment.class);
- private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
- private static final QName NAME_QNAME = QName.create(Top.QNAME, "name");
- private static final YangInstanceIdentifier BI_TOP_LEVEL_LIST = YangInstanceIdentifier.builder().node(Top.QNAME)
- .node(TopLevelList.QNAME).nodeWithKey(TopLevelList.QNAME, NAME_QNAME, TOP_FOO_KEY.getName()).build();
-
private CurrentAdapterSerializer serializer;
@Override
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)
final TestListener<TreeComplexUsesAugment> listener = createListener(CONFIGURATION, COMPLEX_AUGMENT,
added(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugmentBefore),
- replaced(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugmentBefore,
+ // While we are overwriting the augment, at the transaction ends up replacing one child with another,
+ // so the Augmentation ends up not being overwritten, but modified
+ subtreeModified(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugmentBefore,
complexUsesAugmentAfter));
writeTx = getDataBroker().newWriteOnlyTransaction();
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
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.codec.api;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+@Beta
+public interface BindingAugmentationCodecTreeNode<T extends Augmentation<?>>
+ extends CommonDataObjectCodecTreeNode<T> {
+ /**
+ * Return an {@link Augmentation} instance backed by specified parent data. Implementations are free to return
+ * either an eager transformation (not retaining {@code parentData}), or a lazy proxy. In both cases they must
+ * ensure the result has at least one property populated (i.e. is not empty).
+ *
+ * @return An Augmentation, or {@code null} if the augmentation would be empty
+ * @throws NullPointerException if {@code parentData} is {@code null}
+ * @throws IllegalArgumentException if {@code parentData} is not a compatible parent
+ */
+ @Nullable T filterFrom(@NonNull DataContainerNode parentData);
+
+ /**
+ * Return an {@link Augmentation} instance backed by specified parent data. Implementations are free to return
+ * either an eager transformation (not retaining {@code parentData}), or a lazy proxy. In both cases they must
+ * ensure the result has at least one property populated (i.e. is not empty).
+ *
+ * @return An Augmentation, or {@code null} if the augmentation would be empty
+ * @throws NullPointerException if {@code parentData} is {@code null}
+ * @throws IllegalArgumentException if {@code parentData} is not a compatible {@link DataContainerNode}
+ */
+ default @Nullable T filterFrom(final @NonNull NormalizedNode parentData) {
+ if (requireNonNull(parentData) instanceof DataContainerNode parentContainer) {
+ return filterFrom(parentContainer);
+ }
+ throw new IllegalArgumentException("Unsupported parent " + parentData.contract());
+ }
+
+ /**
+ * Write the contents of an {@link Augmentation} into a writer. The writer must beinitialized at the
+ * augmentations's {@link Augmentable} parent's equivalent {@link DataContainerNode}.
+ *
+ * @param writer Writer to stream to
+ * @param data Data to stream
+ * @throws NullPointerException if any argument is {@code null}
+ * @throws IOException if a streaming error occurs
+ */
+ void streamTo(@NonNull NormalizedNodeStreamWriter writer, @NonNull T data) throws IOException;
+
+ /**
+ * Returns the {@link PathArgument}s of items contained in this {@link Augmentation}.
+ *
+ * @return A non-empty set of path arguments
+ */
+ @NonNull ImmutableSet<PathArgument> childPathArguments();
+
+ /**
+ * Returns the {@link Class}es of items contain in this {@link Augmentation}.
+ *
+ * @return A non-empty set of classes
+ */
+ @NonNull ImmutableSet<Class<?>> childBindingClasses();
+}
*/
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;
*/
// TODO: Add more detailed documentation
public interface BindingCodecTree extends BindingDataObjectCodecTreeParent<Empty> {
+ /**
+ * A DTO holding a {@link CommonDataObjectCodecTreeNode} and the corresponding {@link YangInstanceIdentifier}.
+ *
+ * @param <T> {@link DataObject} type
+ */
+ record CodecWithPath<T extends DataObject>(
+ @NonNull CommonDataObjectCodecTreeNode<T> codec,
+ @NonNull YangInstanceIdentifier path) {
+ public CodecWithPath {
+ requireNonNull(codec);
+ requireNonNull(path);
+ }
+ }
+
+ /**
+ * Look up the codec for specified path, constructing the {@link YangInstanceIdentifier} corresponding to it.
+ *
+ * @param <T> DataObject type
+ * @param path Binding path
+ * @return A {@link CodecWithPath}
+ * @throws NullPointerException if {@code path} is {@code null}
+ * @throws IllegalArgumentException if the codec cannot be resolved
+ */
+ <T extends DataObject> @NonNull CodecWithPath<T> getSubtreeCodecWithPath(InstanceIdentifier<T> path);
+
/**
* Look up the codec for specified path.
*
* @throws NullPointerException if {@code path} is {@code null}
* @throws IllegalArgumentException if the codec cannot be resolved
*/
- <T extends DataObject> @NonNull BindingDataObjectCodecTreeNode<T> getSubtreeCodec(InstanceIdentifier<T> path);
+ <T extends DataObject> @NonNull CommonDataObjectCodecTreeNode<T> getSubtreeCodec(InstanceIdentifier<T> path);
+ // FIXME: NonNull and throwing exception
@Nullable BindingCodecTreeNode getSubtreeCodec(YangInstanceIdentifier path);
+ // FIXME: NonNull and throwing exception
@Nullable BindingCodecTreeNode getSubtreeCodec(Absolute path);
/**
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableCollection;
-import java.util.List;
-import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.binding.BindingObject;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.Empty;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@Beta
public interface BindingDataObjectCodecTreeNode<T extends DataObject>
- extends BindingDataObjectCodecTreeParent<Empty>, BindingObjectCodecTreeNode<T>, BindingNormalizedNodeCodec<T> {
-
- /**
- * Returns binding class of interface which represents API of current schema node. The result is same as invoking
- * {@link DataObject#implementedInterface()} on instance of data.
- *
- * @return interface which defines API of binding representation of data.
- */
- @Override
- @NonNull Class<T> getBindingClass();
-
- /**
- * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case,
- * one must issue getChild(ChoiceClass).getChild(CaseClass).
- *
- * <p>
- * This method differs from {@link #streamChild(Class)}, that is less strict for interfaces representing
- * augmentation and cases, that may return {@link BindingCodecTreeNode} even if augmentation interface containing
- * same data was supplied and does not represent augmentation of this node.
- *
- * @param childClass Child class by Binding Stream navigation
- * @return Context of child or Optional.empty is supplied class is not
- * applicable in context.
- */
- <E extends DataObject> Optional<? extends BindingDataObjectCodecTreeNode<E>> possibleStreamChild(
- @NonNull Class<E> childClass);
-
- /**
- * Returns nested node context using supplied YANG Instance Identifier.
- *
- * @param child
- * Yang Instance Identifier Argument
- * @return Context of child
- * @throws IllegalArgumentException
- * If supplied argument does not represent valid child.
- */
- @NonNull BindingCodecTreeNode yangPathArgumentChild(YangInstanceIdentifier.@NonNull PathArgument child);
-
- /**
- * Returns nested node context using supplied Binding Instance Identifier and adds YANG instance identifiers to
- * the supplied list.
- *
- * @param arg
- * Binding Instance Identifier Argument
- * @param builder
- * Mutable instance of list, which is appended by YangInstanceIdentifiers
- * as tree is walked. Use null if such side-product is not needed.
- * @return Context of child
- * @throws IllegalArgumentException
- * If supplied argument does not represent valid child.
- */
- @NonNull BindingDataObjectCodecTreeNode<?> bindingPathArgumentChild(InstanceIdentifier.@NonNull PathArgument arg,
- @Nullable List<YangInstanceIdentifier.PathArgument> builder);
-
- /**
- * Serializes path argument for current node.
- *
- * @param arg Binding Path Argument, may be null if Binding Instance Identifier does not have
- * representation for current node (e.g. choice or case).
- * @return Yang Path Argument, may be null if Yang Instance Identifier does not have
- * representation for current node (e.g. case).
- * @throws IllegalArgumentException If supplied {@code arg} is not valid.
- */
- @Beta
- YangInstanceIdentifier.@Nullable PathArgument serializePathArgument(InstanceIdentifier.@Nullable PathArgument arg);
-
- /**
- * Deserializes path argument for current node.
- *
- * @param arg Yang Path Argument, may be null if Yang Instance Identifier does not have
- * representation for current node (e.g. case).
- * @return Binding Path Argument, may be null if Binding Instance Identifier does not have
- * representation for current node (e.g. choice or case).
- * @throws IllegalArgumentException If supplied {@code arg} is not valid.
- */
- @Beta
- InstanceIdentifier.@Nullable PathArgument deserializePathArgument(
- YangInstanceIdentifier.@Nullable PathArgument arg);
-
-
- /**
- * Return a summary of addressability of potential children. Binding specification does not allow all DOM tree
- * elements to be directly addressed, which means some recursive tree operations, like data tree changes do not
- * have a one-to-one mapping from DOM to binding in all cases. This method provides an optimization hint to guide
- * translation of data structures, allowing for fast paths when all children are known to either be addressable
- * or non-addressable.
- *
- * @return Summary children addressability.
- */
- @NonNull ChildAddressabilitySummary getChildAddressabilitySummary();
-
+ extends CommonDataObjectCodecTreeNode<T>, BindingNormalizedNodeCodec<T> {
/**
* Returns codec which uses caches serialization / deserialization results.
*
*/
@NonNull BindingNormalizedNodeCachingCodec<T> createCachingCodec(
@NonNull ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier);
-
- /**
- * Enumeration of possible addressability attribute of all children.
- */
- enum ChildAddressabilitySummary {
- /**
- * All children are addressable.
- */
- ADDRESSABLE,
- /**
- * All children are non-addressable, including the case when this node does not have any children.
- */
- UNADDRESSABLE,
- /**
- * Mixed children, some are addressable and some are not.
- */
- MIXED
- }
}
* @return Context of child
* @throws IllegalArgumentException If supplied child class is not valid in specified context.
*/
- <E extends DataObject> @NonNull BindingDataObjectCodecTreeNode<E> streamChild(@NonNull Class<E> childClass);
+ <E extends DataObject> @NonNull CommonDataObjectCodecTreeNode<E> streamChild(@NonNull Class<E> childClass);
}
*/
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;
import org.opendaylight.yangtools.yang.common.YangConstants;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
/**
- * Serialization service, which provides two-way serialization between Java
- * Binding Data representation and NormalizedNode representation.
+ * Serialization service, which provides two-way serialization between Java Binding Data representation and
+ * NormalizedNode representation.
*/
public interface BindingNormalizedNodeSerializer {
+ /**
+ * Result of a {@link BindingNormalizedNodeSerializer#toNormalizedNode(InstanceIdentifier, DataObject)}. Since the
+ * Binding {@link Augmentation} does not have an exact equivalent, there are two specializations of this class:
+ * {@link NodeResult} and {@link AugmentationResult}.
+ */
+ sealed interface NormalizedResult {
+ /**
+ * Return the {@link YangInstanceIdentifier} path of this result.
+ *
+ * @return A {@link YangInstanceIdentifier}
+ */
+ @NonNull YangInstanceIdentifier path();
+ }
+
+ /**
+ * A {@link NormalizedResult} for an {@link Augmentation}.
+ *
+ * @param path A YangInstanceIdentifier identifying the parent of this augmentation
+ * @param possibleChildren {@link PathArgument}s of each possible child
+ * @param children Augmentation children
+ */
+ record AugmentationResult(
+ @NonNull YangInstanceIdentifier path,
+ @NonNull ImmutableSet<PathArgument> possibleChildren,
+ @NonNull ImmutableList<DataContainerChild> children) implements NormalizedResult {
+ public AugmentationResult {
+ requireNonNull(path);
+ requireNonNull(possibleChildren);
+ requireNonNull(children);
+ }
+ }
+
+ record NodeResult(@NonNull YangInstanceIdentifier path, @NonNull NormalizedNode node) implements NormalizedResult {
+ public NodeResult {
+ requireNonNull(path);
+ requireNonNull(node);
+ }
+ }
+
/**
* Translates supplied Binding Instance Identifier into NormalizedNode instance identifier.
*
*
* @param path Binding Instance Identifier pointing to data
* @param data Data object representing data
- * @return NormalizedNode representation
+ * @return {@link NormalizedResult} representation
* @throws IllegalArgumentException If supplied Instance Identifier is not valid.
*/
- <T extends DataObject> @NonNull Entry<YangInstanceIdentifier, NormalizedNode> toNormalizedNode(
- InstanceIdentifier<T> path, T data);
+ <T extends DataObject> @NonNull NormalizedResult toNormalizedNode(InstanceIdentifier<T> path, T data);
/**
* Translates supplied YANG Instance Identifier and NormalizedNode into Binding data.
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.codec.api;
+
+import com.google.common.annotations.Beta;
+import java.util.List;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Common interface shared between {@link BindingDataObjectCodecTreeNode} and {@link BindingAugmentationCodecTreeNode}.
+ * This interface should never be implemented on its own.
+ *
+ * @param <T> DataObject type
+ */
+@Beta
+public interface CommonDataObjectCodecTreeNode<T extends DataObject>
+ extends BindingDataObjectCodecTreeParent<Empty>, BindingObjectCodecTreeNode<T> {
+ /**
+ * Returns binding class of interface which represents API of current schema node. The result is same as invoking
+ * {@link DataObject#implementedInterface()} on instance of data.
+ *
+ * @return interface which defines API of binding representation of data.
+ */
+ @Override
+ @NonNull Class<T> getBindingClass();
+
+ /**
+ * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case,
+ * one must issue getChild(ChoiceClass).getChild(CaseClass).
+ *
+ * <p>
+ * This method differs from {@link #streamChild(Class)}, that is less strict for interfaces representing
+ * augmentation and cases, that may return {@link BindingCodecTreeNode} even if augmentation interface containing
+ * same data was supplied and does not represent augmentation of this node.
+ *
+ * @param childClass Child class by Binding Stream navigation
+ * @return Context of child or Optional.empty is supplied class is not
+ * applicable in context.
+ */
+ <E extends DataObject> Optional<? extends CommonDataObjectCodecTreeNode<E>> possibleStreamChild(
+ @NonNull Class<E> childClass);
+
+ /**
+ * Returns nested node context using supplied YANG Instance Identifier.
+ *
+ * @param child
+ * Yang Instance Identifier Argument
+ * @return Context of child
+ * @throws IllegalArgumentException
+ * If supplied argument does not represent valid child.
+ */
+ @NonNull BindingCodecTreeNode yangPathArgumentChild(YangInstanceIdentifier.@NonNull PathArgument child);
+
+ /**
+ * Returns nested node context using supplied Binding Instance Identifier and adds YANG instance identifiers to
+ * the supplied list.
+ *
+ * @param arg
+ * Binding Instance Identifier Argument
+ * @param builder
+ * Mutable instance of list, which is appended by YangInstanceIdentifiers
+ * as tree is walked. Use null if such side-product is not needed.
+ * @return Context of child
+ * @throws IllegalArgumentException
+ * If supplied argument does not represent valid child.
+ */
+ @NonNull CommonDataObjectCodecTreeNode<?> bindingPathArgumentChild(InstanceIdentifier.@NonNull PathArgument arg,
+ @Nullable List<YangInstanceIdentifier.PathArgument> builder);
+
+ /**
+ * Serializes path argument for current node.
+ *
+ * @param arg Binding Path Argument, may be null if Binding Instance Identifier does not have
+ * representation for current node (e.g. choice or case).
+ * @return Yang Path Argument, may be null if Yang Instance Identifier does not have
+ * representation for current node (e.g. case).
+ * @throws IllegalArgumentException If supplied {@code arg} is not valid.
+ */
+ @Beta
+ YangInstanceIdentifier.@Nullable PathArgument serializePathArgument(InstanceIdentifier.@Nullable PathArgument arg);
+
+ /**
+ * Deserializes path argument for current node.
+ *
+ * @param arg Yang Path Argument, may be null if Yang Instance Identifier does not have
+ * representation for current node (e.g. case).
+ * @return Binding Path Argument, may be null if Binding Instance Identifier does not have
+ * representation for current node (e.g. choice or case).
+ * @throws IllegalArgumentException If supplied {@code arg} is not valid.
+ */
+ @Beta
+ InstanceIdentifier.@Nullable PathArgument deserializePathArgument(
+ YangInstanceIdentifier.@Nullable PathArgument arg);
+
+ /**
+ * Return a summary of addressability of potential children. Binding specification does not allow all DOM tree
+ * elements to be directly addressed, which means some recursive tree operations, like data tree changes do not
+ * have a one-to-one mapping from DOM to binding in all cases. This method provides an optimization hint to guide
+ * translation of data structures, allowing for fast paths when all children are known to either be addressable
+ * or non-addressable.
+ *
+ * @return Summary children addressability.
+ */
+ @NonNull ChildAddressabilitySummary getChildAddressabilitySummary();
+
+ /**
+ * Enumeration of possible addressability attribute of all children.
+ */
+ enum ChildAddressabilitySummary {
+ /**
+ * All children are addressable.
+ */
+ ADDRESSABLE,
+ /**
+ * All children are non-addressable, including the case when this node does not have any children.
+ */
+ UNADDRESSABLE,
+ /**
+ * Mixed children, some are addressable and some are not.
+ */
+ MIXED
+ }
+}
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;
}
@Override
- public <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode> toNormalizedNode(
- final InstanceIdentifier<T> path, final T data) {
+ public <T extends DataObject> NormalizedResult toNormalizedNode(final InstanceIdentifier<T> path, final T data) {
return delegate().toNormalizedNode(path, data);
}
@Override
public BindingStreamEventWriter newRpcWriter(final Class<? extends DataContainer> rpcInputOrOutput,
final NormalizedNodeStreamWriter streamWriter) {
- return delegate().newRpcWriter(rpcInputOrOutput,streamWriter);
+ return delegate().newRpcWriter(rpcInputOrOutput, streamWriter);
}
@Override
- public <T extends DataObject> BindingDataObjectCodecTreeNode<T> getSubtreeCodec(final InstanceIdentifier<T> path) {
+ public <T extends DataObject> CodecWithPath<T> getSubtreeCodecWithPath(final InstanceIdentifier<T> path) {
+ return delegate().getSubtreeCodecWithPath(path);
+ }
+
+ @Override
+ public <T extends DataObject> CommonDataObjectCodecTreeNode<T> getSubtreeCodec(final InstanceIdentifier<T> path) {
return delegate().getSubtreeCodec(path);
}
}
@Override
- public <E extends DataObject> BindingDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
+ public <E extends DataObject> CommonDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
return delegate().streamChild(childClass);
}
}
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-impl</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-util</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-model-api</artifactId>
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.codec.impl;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.lang.invoke.MethodHandle;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
+import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+
+/**
+ * Abstract base for {@link DataObjectCodecContext} and {@link AugmentationNodeContext}. They share most of their
+ * mechanics, but notably:
+ * <ol>
+ * <li>DataObjectCodecContext has an exact DistinctNodeContainer and YangInstanceIdentifier mapping and can be the
+ * target of augmentations (i.e. can implement Augmentable contract)</li>
+ * <li>AugmentationNodeContext has neither of those traits and really is just a filter of its parent
+ * DistinctNodeContainer</li>
+ * </ol>
+ *
+ * <p>
+ * Unfortunately {@code Augmentation} is a also a {@link DataObject}, so things get a bit messy.
+ *
+ * <p>
+ * While this class is public, it not part of API surface and is an implementation detail. The only reason for it being
+ * public is that it needs to be accessible by code generated at runtime.
+ */
+public abstract class AbstractDataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
+ extends DataContainerCodecContext<D, T> {
+ private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
+ private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
+ private final ImmutableMap<PathArgument, NodeContextSupplier> byYang;
+ private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
+ private final MethodHandle proxyConstructor;
+
+ AbstractDataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
+ final CodecDataObjectAnalysis<T> analysis) {
+ super(prototype);
+ byBindingArgClass = analysis.byBindingArgClass;
+ byStreamClass = analysis.byStreamClass;
+ byYang = analysis.byYang;
+ leafChild = analysis.leafNodes;
+ proxyConstructor = analysis.proxyConstructor;
+ }
+
+ @Override
+ public final WithStatus getSchema() {
+ // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
+ return (WithStatus) getType().statement();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
+ return (DataContainerCodecContext<C, ?>) childNonNull(streamChildPrototype(childClass), childClass,
+ "Child %s is not valid child of %s", getBindingClass(), childClass).get();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public final <C extends DataObject> Optional<DataContainerCodecContext<C, ?>> possibleStreamChild(
+ final Class<C> childClass) {
+ final var childProto = streamChildPrototype(childClass);
+ if (childProto != null) {
+ return Optional.of((DataContainerCodecContext<C, ?>) childProto.get());
+ }
+ return Optional.empty();
+ }
+
+ @Nullable DataContainerCodecPrototype<?> streamChildPrototype(final @NonNull Class<?> childClass) {
+ return byStreamClass.get(childClass);
+ }
+
+ @Override
+ public final DataContainerCodecContext<?, ?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
+ final List<PathArgument> builder) {
+ final var argType = arg.getType();
+ final var context = childNonNull(pathChildPrototype(argType), argType,
+ "Class %s is not valid child of %s", argType, getBindingClass())
+ .get();
+ if (context instanceof ChoiceNodeCodecContext<?> choice) {
+ choice.addYangPathArgument(arg, builder);
+
+ final var caseType = arg.getCaseType();
+ final var type = arg.getType();
+ final DataContainerCodecContext<?, ?> caze;
+ if (caseType.isPresent()) {
+ // Non-ambiguous addressing this should not pose any problems
+ caze = choice.streamChild(caseType.orElseThrow());
+ } else {
+ caze = choice.getCaseByChildClass(type);
+ }
+
+ caze.addYangPathArgument(arg, builder);
+ return caze.bindingPathArgumentChild(arg, builder);
+ }
+ context.addYangPathArgument(arg, builder);
+ return context;
+ }
+
+ @Nullable DataContainerCodecPrototype<?> pathChildPrototype(final @NonNull Class<? extends DataObject> argType) {
+ return byBindingArgClass.get(argType);
+ }
+
+ @Override
+ public final NodeCodecContext yangPathArgumentChild(final PathArgument arg) {
+ final var lookup = arg instanceof NodeIdentifierWithPredicates ? new NodeIdentifier(arg.getNodeType()) : arg;
+ return childNonNull(yangChildSupplier(lookup), arg,
+ "Argument %s is not valid child of %s", arg, getSchema())
+ .get();
+ }
+
+ // FIXME: Never contains NodeIdentifierWithPredicates, what about NodeWithValue?
+ // If it can't be here, it is always NodeIdentifier and we should specify that
+ @Nullable NodeContextSupplier yangChildSupplier(final @NonNull PathArgument arg) {
+ return byYang.get(arg);
+ }
+
+ @SuppressWarnings("checkstyle:illegalCatch")
+ final @NonNull D createBindingProxy(final DistinctNodeContainer<?, ?> node) {
+ try {
+ return (D) proxyConstructor.invokeExact(this, node);
+ } catch (final Throwable e) {
+ Throwables.throwIfUnchecked(e);
+ throw new IllegalStateException(e);
+ }
+ }
+
+ final ValueNodeCodecContext getLeafChild(final String name) {
+ final ValueNodeCodecContext value = leafChild.get(name);
+ if (value == null) {
+ throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass());
+ }
+ return value;
+ }
+
+ final @NonNull ImmutableSet<PathArgument> byYangKeySet() {
+ return byYang.keySet();
+ }
+
+ final @NonNull ImmutableSet<Class<?>> byBindingArgClassKeySet() {
+ return byBindingArgClass.keySet();
+ }
+
+ abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
+ DistinctNodeContainer<PathArgument, NormalizedNode> data);
+}
* This is not really a codec context, but rather a holder of input and output codec contexts.
*/
final class ActionCodecContext {
- private final DataContainerCodecContext<?, InputRuntimeType> input;
- private final DataContainerCodecContext<?, OutputRuntimeType> output;
+ private final DataObjectCodecContext<?, InputRuntimeType> input;
+ private final DataObjectCodecContext<?, OutputRuntimeType> output;
- ActionCodecContext(final DataContainerCodecContext<?, InputRuntimeType> input,
- final DataContainerCodecContext<?, OutputRuntimeType> output) {
+ ActionCodecContext(final DataObjectCodecContext<?, InputRuntimeType> input,
+ final DataObjectCodecContext<?, OutputRuntimeType> output) {
this.input = requireNonNull(input);
this.output = requireNonNull(output);
}
- DataContainerCodecContext<?, InputRuntimeType> input() {
+ DataObjectCodecContext<?, InputRuntimeType> input() {
return input;
}
- DataContainerCodecContext<?, OutputRuntimeType> output() {
+ DataObjectCodecContext<?, OutputRuntimeType> output() {
return output;
}
}
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}.
@SuppressWarnings("unused")
private volatile ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> cachedAugmentations;
- protected AugmentableCodecDataObject(final DataObjectCodecContext<T, ?> context,
+ protected AugmentableCodecDataObject(final AbstractDataObjectCodecContext<T, ?> context,
final DistinctNodeContainer<?, ?> data) {
super(context, data);
}
public final <A extends Augmentation<T>> @Nullable A augmentation(final Class<A> augmentationType) {
requireNonNull(augmentationType, "Supplied augmentation must not be null.");
- final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> aug = acquireAugmentations();
+ final var aug = acquireAugmentations();
if (aug != null) {
return (A) aug.get(augmentationType);
}
@SuppressWarnings("rawtypes")
- final Optional<DataContainerCodecContext<?, ?>> optAugCtx = codecContext().possibleStreamChild(
- (Class) augmentationType);
+ final var optAugCtx = codecContext().possibleStreamChild((Class) augmentationType);
if (optAugCtx.isPresent()) {
- final DataContainerCodecContext<?, ?> augCtx = optAugCtx.orElseThrow();
+ final var augCtx = (AugmentationNodeContext<A>) optAugCtx.orElseThrow();
// Due to binding specification not representing grouping instantiations we can end up having the same
// augmentation applied to a grouping multiple times. While these augmentations have the same shape, they
// are still represented by distinct binding classes and therefore we need to make sure the result matches
// the augmentation the user is requesting -- otherwise a strict receiver would end up with a cryptic
// ClassCastException.
if (augmentationType.isAssignableFrom(augCtx.getBindingClass())) {
- final NormalizedNode augData = codecData().childByArg(augCtx.getDomPathArgument());
- if (augData != null) {
- return (A) augCtx.deserialize(augData);
+ final var augObj = augCtx.filterFrom((DataContainerNode) codecData());
+ if (augObj != null) {
+ return augObj;
}
}
}
}
@SuppressWarnings("unchecked")
- private ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> loadAugmentations() {
- final ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> ret = ImmutableMap.copyOf(
- codecContext().getAllAugmentationsFrom(codecData()));
+ private @NonNull ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>> loadAugmentations() {
+ final var ret = ImmutableMap.copyOf(codecContext().getAllAugmentationsFrom(codecData()));
final Object witness = CACHED_AUGMENTATIONS.compareAndExchangeRelease(this, null, ret);
return witness == null ? ret : (ImmutableMap<Class<? extends Augmentation<T>>, Augmentation<T>>) witness;
}
*/
package org.opendaylight.mdsal.binding.dom.codec.impl;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
final class AugmentationNodeContext<D extends DataObject & Augmentation<?>>
- extends DataObjectCodecContext<D, AugmentRuntimeType> {
- AugmentationNodeContext(final DataContainerCodecPrototype<AugmentRuntimeType> prototype) {
- super(prototype);
+ extends AbstractDataObjectCodecContext<D, AugmentRuntimeType> implements BindingAugmentationCodecTreeNode<D> {
+ AugmentationNodeContext(final DataContainerCodecPrototype.Augmentation prototype) {
+ super(prototype, new CodecDataObjectAnalysis<>(prototype, CodecItemFactory.of(), null));
+ }
+
+ @Override
+ public PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
+ if (!bindingArg().equals(arg)) {
+ throw new IllegalArgumentException("Unexpected argument " + arg);
+ }
+ return null;
+ }
+
+ @Override
+ public InstanceIdentifier.PathArgument deserializePathArgument(final PathArgument arg) {
+ if (arg != null) {
+ throw new IllegalArgumentException("Unexpected argument " + arg);
+ }
+ return bindingArg();
+ }
+
+ @Override
+ public D filterFrom(final DataContainerNode parentData) {
+ for (var childArg : ((DataContainerCodecPrototype.Augmentation) prototype).getChildArgs()) {
+ if (parentData.childByArg(childArg) != null) {
+ return createProxy(parentData);
+ }
+ }
+ return null;
+ }
+
+ private @NonNull D createProxy(final @NonNull DataContainerNode parentData) {
+ return createBindingProxy(parentData);
}
@Override
- public D deserialize(final NormalizedNode data) {
- return createBindingProxy(checkDataArgument(AugmentationNode.class, data));
+ public void streamTo(final NormalizedNodeStreamWriter writer, final D data) throws IOException {
+ eventStreamSerializer().serialize(requireNonNull(data), new BindingToNormalizedStreamWriter(this, writer));
+ }
+
+ @Override
+ public ImmutableSet<PathArgument> childPathArguments() {
+ return byYangKeySet();
+ }
+
+ @Override
+ public ImmutableSet<Class<?>> childBindingClasses() {
+ return byBindingArgClassKeySet();
}
@Override
protected Object deserializeObject(final NormalizedNode normalizedNode) {
- return deserialize(normalizedNode);
+ return filterFrom(checkDataArgument(DataContainerNode.class, normalizedNode));
+ }
+
+ @Override
+ protected PathArgument getDomPathArgument() {
+ return null;
+ }
+
+ @Override
+ Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
+ final DistinctNodeContainer<PathArgument, NormalizedNode> data) {
+ return Map.of();
}
-}
\ No newline at end of file
+}
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;
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;
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;
}
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 {
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
}
@Override
- public <E extends DataObject> BindingDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
+ public <E extends DataObject> CommonDataObjectCodecTreeNode<E> streamChild(final Class<E> childClass) {
return root.streamChild(childClass);
}
@Override
@SuppressWarnings("unchecked")
- public <T extends DataObject> BindingDataObjectCodecTreeNode<T> getSubtreeCodec(final InstanceIdentifier<T> path) {
+ public <T extends DataObject> CodecWithPath<T> getSubtreeCodecWithPath(final InstanceIdentifier<T> path) {
+ final var yangArgs = new ArrayList<YangInstanceIdentifier.PathArgument>();
+ final var codecContext = getCodecContextNode(path, yangArgs);
+
+ // TODO Do we need defensive check here?
+ return new CodecWithPath<>((CommonDataObjectCodecTreeNode<T>) codecContext,
+ YangInstanceIdentifier.create(yangArgs));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T extends DataObject> CommonDataObjectCodecTreeNode<T> getSubtreeCodec(final InstanceIdentifier<T> path) {
// TODO Do we need defensive check here?
- return (BindingDataObjectCodecTreeNode<T>) getCodecContextNode(path, null);
+ return (CommonDataObjectCodecTreeNode<T>) getCodecContextNode(path, null);
}
@Override
}
@Override
- public <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode> toNormalizedNode(
- final InstanceIdentifier<T> path, final T data) {
- final NormalizedNodeResult result = new NormalizedNodeResult();
- // We create DOM stream writer which produces normalized nodes
- final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
-
+ public <T extends DataObject> NormalizedResult toNormalizedNode(final InstanceIdentifier<T> path, final T data) {
// We create Binding Stream Writer which translates from Binding to Normalized Nodes
- final Entry<YangInstanceIdentifier, BindingStreamEventWriter> writeCtx = newWriterAndIdentifier(path,
- domWriter);
+ final var yangArgs = new ArrayList<YangInstanceIdentifier.PathArgument>();
+ final var codecContext = getCodecContextNode(path, yangArgs);
+ final var yangPath = YangInstanceIdentifier.create(yangArgs);
+
+ // We create DOM stream writer which produces normalized nodes
+ final var result = new NormalizedNodeResult();
+ final var domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+ final var bindingWriter = new BindingToNormalizedStreamWriter(codecContext, domWriter);
+ final var augment = codecContext instanceof BindingAugmentationCodecTreeNode<?> augmentNode ? augmentNode
+ : null;
- // We get serializer which reads binding data and uses Binding To Normalized Node writer to write result
try {
- getSerializer(path.getTargetType()).serialize(data, writeCtx.getValue());
+ // Augmentations do not have a representation, so we are faking a ContainerNode as the parent and we will be
+ // extracting the resulting children.
+ if (augment != null) {
+ domWriter.startContainerNode(FAKE_NODEID, NormalizedNodeStreamWriter.UNKNOWN_SIZE);
+ }
+
+ // We get serializer which reads binding data and uses Binding To Normalized Node writer to write result
+ getSerializer(path.getTargetType()).serialize(data, bindingWriter);
+
+ if (augment != null) {
+ domWriter.endNode();
+ }
} catch (final IOException e) {
LOG.error("Unexpected failure while serializing path {} data {}", path, data, e);
throw new IllegalStateException("Failed to create normalized node", e);
}
- return Map.entry(writeCtx.getKey(), result.getResult());
+
+ // Terminate the fake container and extract it to the result
+ if (augment != null) {
+ return new AugmentationResult(yangPath, augment.childPathArguments(),
+ ImmutableList.copyOf(((ContainerNode) result.getResult()).body()));
+ }
+ return new NodeResult(yangPath, result.getResult());
}
@Override
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;
@Override
public void endNode() throws IOException {
NodeCodecContext left = schema.pop();
- // NormalizedNode writer does not have entry into case, but into choice
- // so for leaving case, we do not emit endNode.
- if (!(left instanceof CaseNodeCodecContext)) {
+ // Due to writer does not start a new node on startCase() and on startAugmentationNode()
+ // node ending should not be triggered when associated endNode() is invoked.
+ if (!(left instanceof CaseNodeCodecContext) && !(left instanceof AugmentationNodeContext)) {
delegate.endNode();
}
}
private Map.Entry<NodeIdentifier, Object> serializeLeaf(final String localName, final Object value) {
final var current = current();
- if (!(current instanceof DataObjectCodecContext<?, ?> currentCasted)) {
+ if (!(current instanceof AbstractDataObjectCodecContext<?, ?> currentCasted)) {
throw new IllegalArgumentException("Unexpected current context " + current);
}
}
@Override
- public void startAugmentationNode(final Class<? extends Augmentation<?>> augmentationType)
- throws IOException {
- delegate.startAugmentationNode(enter(augmentationType, AugmentationIdentifier.class));
+ public void startAugmentationNode(final Class<? extends Augmentation<?>> augmentationType) throws IOException {
+ enter(augmentationType, NodeIdentifier.class);
}
@Override
import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableSet;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCodec;
import org.opendaylight.yangtools.yang.binding.BindingObject;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-class CachingNormalizedNodeCodec<D extends DataObject> extends AbstractBindingNormalizedNodeCacheHolder implements
- BindingNormalizedNodeCachingCodec<D> {
- private final DataContainerCodecContext<D, ?> context;
+class CachingNormalizedNodeCodec<D extends DataObject,
+ C extends DataContainerCodecContext<D, ?> & BindingNormalizedNodeCodec<D>>
+ extends AbstractBindingNormalizedNodeCacheHolder implements BindingNormalizedNodeCachingCodec<D> {
+ private final @NonNull C context;
- CachingNormalizedNodeCodec(final DataContainerCodecContext<D, ?> subtreeRoot,
- final ImmutableSet<Class<? extends BindingObject>> cacheSpec) {
+ CachingNormalizedNodeCodec(final C context, final ImmutableSet<Class<? extends BindingObject>> cacheSpec) {
super(cacheSpec);
- this.context = requireNonNull(subtreeRoot);
+ this.context = requireNonNull(context);
}
@Override
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;
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;
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;
* ambiguous reference and issue warn once when they are encountered -- tracking warning information in
* {@link #ambiguousByCaseChildWarnings}.
*/
-final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCodecContext<D, ChoiceRuntimeType> {
+final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCodecContext<D, ChoiceRuntimeType>
+ implements BindingDataObjectCodecTreeNode<D> {
private static final Logger LOG = LoggerFactory.getLogger(ChoiceNodeCodecContext.class);
private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChild;
// 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);
}
}
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
return getDomPathArgument();
}
+ @Override
+ public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
+ final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
+ return createCachingCodec(this, cacheSpecifier);
+ }
+
DataContainerCodecContext<?, ?> getCaseByChildClass(final @NonNull Class<? extends DataObject> type) {
DataContainerCodecPrototype<?> result = byCaseChildClass.get(type);
if (result == null) {
}
}
- private final @NonNull DataObjectCodecContext<T, ?> context;
+ private final @NonNull AbstractDataObjectCodecContext<T, ?> context;
@SuppressWarnings("rawtypes")
private final @NonNull DistinctNodeContainer data;
// FIXME: consider using a primitive int-based cache (with 0 being uninit)
private volatile Integer cachedHashcode;
- protected CodecDataObject(final DataObjectCodecContext<T, ?> context, final DistinctNodeContainer<?, ?> data) {
+ protected CodecDataObject(final AbstractDataObjectCodecContext<T, ?> context,
+ final DistinctNodeContainer<?, ?> data) {
this.data = requireNonNull(data, "Data must not be null");
this.context = requireNonNull(context, "Context must not be null");
}
protected abstract boolean codecEquals(Object obj);
- final @NonNull DataObjectCodecContext<T, ?> codecContext() {
+ final @NonNull AbstractDataObjectCodecContext<T, ?> codecContext() {
return context;
}
*/
final class CodecDataObjectAnalysis<R extends CompositeRuntimeType> {
private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class,
- DataObjectCodecContext.class, DistinctNodeContainer.class);
+ AbstractDataObjectCodecContext.class, DistinctNodeContainer.class);
private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class,
- DataObjectCodecContext.class, DistinctNodeContainer.class);
+ AbstractDataObjectCodecContext.class, DistinctNodeContainer.class);
final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
*/
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;
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;
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;
import org.slf4j.LoggerFactory;
abstract class DataContainerCodecContext<D extends DataObject, T extends RuntimeTypeContainer> extends NodeCodecContext
- implements BindingDataObjectCodecTreeNode<D> {
+ implements CommonDataObjectCodecTreeNode<D> {
private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecContext.class);
private static final VarHandle EVENT_STREAM_SERIALIZER;
}
}
- private final @NonNull DataContainerCodecPrototype<T> prototype;
+ final @NonNull DataContainerCodecPrototype<T> prototype;
// Accessed via a VarHandle
@SuppressWarnings("unused")
}
@Override
- protected final YangInstanceIdentifier.PathArgument getDomPathArgument() {
+ protected YangInstanceIdentifier.PathArgument getDomPathArgument() {
return prototype.getYangArg();
}
*/
void addYangPathArgument(final PathArgument arg, final List<YangInstanceIdentifier.PathArgument> builder) {
if (builder != null) {
- builder.add(getDomPathArgument());
+ final var yangArg = getDomPathArgument();
+ if (yangArg != null) {
+ builder.add(yangArg);
+ }
}
}
return getClass().getSimpleName() + " [" + prototype.getBindingClass() + "]";
}
- @Override
- public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
- final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
- if (cacheSpecifier.isEmpty()) {
- return new NonCachingCodec<>(this);
- }
- return new CachingNormalizedNodeCodec<>(this, ImmutableSet.copyOf(cacheSpecifier));
+ static final <T extends DataObject, C extends DataContainerCodecContext<T, ?> & BindingNormalizedNodeCodec<T>>
+ @NonNull BindingNormalizedNodeCachingCodec<T> createCachingCodec(final C context,
+ final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
+ return cacheSpecifier.isEmpty() ? new NonCachingCodec<>(context)
+ : new CachingNormalizedNodeCodec<>(context, ImmutableSet.copyOf(cacheSpecifier));
}
protected final <V> @NonNull V childNonNull(final @Nullable V nullable,
final YangInstanceIdentifier.PathArgument child, final String message, final Object... args) {
if (nullable == null) {
- throw childNullException(extractName(child), message, args);
+ throw childNullException(child.getNodeType(), message, args);
}
return nullable;
}
return new IncorrectNestingException(message, args);
}
- private static QName extractName(final YangInstanceIdentifier.PathArgument child) {
- if (child instanceof AugmentationIdentifier) {
- final Set<QName> children = ((AugmentationIdentifier) child).getPossibleChildNames();
- checkArgument(!children.isEmpty(), "Augmentation without childs must not be used in data");
- return children.iterator().next();
- }
- return child.getNodeType();
- }
-
final DataObjectSerializer eventStreamSerializer() {
final DataObjectSerializer existing = (DataObjectSerializer) EVENT_STREAM_SERIALIZER.getAcquire(this);
return existing != null ? existing : loadEventStreamSerializer();
return witness == null ? loaded : (DataObjectSerializer) witness;
}
- @Override
- public NormalizedNode serialize(final D data) {
+ final @NonNull NormalizedNode serializeImpl(final @NonNull D data) {
final NormalizedNodeResult result = new NormalizedNodeResult();
// We create DOM stream writer which produces normalized nodes
final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
+import com.google.common.collect.ImmutableSet;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode.ChildAddressabilitySummary;
+import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode.ChildAddressabilitySummary;
import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-final class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements NodeContextSupplier {
+abstract sealed class DataContainerCodecPrototype<T extends RuntimeTypeContainer> implements NodeContextSupplier {
+ static final class Augmentation extends DataContainerCodecPrototype<AugmentRuntimeType> {
+ private final @NonNull ImmutableSet<NodeIdentifier> childArgs;
+
+ @SuppressWarnings("unchecked")
+ Augmentation(final Class<?> cls, final QNameModule namespace, final AugmentRuntimeType type,
+ final CodecContextFactory factory, final ImmutableSet<NodeIdentifier> childArgs) {
+ super(Item.of((Class<? extends DataObject>) cls), namespace, type, factory);
+ this.childArgs = requireNonNull(childArgs);
+ }
+
+ @Override
+ PathArgument getYangArg() {
+ throw new UnsupportedOperationException("Augmentation does not have PathArgument address");
+ }
+
+ @Override
+ AugmentationNodeContext<?> createInstance() {
+ return new AugmentationNodeContext<>(this);
+ }
+
+ // Guaranteed to be non-empty
+ @NonNull ImmutableSet<NodeIdentifier> getChildArgs() {
+ return childArgs;
+ }
+ }
+
+ static final class Regular<T extends RuntimeTypeContainer> extends DataContainerCodecPrototype<T> {
+ private final @NonNull PathArgument yangArg;
+
+ @SuppressWarnings("unchecked")
+ private Regular(final Class<?> cls, final PathArgument yangArg, final T type,
+ final CodecContextFactory factory) {
+ this(Item.of((Class<? extends DataObject>) cls), yangArg, type, factory);
+ }
+
+ private Regular(final Item<?> bindingArg, final PathArgument yangArg, final T type,
+ final CodecContextFactory factory) {
+ super(bindingArg, yangArg.getNodeType().getModule(), type, factory);
+ this.yangArg = requireNonNull(yangArg);
+ }
+
+ @Override
+ PathArgument getYangArg() {
+ return yangArg;
+ }
+
+ @Override
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ DataContainerCodecContext<?, T> createInstance() {
+ final var type = getType();
+ if (type instanceof ContainerLikeRuntimeType containerLike) {
+ if (containerLike instanceof ContainerRuntimeType container
+ && container.statement().findFirstEffectiveSubstatement(PresenceEffectiveStatement.class)
+ .isEmpty()) {
+ return new NonPresenceContainerNodeCodecContext(this);
+ }
+ return new ContainerNodeCodecContext(this);
+ } else if (type instanceof ListRuntimeType) {
+ return Identifiable.class.isAssignableFrom(getBindingClass())
+ ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListRuntimeType>) this)
+ : new ListNodeCodecContext(this);
+ } else if (type instanceof ChoiceRuntimeType) {
+ return new ChoiceNodeCodecContext(this);
+ } else if (type instanceof CaseRuntimeType) {
+ return new CaseNodeCodecContext(this);
+ }
+ throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + type);
+ }
+ }
+
private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
private static final VarHandle INSTANCE;
private final @NonNull QNameModule namespace;
private final @NonNull CodecContextFactory factory;
private final @NonNull Item<?> bindingArg;
- private final @NonNull PathArgument yangArg;
private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
+ // multiple paths represent augmentation wrapper
+ // FIXME: this means it is either this or 'childArgs'
+
// Accessed via INSTANCE
@SuppressWarnings("unused")
private volatile DataContainerCodecContext<?, T> instance;
- @SuppressWarnings("unchecked")
- private DataContainerCodecPrototype(final Class<?> cls, final PathArgument yangArg, final T type,
- final CodecContextFactory factory) {
- this(Item.of((Class<? extends DataObject>) cls), yangArg, type, factory);
- }
-
- private DataContainerCodecPrototype(final Item<?> bindingArg, final PathArgument yangArg, final T type,
+ private DataContainerCodecPrototype(final Item<?> bindingArg, final QNameModule namespace, final T type,
final CodecContextFactory factory) {
this.bindingArg = requireNonNull(bindingArg);
- this.yangArg = requireNonNull(yangArg);
+ this.namespace = requireNonNull(namespace);
this.type = requireNonNull(type);
this.factory = requireNonNull(factory);
- if (yangArg instanceof AugmentationIdentifier augId) {
- final var childNames = augId.getPossibleChildNames();
- verify(!childNames.isEmpty(), "Unexpected empty identifier for %s", type);
- namespace = childNames.iterator().next().getModule();
- } else {
- namespace = yangArg.getNodeType().getModule();
- }
-
- childAddressabilitySummary = type instanceof RuntimeType
- ? computeChildAddressabilitySummary(((RuntimeType) type).statement())
+ childAddressabilitySummary = type instanceof RuntimeType runtimeType
+ ? computeChildAddressabilitySummary(runtimeType.statement())
// BindingRuntimeTypes, does not matter
: ChildAddressabilitySummary.MIXED;
}
}
static DataContainerCodecPrototype<BindingRuntimeTypes> rootPrototype(final CodecContextFactory factory) {
- return new DataContainerCodecPrototype<>(DataRoot.class, NodeIdentifier.create(SchemaContext.NAME),
+ return new Regular<>(DataRoot.class, NodeIdentifier.create(SchemaContext.NAME),
factory.getRuntimeContext().getTypes(), factory);
}
static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Class<?> cls, final T type,
final CodecContextFactory factory) {
- return new DataContainerCodecPrototype<>(cls, createIdentifier(type), type, factory);
+ return new Regular<>(cls, createIdentifier(type), type, factory);
}
static <T extends CompositeRuntimeType> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T type,
final CodecContextFactory factory) {
- return new DataContainerCodecPrototype<>(bindingArg, createIdentifier(type), type, factory);
- }
-
- static DataContainerCodecPrototype<AugmentRuntimeType> from(final Class<?> augClass,
- final AugmentationIdentifier arg, final AugmentRuntimeType schema, final CodecContextFactory factory) {
- return new DataContainerCodecPrototype<>(augClass, arg, schema, factory);
+ return new Regular<>(bindingArg, createIdentifier(type), type, factory);
}
static DataContainerCodecPrototype<NotificationRuntimeType> from(final Class<?> augClass,
final NotificationRuntimeType schema, final CodecContextFactory factory) {
- return new DataContainerCodecPrototype<>(augClass, NodeIdentifier.create(schema.statement().argument()), schema,
- factory);
+ return new Regular<>(augClass, NodeIdentifier.create(schema.statement().argument()), schema, factory);
}
private static @NonNull NodeIdentifier createIdentifier(final CompositeRuntimeType type) {
return NodeIdentifier.create((QName) arg);
}
- @NonNull T getType() {
+ final @NonNull T getType() {
return type;
}
- @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
+ final @NonNull ChildAddressabilitySummary getChildAddressabilitySummary() {
return childAddressabilitySummary;
}
- @NonNull QNameModule getNamespace() {
+ final @NonNull QNameModule getNamespace() {
return namespace;
}
- @NonNull CodecContextFactory getFactory() {
+ final @NonNull CodecContextFactory getFactory() {
return factory;
}
- @NonNull Class<?> getBindingClass() {
+ final @NonNull Class<?> getBindingClass() {
return bindingArg.getType();
}
- @NonNull Item<?> getBindingArg() {
+ final @NonNull Item<?> getBindingArg() {
return bindingArg;
}
- @NonNull PathArgument getYangArg() {
- return yangArg;
- }
+ abstract @NonNull PathArgument getYangArg();
@Override
- public DataContainerCodecContext<?, T> get() {
- final DataContainerCodecContext<?, T> existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
+ public final DataContainerCodecContext<?, T> get() {
+ final var existing = (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
return existing != null ? existing : loadInstance();
}
+ @SuppressWarnings("unchecked")
+ final <R extends CompositeRuntimeType> DataObjectCodecContext<?, R> getDataObject() {
+ final var context = get();
+ verify(context instanceof DataObjectCodecContext, "Unexpected instance %s", context);
+ return (DataObjectCodecContext<?, R>) context;
+ }
+
private @NonNull DataContainerCodecContext<?, T> loadInstance() {
final var tmp = createInstance();
final var witness = (DataContainerCodecContext<?, T>) INSTANCE.compareAndExchangeRelease(this, null, tmp);
@SuppressWarnings({ "rawtypes", "unchecked" })
// This method must allow concurrent loading, i.e. nothing in it may have effects outside of the loaded object
- private @NonNull DataContainerCodecContext<?, T> createInstance() {
- // FIXME: make protected abstract
- if (type instanceof ContainerLikeRuntimeType containerLike) {
- if (containerLike instanceof ContainerRuntimeType container
- && container.statement().findFirstEffectiveSubstatement(PresenceEffectiveStatement.class).isEmpty()) {
- return new NonPresenceContainerNodeCodecContext(this);
- }
- return new ContainerNodeCodecContext(this);
- } else if (type instanceof ListRuntimeType) {
- return Identifiable.class.isAssignableFrom(getBindingClass())
- ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListRuntimeType>) this)
- : new ListNodeCodecContext(this);
- } else if (type instanceof ChoiceRuntimeType) {
- return new ChoiceNodeCodecContext(this);
- } else if (type instanceof AugmentRuntimeType) {
- return new AugmentationNodeContext(this);
- } else if (type instanceof CaseRuntimeType) {
- return new CaseNodeCodecContext(this);
- }
- throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + type);
- }
+ abstract @NonNull DataContainerCodecContext<?, T> createInstance();
}
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;
*/
@Beta
public abstract class DataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
- extends DataContainerCodecContext<D, T> {
+ extends AbstractDataObjectCodecContext<D, T> implements BindingDataObjectCodecTreeNode<D> {
private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class);
private static final VarHandle MISMATCHED_AUGMENTED;
}
}
- private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
- private final ImmutableMap<YangInstanceIdentifier.PathArgument, NodeContextSupplier> byYang;
- private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
- private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
- private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> augmentationByYang;
- private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> augmentationByStream;
+ private final ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation> augmentToPrototype;
+ private final ImmutableMap<PathArgument, Class<?>> yangToAugmentClass;
private final @NonNull Class<? extends CodecDataObject<?>> generatedClass;
- private final MethodHandle proxyConstructor;
// Note this the content of this field depends only of invariants expressed as this class's fields or
// BindingRuntimeContext. It is only accessed via MISMATCHED_AUGMENTED above.
private DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
final CodecDataObjectAnalysis<T> analysis) {
- super(prototype);
+ super(prototype, analysis);
// Inherit analysis stuff
- leafChild = analysis.leafNodes;
- proxyConstructor = analysis.proxyConstructor;
generatedClass = analysis.generatedClass;
- byBindingArgClass = analysis.byBindingArgClass;
- byStreamClass = analysis.byStreamClass;
- byYang = analysis.byYang;
// Deal with augmentations, which are not something we analysis provides
- final var augByYang = new HashMap<PathArgument, DataContainerCodecPrototype<?>>();
- final var augByStream = new HashMap<Class<?>, DataContainerCodecPrototype<?>>();
+ final var augPathToBinding = new HashMap<PathArgument, Class<?>>();
+ final var augClassToProto = new HashMap<Class<?>, DataContainerCodecPrototype.Augmentation>();
for (var augment : analysis.possibleAugmentations) {
final var augProto = loadAugmentPrototype(augment);
if (augProto != null) {
- final var augYangArg = augProto.getYangArg();
- if (augByYang.putIfAbsent(augYangArg, augProto) == null) {
- LOG.trace("Discovered new YANG mapping {} -> {} in {}", augYangArg, augProto, this);
- }
final var augBindingClass = augProto.getBindingClass();
- if (augByStream.putIfAbsent(augBindingClass, augProto) == null) {
- LOG.trace("Discovered new class mapping {} -> {} in {}", augBindingClass, augProto, this);
+ for (var childPath : augProto.getChildArgs()) {
+ augPathToBinding.putIfAbsent(childPath, augBindingClass);
}
+ augClassToProto.putIfAbsent(augBindingClass, augProto);
}
}
- augmentationByYang = ImmutableMap.copyOf(augByYang);
- augmentationByStream = ImmutableMap.copyOf(augByStream);
- }
-
- @Override
- public final WithStatus getSchema() {
- // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
- return (WithStatus) getType().statement();
+ yangToAugmentClass = ImmutableMap.copyOf(augPathToBinding);
+ augmentToPrototype = ImmutableMap.copyOf(augClassToProto);
}
@Override
- @SuppressWarnings("unchecked")
- public <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
- return (DataContainerCodecContext<C, ?>) childNonNull(streamChildPrototype(childClass), childClass,
- "Child %s is not valid child of %s", getBindingClass(), childClass).get();
+ final DataContainerCodecPrototype<?> pathChildPrototype(final Class<? extends DataObject> argType) {
+ final var child = super.pathChildPrototype(argType);
+ return child != null ? child : augmentToPrototype.get(argType);
}
- private DataContainerCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
- final DataContainerCodecPrototype<?> childProto = byStreamClass.get(childClass);
- if (childProto != null) {
- return childProto;
- }
- if (Augmentation.class.isAssignableFrom(childClass)) {
- return augmentationByClass(childClass);
- }
- return null;
- }
-
- @SuppressWarnings("unchecked")
@Override
- public <C extends DataObject> Optional<DataContainerCodecContext<C, ?>> possibleStreamChild(
- final Class<C> childClass) {
- final DataContainerCodecPrototype<?> childProto = streamChildPrototype(childClass);
- if (childProto != null) {
- return Optional.of((DataContainerCodecContext<C, ?>) childProto.get());
+ final DataContainerCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
+ final var child = super.streamChildPrototype(childClass);
+ if (child == null && Augmentation.class.isAssignableFrom(childClass)) {
+ return getAugmentationProtoByClass(childClass);
}
- return Optional.empty();
+ return child;
}
@Override
- public DataContainerCodecContext<?,?> bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg,
- final List<YangInstanceIdentifier.PathArgument> builder) {
-
- final Class<? extends DataObject> argType = arg.getType();
- DataContainerCodecPrototype<?> ctxProto = byBindingArgClass.get(argType);
- if (ctxProto == null && Augmentation.class.isAssignableFrom(argType)) {
- ctxProto = augmentationByClass(argType);
- }
- final DataContainerCodecContext<?, ?> context = childNonNull(ctxProto, argType,
- "Class %s is not valid child of %s", argType, getBindingClass()).get();
- if (context instanceof ChoiceNodeCodecContext<?> choice) {
- choice.addYangPathArgument(arg, builder);
-
- final Optional<? extends Class<? extends DataObject>> caseType = arg.getCaseType();
- final Class<? extends DataObject> type = arg.getType();
- final DataContainerCodecContext<?, ?> caze;
- if (caseType.isPresent()) {
- // Non-ambiguous addressing this should not pose any problems
- caze = choice.streamChild(caseType.orElseThrow());
- } else {
- caze = choice.getCaseByChildClass(type);
+ final NodeContextSupplier yangChildSupplier(final PathArgument arg) {
+ final var child = super.yangChildSupplier(arg);
+ if (child == null) {
+ final var augClass = yangToAugmentClass.get(arg);
+ if (augClass != null) {
+ return augmentToPrototype.get(augClass);
}
-
- caze.addYangPathArgument(arg, builder);
- return caze.bindingPathArgumentChild(arg, builder);
- }
- context.addYangPathArgument(arg, builder);
- return context;
- }
-
- @Override
- public NodeCodecContext yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
- final NodeContextSupplier childSupplier;
- if (arg instanceof NodeIdentifierWithPredicates) {
- childSupplier = byYang.get(new NodeIdentifier(arg.getNodeType()));
- } else if (arg instanceof AugmentationIdentifier) {
- childSupplier = augmentationByYang.get(arg);
- } else {
- childSupplier = byYang.get(arg);
- }
-
- return childNonNull(childSupplier, arg, "Argument %s is not valid child of %s", arg, getSchema()).get();
- }
-
- protected final ValueNodeCodecContext getLeafChild(final String name) {
- final ValueNodeCodecContext value = leafChild.get(name);
- if (value == null) {
- throw new IncorrectNestingException("Leaf %s is not valid for %s", name, getBindingClass());
}
- return value;
+ return child;
}
- private @Nullable DataContainerCodecPrototype<?> augmentationByClass(final @NonNull Class<?> childClass) {
- final DataContainerCodecPrototype<?> childProto = augmentationByStream.get(childClass);
- return childProto != null ? childProto : mismatchedAugmentationByClass(childClass);
+ private DataContainerCodecPrototype.@Nullable Augmentation getAugmentationProtoByClass(
+ final @NonNull Class<?> augmClass) {
+ final var childProto = augmentToPrototype.get(augmClass);
+ return childProto != null ? childProto : mismatchedAugmentationByClass(augmClass);
}
- private @Nullable DataContainerCodecPrototype<?> mismatchedAugmentationByClass(final @NonNull Class<?> childClass) {
+ private DataContainerCodecPrototype.@Nullable Augmentation mismatchedAugmentationByClass(
+ final @NonNull Class<?> childClass) {
/*
* It is potentially mismatched valid augmentation - we look up equivalent augmentation using reflection
* and walk all stream child and compare augmentations classes if they are equivalent. When we find a match
* we'll cache it so we do not need to perform reflection operations again.
*/
- final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> local =
- (ImmutableMap<Class<?>, DataContainerCodecPrototype<?>>) MISMATCHED_AUGMENTED.getAcquire(this);
- final DataContainerCodecPrototype<?> mismatched = local.get(childClass);
+ final var local =
+ (ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation>) MISMATCHED_AUGMENTED.getAcquire(this);
+ final var mismatched = local.get(childClass);
return mismatched != null ? mismatched : loadMismatchedAugmentation(local, childClass);
-
}
- private @Nullable DataContainerCodecPrototype<?> loadMismatchedAugmentation(
- final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> oldMismatched,
+ private DataContainerCodecPrototype.@Nullable Augmentation loadMismatchedAugmentation(
+ final ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation> oldMismatched,
final @NonNull Class<?> childClass) {
@SuppressWarnings("rawtypes")
final Class<?> augTarget = BindingReflections.findAugmentationTarget((Class) childClass);
// Do not bother with proposals which are not augmentations of our class, or do not match what the runtime
// context would load.
if (getBindingClass().equals(augTarget) && belongsToRuntimeContext(childClass)) {
- for (final DataContainerCodecPrototype<?> realChild : augmentationByStream.values()) {
+ for (var realChild : augmentToPrototype.values()) {
if (Augmentation.class.isAssignableFrom(realChild.getBindingClass())
&& isSubstitutionFor(childClass, realChild.getBindingClass())) {
return cacheMismatched(oldMismatched, childClass, realChild);
return null;
}
- private @NonNull DataContainerCodecPrototype<?> cacheMismatched(
- final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> oldMismatched,
- final @NonNull Class<?> childClass, final @NonNull DataContainerCodecPrototype<?> prototype) {
+ private DataContainerCodecPrototype.@NonNull Augmentation cacheMismatched(
+ final @NonNull ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation> oldMismatched,
+ final @NonNull Class<?> childClass, final DataContainerCodecPrototype.@NonNull Augmentation prototype) {
- ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> expected = oldMismatched;
+ var expected = oldMismatched;
while (true) {
- final Map<Class<?>, DataContainerCodecPrototype<?>> newMismatched =
- ImmutableMap.<Class<?>, DataContainerCodecPrototype<?>>builderWithExpectedSize(expected.size() + 1)
- .putAll(expected)
- .put(childClass, prototype)
- .build();
+ final var newMismatched =
+ ImmutableMap.<Class<?>, DataContainerCodecPrototype<?>>builderWithExpectedSize(expected.size() + 1)
+ .putAll(expected)
+ .put(childClass, prototype)
+ .build();
- final var witness = (ImmutableMap<Class<?>, DataContainerCodecPrototype<?>>)
+ final var witness = (ImmutableMap<Class<?>, DataContainerCodecPrototype.Augmentation>)
MISMATCHED_AUGMENTED.compareAndExchangeRelease(this, expected, newMismatched);
if (witness == expected) {
LOG.trace("Cached mismatched augmentation {} -> {} in {}", childClass, prototype, this);
}
expected = witness;
- final DataContainerCodecPrototype<?> existing = expected.get(childClass);
+ final var existing = expected.get(childClass);
if (existing != null) {
LOG.trace("Using raced mismatched augmentation {} -> {} in {}", childClass, existing, this);
return existing;
return cls.equals(loaded);
}
- private @Nullable DataContainerCodecPrototype<?> loadAugmentPrototype(final AugmentRuntimeType augment) {
+ private DataContainerCodecPrototype.@Nullable Augmentation loadAugmentPrototype(final AugmentRuntimeType augment) {
// FIXME: in face of deviations this code should be looking at declared view, i.e. all possibilities at augment
// declaration site
- final var possibleChildren = augment.statement()
+ final var childPaths = augment.statement()
.streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class)
- .map(stmt -> (QName) stmt.argument())
+ .map(stmt -> new NodeIdentifier((QName) stmt.argument()))
.collect(ImmutableSet.toImmutableSet());
- if (possibleChildren.isEmpty()) {
+
+ final var it = childPaths.iterator();
+ if (!it.hasNext()) {
return null;
}
+ final var namespace = it.next().getNodeType().getModule();
final var factory = factory();
final GeneratedType javaType = augment.javaType();
throw new IllegalStateException(
"RuntimeContext references type " + javaType + " but failed to load its class", e);
}
-
- return DataContainerCodecPrototype.from(augClass, new AugmentationIdentifier(possibleChildren), augment,
- factory);
- }
-
- @SuppressWarnings("checkstyle:illegalCatch")
- protected final @NonNull D createBindingProxy(final DistinctNodeContainer<?, ?> node) {
- try {
- return (D) proxyConstructor.invokeExact(this, node);
- } catch (final Throwable e) {
- Throwables.throwIfUnchecked(e);
- throw new IllegalStateException(e);
- }
+ return new DataContainerCodecPrototype.Augmentation(augClass, namespace, augment, factory, childPaths);
}
+ @Override
@SuppressWarnings("unchecked")
Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
final DistinctNodeContainer<PathArgument, NormalizedNode> data) {
-
- @SuppressWarnings("rawtypes")
- final Map map = new HashMap<>();
-
- for (final NormalizedNode childValue : data.body()) {
- if (childValue instanceof AugmentationNode augDomNode) {
- final DataContainerCodecPrototype<?> codecProto = augmentationByYang.get(augDomNode.getIdentifier());
- if (codecProto != null) {
- final DataContainerCodecContext<?, ?> codec = codecProto.get();
- map.put(codec.getBindingClass(), codec.deserializeObject(augDomNode));
- }
+ /**
+ * Due to augmentation fields are at same level as direct children the data of each augmentation needs to be
+ * aggregated into own container node, then only deserialized using associated prototype.
+ */
+ final var builders = new HashMap<Class<?>, DataContainerNodeBuilder>();
+ for (var childValue : data.body()) {
+ final var bindingClass = yangToAugmentClass.get(childValue.getIdentifier());
+ if (bindingClass != null) {
+ builders.computeIfAbsent(bindingClass,
+ key -> Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(data.getIdentifier().getNodeType())))
+ .addChild(childValue);
}
}
- for (final DataContainerCodecPrototype<?> value : augmentationByStream.values()) {
- final var augClass = value.getBindingClass();
- // Do not perform duplicate deserialization if we have already created the corresponding augmentation
- // and validate whether the proposed augmentation is valid ion this instantiation context.
- if (!map.containsKey(augClass)
- && ((AugmentableRuntimeType) getType()).augments().contains(value.getType())) {
- final NormalizedNode augData = data.childByArg(value.getYangArg());
- if (augData != null) {
- // ... make sure we do not replace an e
- map.putIfAbsent(augClass, value.get().deserializeObject(augData));
- }
+ @SuppressWarnings("rawtypes")
+ final var map = new HashMap();
+ for (final var entry : builders.entrySet()) {
+ final var bindingClass = entry.getKey();
+ final var codecProto = augmentToPrototype.get(bindingClass);
+ if (codecProto != null) {
+ map.put(bindingClass, codecProto.get().deserializeObject(entry.getValue().build()));
}
}
return map;
}
@Override
- public InstanceIdentifier.PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
+ public InstanceIdentifier.PathArgument deserializePathArgument(final PathArgument arg) {
checkArgument(getDomPathArgument().equals(arg));
return bindingArg();
}
@Override
- public YangInstanceIdentifier.PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
+ public PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
checkArgument(bindingArg().equals(arg));
return getDomPathArgument();
}
+ @Override
+ public NormalizedNode serialize(final D data) {
+ return serializeImpl(data);
+ }
+
+ @Override
+ public final BindingNormalizedNodeCachingCodec<D> createCachingCodec(
+ final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
+ return createCachingCodec(this, cacheSpecifier);
+ }
}
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);
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;
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;
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;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
-final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCodecContext<D, BindingRuntimeTypes> {
+final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCodecContext<D, BindingRuntimeTypes>
+ implements BindingDataObjectCodecTreeNode<D> {
private final LoadingCache<Class<? extends DataObject>, DataContainerCodecContext<?, ?>> childrenByClass =
CacheBuilder.newBuilder().build(new CacheLoader<>() {
throw new UnsupportedOperationException("Could not create Binding data representation for root");
}
+ @Override
+ public NormalizedNode serialize(final D data) {
+ return serializeImpl(data);
+ }
+
ActionCodecContext getAction(final Class<? extends Action<?, ?, ?>> action) {
return getOrRethrow(actionsByClass, action);
}
final ActionRuntimeType schema = factory().getRuntimeContext().getActionDefinition(action);
return new ActionCodecContext(
DataContainerCodecPrototype.from(asClass(args[inputOffset], RpcInput.class), schema.input(),
- factory()).get(),
+ factory()).getDataObject(),
DataContainerCodecPrototype.from(asClass(args[outputOffset], RpcOutput.class), schema.output(),
- factory()).get());
+ factory()).getDataObject());
}
private static <T extends DataObject> Class<? extends T> asClass(final Type type, final Class<T> target) {
return super.bindingPathArgumentChild(arg, builder);
}
+ @Override
+ public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
+ final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
+ return createCachingCodec(this, cacheSpecifier);
+ }
+
private static Class<?> findCaseChoice(final Class<? extends DataObject> caseClass) {
for (var type : caseClass.getGenericInterfaces()) {
if (type instanceof Class<?> typeClass && ChoiceIn.class.isAssignableFrom(typeClass)) {
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;
@Before
public void before() {
- this.codecContext = new BindingCodecContext(getRuntimeContext());
+ codecContext = new BindingCodecContext(getRuntimeContext());
}
@SuppressWarnings("unchecked")
protected <T extends DataObject> T thereAndBackAgain(final InstanceIdentifier<T> path, final T data) {
- final Entry<YangInstanceIdentifier, NormalizedNode> there = codecContext.toNormalizedNode(path, data);
- final Entry<InstanceIdentifier<?>, DataObject> backAgain = codecContext.fromNormalizedNode(there.getKey(),
- there.getValue());
+ final var there = (NodeResult) codecContext.toNormalizedNode(path, data);
+ final var backAgain = codecContext.fromNormalizedNode(there.path(), there.node());
assertEquals(path, backAgain.getKey());
return (T) backAgain.getValue();
}
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;
@Test
public void testAnydataFromBinding() {
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
+ final var entry = (NodeResult) codecContext.toNormalizedNode(
InstanceIdentifier.create(Cont.class), new ContBuilder().setContAny(new FakeCont()).build());
- assertEquals(YangInstanceIdentifier.create(CONT_NODE_ID), entry.getKey());
- assertEquals(cont, entry.getValue());
+ assertEquals(YangInstanceIdentifier.create(CONT_NODE_ID), entry.path());
+ assertEquals(cont, entry.node());
}
private final class FakeData extends AbstractOpaqueData<DOMSource> {
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;
@Test
public void testAnyxmlFromBinding() {
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
+ final var entry = (NodeResult) codecContext.toNormalizedNode(
InstanceIdentifier.create(Cont.class), new ContBuilder().setContAny(new FakeCont()).build());
- assertEquals(YangInstanceIdentifier.create(CONT_NODE_ID), entry.getKey());
- assertEquals(cont, entry.getValue());
+ assertEquals(YangInstanceIdentifier.create(CONT_NODE_ID), entry.path());
+ assertEquals(cont, entry.node());
}
private final class FakeData extends AbstractOpaqueData<DOMSource> {
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;
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");
.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);
}
.addAugmentation(new TreeComplexUsesAugmentBuilder(createComplexData()).build())
.build();
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
- BA_TOP_LEVEL_LIST, manuallyConstructed);
- final TopLevelList deserialized = (TopLevelList) codecContext.fromNormalizedNode(entry.getKey(),
- entry.getValue()).getValue();
+ final var result = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, manuallyConstructed);
+ final TopLevelList deserialized =
+ (TopLevelList) codecContext.fromNormalizedNode(result.path(), result.node()).getValue();
assertEquals(manuallyConstructed, deserialized);
final TopLevelList copiedFromDeserialized = new TopLevelListBuilder(deserialized).build();
assertEquals(manuallyConstructed, copiedFromDeserialized);
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import java.util.Map.Entry;
import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
import org.opendaylight.yang.gen.v1.odl.test.binary.key.rev160101.BinaryList;
import org.opendaylight.yang.gen.v1.odl.test.binary.key.rev160101.BinaryListBuilder;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
public class BinaryKeyTest extends AbstractBindingCodecTest {
private final InstanceIdentifier<BinaryList> instanceIdentifier = InstanceIdentifier.create(BinaryList.class);
}
private BinaryList process(final BinaryList binaryList) {
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
- instanceIdentifier, binaryList);
- return (BinaryList) codecContext.fromNormalizedNode(entry.getKey(), entry.getValue()).getValue();
+ final var entry = (NodeResult) codecContext.toNormalizedNode(instanceIdentifier, binaryList);
+ return (BinaryList) codecContext.fromNormalizedNode(entry.path(), entry.node()).getValue();
}
}
.build())
.build();
- final BindingDataObjectCodecTreeNode<Module4Main> subtreeCodec = codecContext.getSubtreeCodec(
+ final var subtreeCodec = (BindingDataObjectCodecTreeNode<Module4Main>) codecContext.getSubtreeCodec(
InstanceIdentifier.create(Module4Main.class));
final NormalizedNode serialized = subtreeCodec.serialize(module4Main);
final NormalizedNode manualSerialized = subtreeCodec.serialize(manualModule4Main);
.build()))
.build();
- final BindingDataObjectCodecTreeNode<BooleanContainer> subtreeCodec = codecContext.getSubtreeCodec(
+ final var subtreeCodec = (BindingDataObjectCodecTreeNode<BooleanContainer>) codecContext.getSubtreeCodec(
InstanceIdentifier.create(BooleanContainer.class));
final NormalizedNode serializedInt = subtreeCodec.serialize(booleanContainerInt);
assertNotNull(serializedInt);
@Before
public void before() {
super.before();
- topNode = codecContext.getSubtreeCodec(TOP_PATH);
- contNode = codecContext.getSubtreeCodec(CONT_PATH);
+ topNode = (BindingDataObjectCodecTreeNode<Top>) codecContext.getSubtreeCodec(TOP_PATH);
+ contNode = (BindingDataObjectCodecTreeNode<Cont>) codecContext.getSubtreeCodec(CONT_PATH);
}
private static Map<TopLevelListKey, TopLevelList> createList(final int num) {
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;
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 {
.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);
}
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;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.top.level.list.ChoiceInList;
-import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.Empty;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
public class EmptyLeafTest extends AbstractBindingCodecTest {
private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class)
.child(TopLevelList.class, TOP_FOO_KEY).build();
- private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
- .augmentation(TreeLeafOnlyAugment.class);
- private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
- .augmentation(TreeComplexUsesAugment.class);
- private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
@Test
public void testCaseWithEmptyLeafType() {
.withKey(TOP_FOO_KEY)
.setChoiceInList(new EmptyLeafBuilder().setEmptyType(Empty.value()).build())
.build();
- final Entry<YangInstanceIdentifier, NormalizedNode> dom = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
+ final var dom = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
withEmptyCase);
- final Entry<InstanceIdentifier<?>, DataObject> readed = codecContext.fromNormalizedNode(dom.getKey(),
- dom.getValue());
+ final var readed = codecContext.fromNormalizedNode(dom.path(), dom.node());
final ChoiceInList list = ((TopLevelList) readed.getValue()).getChoiceInList();
assertTrue(list instanceof EmptyLeaf);
assertNotNull(((EmptyLeaf) list).getEmptyType());
// 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();
}
}
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;
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;
private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
.builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).build();
- private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY =
- BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class);
- private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES =
- BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class);
public static final QName TOP_QNAME = Top.QNAME;
public static final QName TOP_LEVEL_LIST_QNAME = QName.create(TOP_QNAME, "top-level-list");
public static final QName TOP_LEVEL_LIST_KEY = QName.create(TOP_QNAME, "name");
- private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
public static final YangInstanceIdentifier BI_TOP_PATH = YangInstanceIdentifier.of(TOP_QNAME);
public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_PATH = BI_TOP_PATH.node(TOP_LEVEL_LIST_QNAME);
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(
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;
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");
.child(TopLevelList.class, TOP_FOO_KEY).build();
private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST
.augmentation(TreeLeafOnlyAugment.class);
- private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
- .augmentation(TreeComplexUsesAugment.class);
- private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+ private static final InstanceIdentifier<ListViaUses> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST
+ .augmentation(TreeComplexUsesAugment.class).child(ListViaUses.class, new ListViaUsesKey("bar"));
@Test
public void testComplexAugmentationSerialization() {
- final YangInstanceIdentifier yangII = codecContext.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES);
- final PathArgument lastArg = yangII.getLastPathArgument();
- assertTrue("Last argument should be AugmentationIdentifier", lastArg instanceof AugmentationIdentifier);
- final InstanceIdentifier<?> bindingII = codecContext.fromYangInstanceIdentifier(yangII);
- assertEquals(BA_TREE_COMPLEX_USES, bindingII);
+ // augmentation child pointer fully recoverable after reverse transformation
+ final YangInstanceIdentifier yii = codecContext.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES);
+ final InstanceIdentifier<?> converted = codecContext.fromYangInstanceIdentifier(yii);
+ assertEquals(BA_TREE_COMPLEX_USES, converted);
}
@Test
public void testLeafOnlyAugmentationSerialization() {
- final PathArgument leafOnlyLastArg = codecContext.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY)
- .getLastPathArgument();
- assertTrue("Last argument should be AugmentationIdentifier", leafOnlyLastArg instanceof AugmentationIdentifier);
- assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
+ // augmentation only pointer translated to parent node being augmented,
+ // because of augmentation only have no corresponding yang identifier
+ final YangInstanceIdentifier yii = codecContext.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY);
+ final InstanceIdentifier<?> converted = codecContext.fromYangInstanceIdentifier(yii);
+ assertEquals(BA_TOP_LEVEL_LIST, converted);
}
@Test
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;
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");
@Test
public void testFromBinding() {
- final Entry<YangInstanceIdentifier, NormalizedNode> domDef = codecContext.toNormalizedNode(DEF_IID, DEF);
- Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(domDef.getKey(),
- domDef.getValue());
+ final var domDef = (NodeResult) codecContext.toNormalizedNode(DEF_IID, DEF);
+ Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(domDef.path(), domDef.node());
assertEquals(DEF_IID, entry.getKey());
final Def codecDef = (Def) entry.getValue();
- final Entry<YangInstanceIdentifier, NormalizedNode> domUse = codecContext.toNormalizedNode(USE_IID, USE);
- entry = codecContext.fromNormalizedNode(domUse.getKey(), domUse.getValue());
+ final var domUse = (NodeResult) codecContext.toNormalizedNode(USE_IID, USE);
+ entry = codecContext.fromNormalizedNode(domUse.path(), domUse.node());
assertEquals(USE_IID, entry.getKey());
final Use codecUse = (Use) entry.getValue();
Use copiedUse = new UseBuilder(DEF).build();
assertEquals(USE, copiedUse);
- assertEquals(domUse.getValue(), codecContext.toNormalizedNode(USE_IID, copiedUse).getValue());
+ assertEquals(domUse.node(), ((NodeResult) codecContext.toNormalizedNode(USE_IID, copiedUse)).node());
copiedUse = new UseBuilder(codecDef).build();
assertEquals(USE, copiedUse);
- assertEquals(domUse.getValue(), codecContext.toNormalizedNode(USE_IID, copiedUse).getValue());
+ assertEquals(domUse.node(), ((NodeResult) codecContext.toNormalizedNode(USE_IID, copiedUse)).node());
copiedUse = new UseBuilder(codecUse).build();
assertEquals(USE, copiedUse);
- assertEquals(domUse.getValue(), codecContext.toNormalizedNode(USE_IID, copiedUse).getValue());
+ assertEquals(domUse.node(), ((NodeResult) codecContext.toNormalizedNode(USE_IID, copiedUse)).node());
}
}
import static org.junit.Assert.assertEquals;
-import java.util.Map.Entry;
import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.ThirdParty;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexLeaves;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexLeavesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Int32StringUnion;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
-import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
public class LeafReferenceTest extends AbstractBindingCodecTest {
private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
- private static final InstanceIdentifier<TreeComplexLeaves> BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class)
- .child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeComplexLeaves.class).build();
+ private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class)
+ .child(TopLevelList.class, TOP_FOO_KEY).build();
@Test
public void testCaseWithLeafReferencesType() {
- final TreeComplexLeaves binding = new TreeComplexLeavesBuilder()
+ final TreeComplexLeaves augment = new TreeComplexLeavesBuilder()
.setIdentity(ThirdParty.VALUE)
.setIdentityRef(ThirdParty.VALUE)
.setSimpleType(10)
.setSchemaUnawareUnion(new Int32StringUnion("foo"))
.setSchemaUnawareUnionRef(new Int32StringUnion(10))
.build();
- final Entry<YangInstanceIdentifier, NormalizedNode> dom = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
- binding);
- final Entry<InstanceIdentifier<?>, DataObject> readed = codecContext.fromNormalizedNode(dom.getKey(),
- dom.getValue());
- final TreeComplexLeaves readedAugment = (TreeComplexLeaves) readed.getValue();
+ final TopLevelList list = new TopLevelListBuilder()
+ .setName("foo")
+ .addAugmentation(augment)
+ .build();
+ final var dom = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
+ list);
+ final var readed = codecContext.fromNormalizedNode(dom.path(), dom.node());
+ final var readAugment = ((TopLevelList) readed.getValue()).augmentation(TreeComplexLeaves.class);
- assertEquals(binding,readedAugment);
+ assertEquals(augment, readAugment);
}
}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import java.util.Map.Entry;
import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
import org.opendaylight.yang.gen.v1.bug8449.rev170516.Cont;
import org.opendaylight.yang.gen.v1.bug8449.rev170516.Cont.Ref;
import org.opendaylight.yang.gen.v1.bug8449.rev170516.ContBuilder;
import org.opendaylight.yang.gen.v1.bug8449.rev170516.ContInt32;
import org.opendaylight.yang.gen.v1.bug8449.rev170516.ContInt32.RefUnionInt32;
import org.opendaylight.yang.gen.v1.bug8449.rev170516.ContInt32Builder;
-import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.Uint32;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
public class LeafrefSerializeDeserializeTest extends AbstractBindingCodecTest {
@Test
public void listReferenceTest() {
final YangInstanceIdentifier contYII = YangInstanceIdentifier.builder().node(Cont.QNAME).build();
- final InstanceIdentifier<?> fromYangInstanceIdentifier = this.codecContext.fromYangInstanceIdentifier(contYII);
+ final InstanceIdentifier<?> fromYangInstanceIdentifier = codecContext.fromYangInstanceIdentifier(contYII);
assertNotNull(fromYangInstanceIdentifier);
final InstanceIdentifier<Cont> BA_II_CONT = InstanceIdentifier.builder(Cont.class).build();
final Ref refVal = new Ref("myvalue");
final Cont data = new ContBuilder().setRef(refVal).build();
- final Entry<YangInstanceIdentifier, NormalizedNode> normalizedNode =
- this.codecContext.toNormalizedNode(BA_II_CONT, data);
+ final var normalizedNode = (NodeResult) codecContext.toNormalizedNode(BA_II_CONT, data);
assertNotNull(normalizedNode);
- final Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode =
- this.codecContext.fromNormalizedNode(contYII, normalizedNode.getValue());
+ final var fromNormalizedNode = codecContext.fromNormalizedNode(contYII, normalizedNode.node());
assertNotNull(fromNormalizedNode);
final Cont value = (Cont) fromNormalizedNode.getValue();
assertEquals(refVal, value.getRef());
@Test
public void uint32LeafrefTest() {
final YangInstanceIdentifier contYII = YangInstanceIdentifier.builder().node(ContInt32.QNAME).build();
- final InstanceIdentifier<?> fromYangInstanceIdentifier = this.codecContext.fromYangInstanceIdentifier(contYII);
+ final InstanceIdentifier<?> fromYangInstanceIdentifier = codecContext.fromYangInstanceIdentifier(contYII);
assertNotNull(fromYangInstanceIdentifier);
final InstanceIdentifier<ContInt32> BA_II_CONT = InstanceIdentifier.builder(ContInt32.class).build();
final RefUnionInt32 refVal = new RefUnionInt32(Uint32.valueOf(5));
final ContInt32 data = new ContInt32Builder().setRefUnionInt32(refVal).build();
- final Entry<YangInstanceIdentifier, NormalizedNode> normalizedNode =
- this.codecContext.toNormalizedNode(BA_II_CONT, data);
+ final var normalizedNode = (NodeResult) codecContext.toNormalizedNode(BA_II_CONT, data);
assertNotNull(normalizedNode);
- final Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode =
- this.codecContext.fromNormalizedNode(contYII, normalizedNode.getValue());
+ final var fromNormalizedNode = codecContext.fromNormalizedNode(contYII, normalizedNode.node());
assertNotNull(fromNormalizedNode);
final ContInt32 value = (ContInt32) fromNormalizedNode.getValue();
assertEquals(refVal, value.getRefUnionInt32());
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;
.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());
}
}
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;
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;
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";
public static final QName CHOICE_CONTAINER_QNAME = ChoiceContainer.QNAME;
public static final QName CHOICE_IDENTIFIER_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "identifier");
public static final QName CHOICE_IDENTIFIER_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "id");
- public static final QName SIMPLE_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "simple-id");
public static final QName EXTENDED_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "extended-id");
private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
.builder(Top.class).child(TopLevelList.class, TOP_LEVEL_LIST_FOO_KEY).build();
- private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY =
- BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class);
- private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES =
- BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class);
public static final YangInstanceIdentifier BI_TOP_PATH = YangInstanceIdentifier.of(TOP_QNAME);
public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_PATH = BI_TOP_PATH.node(TOP_LEVEL_LIST_QNAME);
@Test
public void containerToNormalized() {
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
- InstanceIdentifier.create(Top.class), top());
- final ContainerNode topNormalized = getEmptyTop();
- assertEquals(topNormalized, entry.getValue());
+ final var entry = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.create(Top.class), top());
+ assertEquals(getEmptyTop(), entry.node());
}
@Test
public void containerFromNormalized() {
- final ContainerNode topNormalized = getEmptyTop();
- final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(BI_TOP_PATH,
- topNormalized);
+ final var entry = codecContext.fromNormalizedNode(BI_TOP_PATH, getEmptyTop());
assertEquals(top(), entry.getValue());
}
@Test
public void equalsWithAugment() {
- final ContainerNode topNormalizedWithAugments = getNormalizedTopWithAugments(augmentationBuilder()
- .withNodeIdentifier(new AugmentationIdentifier(Set.of(AGUMENT_STRING_Q)))
- .withChild(leafNode(AGUMENT_STRING_Q, AUGMENT_STRING_VALUE))
- .build());
+ final ContainerNode topNormalizedWithAugments =
+ getNormalizedTopWithChildren(leafNode(AGUMENT_STRING_Q, AUGMENT_STRING_VALUE));
final ContainerNode topNormalized = getEmptyTop();
final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(BI_TOP_PATH,
@Test
public void equalsWithMultipleAugments() {
- final ContainerNode topNormalizedWithAugments = getNormalizedTopWithAugments(
- augmentationBuilder()
- .withNodeIdentifier(new AugmentationIdentifier(Set.of(AGUMENT_STRING_Q)))
- .withChild(leafNode(AGUMENT_STRING_Q, AUGMENT_STRING_VALUE))
- .build(),
- augmentationBuilder()
- .withNodeIdentifier(new AugmentationIdentifier(Set.of(AUGMENT_INT_Q)))
- .withChild(leafNode(AUGMENT_INT_Q, AUGMENT_INT_VALUE))
- .build());
-
+ final ContainerNode topNormalizedWithAugments = getNormalizedTopWithChildren(
+ leafNode(AGUMENT_STRING_Q, AUGMENT_STRING_VALUE),
+ leafNode(AUGMENT_INT_Q, AUGMENT_INT_VALUE));
final Entry<InstanceIdentifier<?>, DataObject> entryWithAugments = codecContext.fromNormalizedNode(BI_TOP_PATH,
topNormalizedWithAugments);
Top topWithAugments = topWithAugments(Map.of(
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();
@Test
public void listWithKeysToNormalized() {
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
- BA_TOP_LEVEL_LIST, topLevelList(TOP_LEVEL_LIST_FOO_KEY));
- final MapEntryNode topLevelListNormalized = mapEntryBuilder()
+ final var entry = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
+ topLevelList(TOP_LEVEL_LIST_FOO_KEY));
+ assertEquals(mapEntryBuilder()
.withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME,
TOP_LEVEL_LIST_FOO_KEY_VALUE))
.withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
- .build();
- assertEquals(topLevelListNormalized, entry.getValue());
+ .build(),
+ entry.node());
}
@Test
public void listWithKeysFromNormalized() {
- final MapEntryNode topLevelListNormalized = mapEntryBuilder()
+ final var entry = codecContext.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, mapEntryBuilder()
.withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME,
TOP_LEVEL_LIST_FOO_KEY_VALUE))
.withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
- .build();
- final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(
- BI_TOP_LEVEL_LIST_FOO_PATH, topLevelListNormalized);
+ .build());
assertEquals(topLevelList(TOP_LEVEL_LIST_FOO_KEY), entry.getValue());
}
@Test
public void leafOnlyAugmentationToNormalized() {
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
- BA_TREE_LEAF_ONLY, new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build());
- final AugmentationNode augmentationNode = augmentationBuilder()
- .withNodeIdentifier(new AugmentationIdentifier(Set.of(SIMPLE_VALUE_QNAME)))
- .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue"))
- .build();
- assertEquals(augmentationNode, entry.getValue());
+ final var entry = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
+ topLevelList(TOP_LEVEL_LIST_FOO_KEY,
+ new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build()));
+ assertEquals(mapEntryBuilder()
+ .withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME,
+ TOP_LEVEL_LIST_FOO_KEY_VALUE))
+ .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+ .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue")) // augmentation child
+ .build(),
+ entry.node());
}
@Test
public void leafOnlyAugmentationFromNormalized() {
- final AugmentationIdentifier augmentationId = new AugmentationIdentifier(Set.of(SIMPLE_VALUE_QNAME));
- final AugmentationNode augmentationNode = augmentationBuilder()
- .withNodeIdentifier(augmentationId)
- .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue"))
- .build();
- final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(
- BI_TOP_LEVEL_LIST_FOO_PATH.node(augmentationId), augmentationNode);
- assertEquals(new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build(), entry.getValue());
+ final var entry = codecContext.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, mapEntryBuilder()
+ .withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME,
+ TOP_LEVEL_LIST_FOO_KEY_VALUE))
+ .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+ .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue")) // augmentation child
+ .build());
+ assertEquals(
+ topLevelList(TOP_LEVEL_LIST_FOO_KEY,
+ new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build()),
+ entry.getValue());
}
@Test
public void orderedleafListToNormalized() {
- Top top = new TopBuilder().setTopLevelOrderedLeafList(List.of("foo")).build();
-
- Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
- InstanceIdentifier.create(Top.class), top);
- ContainerNode containerNode = containerBuilder()
+ final var entry = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.create(Top.class),
+ new TopBuilder().setTopLevelOrderedLeafList(List.of("foo")).build());
+ assertEquals(containerBuilder()
.withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
.withChild(orderedLeafSetBuilder()
.withNodeIdentifier(new NodeIdentifier(TOP_LEVEL_ORDERED_LEAF_LIST_QNAME))
.withValue("foo")
.build())
.build())
- .build();
- assertEquals(containerNode, entry.getValue());
+ .build(),
+ entry.node());
}
@Test
public void leafListToNormalized() {
- final Top top = new TopBuilder().setTopLevelLeafList(Set.of("foo")).build();
-
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
- InstanceIdentifier.create(Top.class), top);
- final ContainerNode containerNode = containerBuilder()
+ final var entry = (NodeResult) codecContext.toNormalizedNode(
+ InstanceIdentifier.create(Top.class), new TopBuilder().setTopLevelLeafList(Set.of("foo")).build());
+ assertEquals(containerBuilder()
.withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
.withChild(leafSetBuilder()
.withNodeIdentifier(new NodeIdentifier(TOP_LEVEL_LEAF_LIST_QNAME))
.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))
.withValue("foo")
.build())
.build())
- .build();
- final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(BI_TOP_PATH,
- topWithLeafList);
- final Top top = new TopBuilder().setTopLevelLeafList(Set.of("foo")).build();
- assertEquals(top, entry.getValue());
+ .build());
+ assertEquals(new TopBuilder().setTopLevelLeafList(Set.of("foo")).build(), entry.getValue());
}
@Test
public void orderedLeafListFromNormalized() {
- ContainerNode topWithLeafList = containerBuilder()
+ final var entry = codecContext.fromNormalizedNode(BI_TOP_PATH, containerBuilder()
.withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
.withChild(orderedLeafSetBuilder()
.withNodeIdentifier(new NodeIdentifier(TOP_LEVEL_ORDERED_LEAF_LIST_QNAME))
.withNodeIdentifier(new NodeWithValue<>(TOP_LEVEL_ORDERED_LEAF_LIST_QNAME, "foo"))
.withValue("foo").build())
.build())
- .build();
- Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(BI_TOP_PATH, topWithLeafList);
- Top top = new TopBuilder().setTopLevelOrderedLeafList(List.of("foo")).build();
- assertEquals(top, entry.getValue());
+ .build());
+ assertEquals(new TopBuilder().setTopLevelOrderedLeafList(List.of("foo")).build(), entry.getValue());
}
@Test
public void choiceToNormalized() {
- final ChoiceContainer choiceContainerBA = new ChoiceContainerBuilder().setIdentifier(new ExtendedBuilder()
- .setExtendedId(new ExtendedIdBuilder().setId("identifier_value").build()).build()).build();
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(
- InstanceIdentifier.create(ChoiceContainer.class), choiceContainerBA);
- final ContainerNode choiceContainer = containerBuilder()
+ final var entry = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.create(ChoiceContainer.class),
+ new ChoiceContainerBuilder()
+ .setIdentifier(new ExtendedBuilder()
+ .setExtendedId(new ExtendedIdBuilder().setId("identifier_value").build())
+ .build())
+ .build());
+ assertEquals(containerBuilder()
.withNodeIdentifier(new NodeIdentifier(CHOICE_CONTAINER_QNAME))
.withChild(choiceBuilder()
.withNodeIdentifier(new NodeIdentifier(CHOICE_IDENTIFIER_QNAME))
.withChild(leafNode(CHOICE_IDENTIFIER_ID_QNAME, "identifier_value"))
.build())
.build())
- .build();
- assertEquals(choiceContainer, entry.getValue());
+ .build(),
+ entry.node());
}
@Test
@Test
public void orderedLisToNormalized() {
- final TopLevelList topLevelList = new TopLevelListBuilder()
+ final var entry = (NodeResult) codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST, new TopLevelListBuilder()
.withKey(TOP_LEVEL_LIST_FOO_KEY)
.setNestedList(List.of(
new NestedListBuilder().withKey(new NestedListKey("foo")).build(),
new NestedListBuilder().withKey(new NestedListKey("bar")).build()))
- .build();
- final Entry<YangInstanceIdentifier, NormalizedNode> entry = codecContext.toNormalizedNode(BA_TOP_LEVEL_LIST,
- topLevelList);
- final MapEntryNode foo = mapEntryBuilder().withNodeIdentifier(NodeIdentifierWithPredicates.of(
- TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
- .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
- .withChild(orderedMapBuilder()
- .withNodeIdentifier(new NodeIdentifier(NESTED_LIST_QNAME))
- .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo"))
- .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar")).build()).build();
- assertEquals(foo, entry.getValue());
+ .build());
+ assertEquals(mapEntryBuilder()
+ .withNodeIdentifier(NodeIdentifierWithPredicates.of(TOP_LEVEL_LIST_QNAME,
+ TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+ .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
+ .withChild(orderedMapBuilder()
+ .withNodeIdentifier(new NodeIdentifier(NESTED_LIST_QNAME))
+ .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo"))
+ .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar"))
+ .build())
+ .build(),
+ entry.node());
}
@Test
public void orderedLisFromNormalized() {
- final MapEntryNode foo = mapEntryBuilder()
+ final var entry = codecContext.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, mapEntryBuilder()
.withNodeIdentifier(NodeIdentifierWithPredicates.of(
TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
.withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE))
.withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo"))
.withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar"))
.build())
- .build();
- final Entry<InstanceIdentifier<?>, DataObject> entry = codecContext.fromNormalizedNode(
- BI_TOP_LEVEL_LIST_FOO_PATH, foo);
- final TopLevelList topLevelList = new TopLevelListBuilder()
+ .build());
+ assertEquals(new TopLevelListBuilder()
.withKey(TOP_LEVEL_LIST_FOO_KEY)
.setNestedList(List.of(
new NestedListBuilder().withKey(new NestedListKey("foo")).build(),
new NestedListBuilder().withKey(new NestedListKey("bar")).build()))
- .build();
- assertEquals(topLevelList, entry.getValue());
+ .build(),
+ entry.getValue());
}
@Test
final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container");
final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf");
- final AugmentationIdentifier aug1Id = new AugmentationIdentifier(Set.of(augmentChoice1QName));
- final AugmentationIdentifier aug2Id = new AugmentationIdentifier(Set.of(augmentChoice2QName));
final NodeIdentifier augmentChoice1Id = new NodeIdentifier(augmentChoice1QName);
final NodeIdentifier augmentChoice2Id = new NodeIdentifier(augmentChoice2QName);
final NodeIdentifier containerId = new NodeIdentifier(containerQName);
- final TopBuilder tBuilder = new TopBuilder();
- final TopChoiceAugment1Builder tca1Builder = new TopChoiceAugment1Builder();
- final Case1Builder c1Builder = new Case1Builder();
- final TopChoiceAugment2Builder tca2Builder = new TopChoiceAugment2Builder();
- final Case11Builder c11Builder = new Case11Builder();
- final Case11ChoiceCaseContainerBuilder cccc1Builder = new Case11ChoiceCaseContainerBuilder();
- cccc1Builder.setCase11ChoiceCaseLeaf("leaf-value");
- c11Builder.setCase11ChoiceCaseContainer(cccc1Builder.build());
- tca2Builder.setAugmentChoice2(c11Builder.build());
- c1Builder.addAugmentation(tca2Builder.build());
- tca1Builder.setAugmentChoice1(c1Builder.build());
- tBuilder.addAugmentation(tca1Builder.build());
- final Top top = tBuilder.build();
-
- final Entry<YangInstanceIdentifier, NormalizedNode> biResult = codecContext.toNormalizedNode(
- InstanceIdentifier.create(Top.class), top);
-
- final NormalizedNode topNormalized = containerBuilder()
+ final Top top = new TopBuilder().addAugmentation(
+ // top is augmented with choice1 having case1
+ new TopChoiceAugment1Builder().setAugmentChoice1(
+ new Case1Builder().addAugmentation(
+ // case1 is augmented with choice2 having case11 (with container having leaf)
+ new TopChoiceAugment2Builder().setAugmentChoice2(
+ new Case11Builder().setCase11ChoiceCaseContainer(
+ new Case11ChoiceCaseContainerBuilder()
+ .setCase11ChoiceCaseLeaf("leaf-value").build()
+ ).build()
+ ).build()
+ ).build()
+ ).build()
+ ).build();
+
+ final var biResult = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.create(Top.class), top);
+
+ final var topNormalized = containerBuilder()
.withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
- .withChild(augmentationBuilder().withNodeIdentifier(aug1Id)
- .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id)
- .withChild(augmentationBuilder().withNodeIdentifier(aug2Id)
- .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id)
- .withChild(containerBuilder().withNodeIdentifier(containerId)
- .withChild(leafNode(leafQName, "leaf-value"))
- .build())
- .build())
- .build())
+ .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id) // choice 1
+ .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id) // choice 2
+ .withChild(containerBuilder().withNodeIdentifier(containerId)
+ .withChild(leafNode(leafQName, "leaf-value")).build())
.build())
.build())
.build();
- assertEquals(BI_TOP_PATH, biResult.getKey());
- assertEquals(topNormalized, biResult.getValue());
+ assertEquals(BI_TOP_PATH, biResult.path());
+ assertEquals(topNormalized, biResult.node());
- final Entry<InstanceIdentifier<?>, DataObject> baResult = codecContext.fromNormalizedNode(BI_TOP_PATH,
- topNormalized);
+ final var baResult = codecContext.fromNormalizedNode(BI_TOP_PATH, topNormalized);
assertEquals(InstanceIdentifier.create(Top.class), baResult.getKey());
assertEquals(top, baResult.getValue());
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
-import java.util.Map;
import java.util.Set;
import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
import org.opendaylight.yang.gen.v1.mdsal426.norev.BarCont;
import org.opendaylight.yang.gen.v1.mdsal426.norev.BarContBuilder;
import org.opendaylight.yang.gen.v1.mdsal426.norev.BooleanCont;
import org.opendaylight.yang.gen.v1.mdsal426.norev.BooleanContBuilder;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
public class SpecializingLeafrefTest extends AbstractBindingCodecTest {
private static final InstanceIdentifier<BooleanCont> BOOLEAN_CONT_II = InstanceIdentifier
public void specifiedBooleanLeafTest() {
final BooleanCont booleanCont = new BooleanContBuilder().setIsFoo(true).build();
- final Map.Entry<YangInstanceIdentifier, NormalizedNode> res = codecContext
- .toNormalizedNode(BOOLEAN_CONT_II, booleanCont);
+ final var res = (NodeResult) codecContext.toNormalizedNode(BOOLEAN_CONT_II, booleanCont);
- final BooleanCont booleanContBinding = (BooleanCont)codecContext
- .fromNormalizedNode(res.getKey(), res.getValue()).getValue();
+ final var booleanContBinding = (BooleanCont) codecContext.fromNormalizedNode(res.path(), res.node()).getValue();
assertTrue(booleanContBinding.getIsFoo());
}
public void specifiedCommonLeafTest() {
final BarCont barCont = new BarContBuilder().setLeaf2("foo").build();
- final Map.Entry<YangInstanceIdentifier, NormalizedNode> res = codecContext
- .toNormalizedNode(BAR_CONT_II, barCont);
+ final var res = (NodeResult) codecContext.toNormalizedNode(BAR_CONT_II, barCont);
- final BarCont booleanContBinding = (BarCont)codecContext
- .fromNormalizedNode(res.getKey(), res.getValue()).getValue();
+ final var booleanContBinding = (BarCont) codecContext.fromNormalizedNode(res.path(), res.node()).getValue();
assertEquals(booleanContBinding.getLeaf2(), "foo");
}
final Set<String> testSet = Set.of("test");
final BarCont barCont = new BarContBuilder().setLeafList1(testSet).build();
- final Map.Entry<YangInstanceIdentifier, NormalizedNode> res = codecContext
- .toNormalizedNode(BAR_CONT_II, barCont);
+ final var res = (NodeResult) codecContext.toNormalizedNode(BAR_CONT_II, barCont);
- final BarCont barContAfterConverting = (BarCont)codecContext
- .fromNormalizedNode(res.getKey(), res.getValue()).getValue();
+ final var barContAfterConverting = (BarCont) codecContext.fromNormalizedNode(res.path(), res.node()).getValue();
assertEquals(barContAfterConverting.getLeafList1(), testSet);
}
import static org.junit.Assert.assertEquals;
-import java.util.Map.Entry;
import org.junit.Test;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer.NodeResult;
import org.opendaylight.yang.gen.v1.bug8903.rev170829.DefaultPolicy;
import org.opendaylight.yang.gen.v1.bug8903.rev170829.DefaultPolicyBuilder;
import org.opendaylight.yang.gen.v1.bug8903.rev170829.PolicyLoggingFlag;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.typedef.empty.rev170829.TestCont;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.typedef.empty.rev170829.TestContBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.typedef.empty.rev170829.TypedefEmpty;
-import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.Empty;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-
public class TypedefTest extends AbstractBindingCodecTest {
-
private static final InstanceIdentifier<DefaultPolicy> BA_DEFAULT_POLICY =
InstanceIdentifier.builder(DefaultPolicy.class).build();
private static final InstanceIdentifier<TestCont> BA_TEST_CONT =
.setAction2(new PolicyLoggingFlag(false))
.setAction3(true)
.build();
- final Entry<YangInstanceIdentifier, NormalizedNode> dom =
- codecContext.toNormalizedNode(BA_DEFAULT_POLICY, binding);
- final Entry<InstanceIdentifier<?>, DataObject> readed =
- codecContext.fromNormalizedNode(dom.getKey(),dom.getValue());
-
- assertEquals(binding,readed.getValue());
+ final var dom = (NodeResult) codecContext.toNormalizedNode(BA_DEFAULT_POLICY, binding);
+ final var readed = codecContext.fromNormalizedNode(dom.path(),dom.node());
+ assertEquals(binding, readed.getValue());
}
@Test
.setEmptyLeaf2(new TypedefEmpty(Empty.value()))
.setEmptyLeaf3(Empty.value())
.build();
- final Entry<YangInstanceIdentifier, NormalizedNode> dom =
- codecContext.toNormalizedNode(BA_TEST_CONT, binding);
- final Entry<InstanceIdentifier<?>, DataObject> readed =
- codecContext.fromNormalizedNode(dom.getKey(),dom.getValue());
-
- assertEquals(binding,readed.getValue());
+ final var dom = (NodeResult) codecContext.toNormalizedNode(BA_TEST_CONT, binding);
+ final var readed = codecContext.fromNormalizedNode(dom.path(),dom.node());
+ assertEquals(binding, readed.getValue());
}
}
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;
.withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
.withChild(ImmutableNodes.leafNode(NodeIdentifier.create(UNION_LEAF_QNAME), identityQname))
.build();
- final Map.Entry<InstanceIdentifier<?>, DataObject> translated =
- codecContext.fromNormalizedNode(YangInstanceIdentifier.create(NodeIdentifier.create(TOP_QNAME)), top);
+ final var translated = codecContext.fromNormalizedNode(YangInstanceIdentifier.of(TOP_QNAME), top);
assertNotNull(translated);
assertNotNull(translated.getValue());
assertTrue(translated.getValue() instanceof Top);
// create binding instance with identity
final Top topContainer = new TopBuilder().setTestUnionLeaf(chosenIdentity).build();
// translate via codec into NN
- final Map.Entry<YangInstanceIdentifier, NormalizedNode> translated =
- codecContext.toNormalizedNode(InstanceIdentifier.builder(Top.class).build(), topContainer);
+ final var translated = (NodeResult) codecContext.toNormalizedNode(InstanceIdentifier.builder(Top.class).build(),
+ topContainer);
assertNotNull(translated);
// verify translation worked
- final NormalizedNode translatedNN = translated.getValue();
+ final var translatedNN = translated.node();
assertNotNull(translatedNN);
// verify the union leaf is present
// verify the leaf is the correct identity
return new TopLevelListBuilder().withKey(key).build();
}
- public static TopLevelList topLevelList(final TopLevelListKey key, final TreeComplexUsesAugment augment) {
+ public static TopLevelList topLevelList(final TopLevelListKey key, final Augmentation<TopLevelList> augment) {
return new TopLevelListBuilder().withKey(key).addAugmentation(augment).build();
}
LegacyContentBuilder(final YangLibraryContentBuilderImpl delegate, final BindingCodecTree codecTree) {
this.delegate = requireNonNull(delegate);
- legacyCodec = verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)));
+ legacyCodec = (BindingDataObjectCodecTreeNode<ModulesState>)
+ verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)));
}
@Override
YangLibraryContentBuilderImpl(final BindingCodecTree codecTree) {
this.codecTree = Objects.requireNonNull(codecTree);
- codec = verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)));
+ codec = (BindingDataObjectCodecTreeNode<YangLibrary>)
+ verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)));
}
@Override
generator.generateTypeMapping(context), snapshot));
identityCodec = codecTree.getIdentityCodec();
- codec = verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)));
- legacyCodec = verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)));
+ codec = (BindingDataObjectCodecTreeNode<YangLibrary>)
+ verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class)));
+ legacyCodec = (BindingDataObjectCodecTreeNode<ModulesState>)
+ verifyNotNull(codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class)));
}
@Override
public class LegacyYangLibraryFormatTest extends AbstractYangLibraryTest {
@Test
public void testLegacyFormat() {
- final BindingDataObjectCodecTreeNode<ModulesState> legacyCodec =
- codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class));
+ final var legacyCodec = (BindingDataObjectCodecTreeNode<ModulesState>)
+ codecTree.getSubtreeCodec(InstanceIdentifier.create(ModulesState.class));
final Optional<ContainerNode> legacyContent = yangLib.newContentBuilder()
.defaultContext(runtimeContext.getEffectiveModelContext())
public class YangLibrarySupportTest extends AbstractYangLibraryTest {
@Test
public void testFormatSchema() {
- final BindingDataObjectCodecTreeNode<YangLibrary> codec =
- codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class));
+ final var codec = (BindingDataObjectCodecTreeNode<YangLibrary>)
+ codecTree.getSubtreeCodec(InstanceIdentifier.create(YangLibrary.class));
final ContainerNode nonLegacyContent = yangLib.newContentBuilder()
.defaultContext(runtimeContext.getEffectiveModelContext()).formatYangLibraryContent();