X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-dom-codec%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fdom%2Fcodec%2Fimpl%2FDataObjectCodecContext.java;h=8a45102c3b03cb5f08f6bafe6bc6d9cea82c0702;hb=f3be50ed801e5de305eb46b824e6bc3c6075e4bc;hp=b0bde73a713c405bd4ac7bb78d0409cece4c9280;hpb=5b45057b26a4c86487f569b940f553c977cb3798;p=mdsal.git diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java index b0bde73a71..8a45102c3b 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java @@ -8,14 +8,14 @@ package org.opendaylight.mdsal.binding.dom.codec.impl; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Verify.verify; -import static com.google.common.base.Verify.verifyNotNull; import com.google.common.annotations.Beta; import com.google.common.base.Throwables; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.ImmutableSet; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -24,39 +24,27 @@ import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -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.JavaTypeName; 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.ChoiceRuntimeType; import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType; -import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections; import org.opendaylight.yangtools.yang.binding.Augmentable; import org.opendaylight.yangtools.yang.binding.Augmentation; -import org.opendaylight.yangtools.yang.binding.DataContainer; +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.binding.InstanceIdentifier.Item; -import org.opendaylight.yangtools.yang.binding.OpaqueObject; -import org.opendaylight.yangtools.yang.binding.contract.Naming; 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.DataContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus; -import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +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; @@ -65,13 +53,15 @@ import org.slf4j.LoggerFactory; * This class is an implementation detail. It is public only due to technical reasons and may change at any time. */ @Beta -public abstract class DataObjectCodecContext - extends DataContainerCodecContext { +public abstract sealed class DataObjectCodecContext + extends AbstractDataObjectCodecContext implements BindingDataObjectCodecTreeNode + permits CaseCodecContext, ContainerLikeCodecContext, ListCodecContext, NotificationCodecContext { private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class); + private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, - DataObjectCodecContext.class, DistinctNodeContainer.class); + AbstractDataObjectCodecContext.class, DataContainerNode.class); private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, - DataObjectCodecContext.class, DistinctNodeContainer.class); + DataObjectCodecContext.class, DataContainerNode.class); private static final VarHandle MISMATCHED_AUGMENTED; static { @@ -83,127 +73,56 @@ public abstract class DataObjectCodecContext leafChild; - private final ImmutableMap byYang; - private final ImmutableMap, DataContainerCodecPrototype> byStreamClass; - private final ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; - private final ImmutableMap> augmentationByYang; - private final ImmutableMap, DataContainerCodecPrototype> augmentationByStream; + private final ImmutableMap, AugmentationCodecPrototype> augmentToPrototype; + private final ImmutableMap> yangToAugmentClass; private final @NonNull Class> generatedClass; private final MethodHandle proxyConstructor; // Note this the content of this field depends only of invariants expressed as this class's fields or // BindingRuntimeContext. It is only accessed via MISMATCHED_AUGMENTED above. @SuppressWarnings("unused") - private volatile ImmutableMap, DataContainerCodecPrototype> mismatchedAugmented = ImmutableMap.of(); + @SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "https://github.com/spotbugs/spotbugs/issues/2749") + private volatile ImmutableMap, CommonDataObjectCodecPrototype> mismatchedAugmented = ImmutableMap.of(); - DataObjectCodecContext(final DataContainerCodecPrototype prototype) { - this(prototype, null); + DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype) { + this(prototype, CodecItemFactory.of()); } - DataObjectCodecContext(final DataContainerCodecPrototype prototype, final Method keyMethod) { - super(prototype); - - final Class bindingClass = getBindingClass(); - - final ImmutableMap tmpLeaves = factory().getLeafNodes(bindingClass, - getType().statement()); - final Map, Method> clsToMethod = getChildrenClassToMethod(bindingClass); - - final Map byYangBuilder = new HashMap<>(); - final Map, DataContainerCodecPrototype> byStreamClassBuilder = new HashMap<>(); - final Map, DataContainerCodecPrototype> byBindingArgClassBuilder = new HashMap<>(); - - // Adds leaves to mapping - final Builder leafChildBuilder = - ImmutableMap.builderWithExpectedSize(tmpLeaves.size()); - for (final ValueNodeCodecContext leaf : tmpLeaves.values()) { - leafChildBuilder.put(leaf.getSchema().getQName().getLocalName(), leaf); - byYangBuilder.put(leaf.getDomPathArgument(), leaf); - } - this.leafChild = leafChildBuilder.build(); - - final Map, PropertyInfo> daoProperties = new HashMap<>(); - for (final Entry, Method> childDataObj : clsToMethod.entrySet()) { - final Method method = childDataObj.getValue(); - verify(!method.isDefault(), "Unexpected default method %s in %s", method, bindingClass); - - final Class retClass = childDataObj.getKey(); - if (OpaqueObject.class.isAssignableFrom(retClass)) { - // Filter OpaqueObjects, they are not containers - continue; - } - - // Record getter method - daoProperties.put(retClass, new PropertyInfo.Getter(method)); - - final DataContainerCodecPrototype childProto = loadChildPrototype(retClass); - byStreamClassBuilder.put(childProto.getBindingClass(), childProto); - byYangBuilder.put(childProto.getYangArg(), childProto); - - // FIXME: It really feels like we should be specializing DataContainerCodecPrototype so as to ditch - // createInstance() and then we could do an instanceof check instead. - if (childProto.getType() instanceof ChoiceRuntimeType) { - final ChoiceNodeCodecContext choice = (ChoiceNodeCodecContext) childProto.get(); - for (final Class cazeChild : choice.getCaseChildrenClasses()) { - byBindingArgClassBuilder.put(cazeChild, childProto); - } - } - } + DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, final CodecItemFactory itemFactory) { + this(prototype, new DataContainerAnalysis<>(prototype, itemFactory), null); + } - // Find all non-default nonnullFoo() methods and update the corresponding property info - for (var entry : getChildrenClassToNonnullMethod(bindingClass).entrySet()) { - final var method = entry.getValue(); - if (!method.isDefault()) { - daoProperties.compute(entry.getKey(), (key, value) -> new PropertyInfo.GetterAndNonnull( - verifyNotNull(value, "No getter for %s", key).getterMethod(), method)); - } - } + DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, final Method keyMethod) { + this(prototype, new DataContainerAnalysis<>(prototype, CodecItemFactory.of()), keyMethod); + } - this.byYang = ImmutableMap.copyOf(byYangBuilder); - this.byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder); + private DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, + final DataContainerAnalysis analysis, final Method keyMethod) { + super(prototype, analysis); - // Slight footprint optimization: we do not want to copy byStreamClass, as that would force its entrySet view - // to be instantiated. Furthermore the two maps can easily end up being equal -- hence we can reuse - // byStreamClass for the purposes of both. - byBindingArgClassBuilder.putAll(byStreamClassBuilder); - this.byBindingArgClass = byStreamClassBuilder.equals(byBindingArgClassBuilder) ? this.byStreamClass - : ImmutableMap.copyOf(byBindingArgClassBuilder); + final var bindingClass = getBindingClass(); + // Final bits: generate the appropriate class, As a side effect we identify what Augmentations are possible final List possibleAugmentations; + final var loader = prototype().contextFactory().getLoader(); if (Augmentable.class.isAssignableFrom(bindingClass)) { // Verify we have the appropriate backing runtimeType - final var type = getType(); - verify(type instanceof AugmentableRuntimeType, "Unexpected type %s backing augmenable %s", type, - bindingClass); - possibleAugmentations = ((AugmentableRuntimeType) type).augments(); - generatedClass = CodecDataObjectGenerator.generateAugmentable(prototype.getFactory().getLoader(), - bindingClass, tmpLeaves, daoProperties, keyMethod); + final var runtimeType = prototype.runtimeType(); + if (!(runtimeType instanceof AugmentableRuntimeType augmentableRuntimeType)) { + throw new VerifyException( + "Unexpected type %s backing augmenable %s".formatted(runtimeType, bindingClass)); + } + + possibleAugmentations = augmentableRuntimeType.augments(); + generatedClass = CodecDataObjectGenerator.generateAugmentable(loader, bindingClass, analysis.leafContexts, + analysis.daoProperties, keyMethod); } else { possibleAugmentations = List.of(); - generatedClass = CodecDataObjectGenerator.generate(prototype.getFactory().getLoader(), bindingClass, - tmpLeaves, daoProperties, keyMethod); + generatedClass = CodecDataObjectGenerator.generate(loader, bindingClass, analysis.leafContexts, + analysis.daoProperties, keyMethod); } - // Iterate over all possible augmentations, indexing them as needed - final Map> augByYang = new HashMap<>(); - final Map, DataContainerCodecPrototype> augByStream = new HashMap<>(); - for (final AugmentRuntimeType augment : possibleAugmentations) { - final DataContainerCodecPrototype augProto = loadAugmentPrototype(augment); - if (augProto != null) { - final PathArgument augYangArg = augProto.getYangArg(); - if (augByYang.putIfAbsent(augYangArg, augProto) == null) { - LOG.trace("Discovered new YANG mapping {} -> {} in {}", augYangArg, augProto, this); - } - final Class augBindingClass = augProto.getBindingClass(); - if (augByStream.putIfAbsent(augBindingClass, augProto) == null) { - LOG.trace("Discovered new class mapping {} -> {} in {}", augBindingClass, augProto, this); - } - } - } - augmentationByYang = ImmutableMap.copyOf(augByYang); - augmentationByStream = ImmutableMap.copyOf(augByStream); - + // All done: acquire the constructor: it is supposed to be public final MethodHandle ctor; try { ctor = MethodHandles.publicLookup().findConstructor(generatedClass, CONSTRUCTOR_TYPE); @@ -212,146 +131,78 @@ public abstract class DataObjectCodecContext DataContainerCodecContext streamChild(final Class childClass) { - return (DataContainerCodecContext) childNonNull(streamChildPrototype(childClass), childClass, - "Child %s is not valid child of %s", getBindingClass(), childClass).get(); - } - 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); + // Deal with augmentations, which are not something we analysis provides + final var augPathToBinding = new HashMap>(); + final var augClassToProto = new HashMap, AugmentationCodecPrototype>(); + for (var augment : possibleAugmentations) { + final var augProto = loadAugmentPrototype(augment); + if (augProto != null) { + final var augBindingClass = augProto.javaClass(); + for (var childPath : augProto.getChildArgs()) { + augPathToBinding.putIfAbsent(childPath, augBindingClass); + } + augClassToProto.putIfAbsent(augBindingClass, augProto); + } } - return null; + yangToAugmentClass = ImmutableMap.copyOf(augPathToBinding); + augmentToPrototype = ImmutableMap.copyOf(augClassToProto); } - @SuppressWarnings("unchecked") @Override - public Optional> possibleStreamChild( - final Class childClass) { - final DataContainerCodecPrototype childProto = streamChildPrototype(childClass); - if (childProto != null) { - return Optional.of((DataContainerCodecContext) childProto.get()); - } - return Optional.empty(); + final DataContainerPrototype pathChildPrototype(final Class argType) { + final var child = super.pathChildPrototype(argType); + return child != null ? child : augmentToPrototype.get(argType); } @Override - public DataContainerCodecContext bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg, - final List builder) { - - final Class argType = arg.getType(); - DataContainerCodecPrototype ctxProto = byBindingArgClass.get(argType); - if (ctxProto == null && Augmentation.class.isAssignableFrom(argType)) { - ctxProto = augmentationByClass(argType); + final DataContainerPrototype streamChildPrototype(final Class childClass) { + final var child = super.streamChildPrototype(childClass); + if (child == null && Augmentation.class.isAssignableFrom(childClass)) { + return getAugmentationProtoByClass(childClass); } - final DataContainerCodecContext context = childNonNull(ctxProto, argType, - "Class %s is not valid child of %s", argType, getBindingClass()).get(); - if (context instanceof ChoiceNodeCodecContext) { - final ChoiceNodeCodecContext choice = (ChoiceNodeCodecContext) context; - choice.addYangPathArgument(arg, builder); - - final Optional> caseType = arg.getCaseType(); - final Class type = arg.getType(); - final DataContainerCodecContext caze; - if (caseType.isPresent()) { - // Non-ambiguous addressing this should not pose any problems - caze = choice.streamChild(caseType.get()); - } else { - caze = choice.getCaseByChildClass(type); - } - - caze.addYangPathArgument(arg, builder); - return caze.bindingPathArgumentChild(arg, builder); - } - context.addYangPathArgument(arg, builder); - return context; + return child; } @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 IncorrectNestingException.create("Leaf %s is not valid for %s", name, getBindingClass()); + final CodecContextSupplier yangChildSupplier(final NodeIdentifier arg) { + final var child = super.yangChildSupplier(arg); + if (child == null) { + final var augClass = yangToAugmentClass.get(arg); + if (augClass != null) { + return augmentToPrototype.get(augClass); + } } - return value; - } - - private DataContainerCodecPrototype loadChildPrototype(final Class childClass) { - final var type = getType(); - final var child = childNonNull(type.bindingChild(JavaTypeName.create(childClass)), childClass, - "Node %s does not have child named %s", type, childClass); - - return DataContainerCodecPrototype.from(createBindingArg(childClass, child.statement()), - (CompositeRuntimeType) child, factory()); - } - - // FIXME: MDSAL-697: move this method into BindingRuntimeContext - // This method is only called from loadChildPrototype() and exists only to be overridden by - // CaseNodeCodecContext. Since we are providing childClass and our schema to BindingRuntimeContext - // and receiving childSchema from it via findChildSchemaDefinition, we should be able to receive - // the equivalent of Map.Entry, along with the override we create here. One - // more input we may need to provide is our bindingClass(). - @SuppressWarnings("unchecked") - Item createBindingArg(final Class childClass, final EffectiveStatement childSchema) { - return Item.of((Class) childClass); + return child; } - private @Nullable DataContainerCodecPrototype augmentationByClass(final @NonNull Class childClass) { - final DataContainerCodecPrototype childProto = augmentationByStream.get(childClass); - return childProto != null ? childProto : mismatchedAugmentationByClass(childClass); + private @Nullable AugmentationCodecPrototype 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 @Nullable AugmentationCodecPrototype mismatchedAugmentationByClass(final @NonNull Class childClass) { /* * It is potentially mismatched valid augmentation - we look up equivalent augmentation using reflection * and walk all stream child and compare augmentations classes if they are equivalent. When we find a match * we'll cache it so we do not need to perform reflection operations again. */ - final ImmutableMap, DataContainerCodecPrototype> local = - (ImmutableMap, DataContainerCodecPrototype>) MISMATCHED_AUGMENTED.getAcquire(this); - final DataContainerCodecPrototype mismatched = local.get(childClass); + final var local = (ImmutableMap, AugmentationCodecPrototype>) MISMATCHED_AUGMENTED.getAcquire(this); + final var mismatched = local.get(childClass); return mismatched != null ? mismatched : loadMismatchedAugmentation(local, childClass); - } - private @Nullable DataContainerCodecPrototype loadMismatchedAugmentation( - final ImmutableMap, DataContainerCodecPrototype> oldMismatched, + private @Nullable AugmentationCodecPrototype loadMismatchedAugmentation( + final ImmutableMap, AugmentationCodecPrototype> oldMismatched, final @NonNull Class childClass) { @SuppressWarnings("rawtypes") - final Class augTarget = BindingReflections.findAugmentationTarget((Class) childClass); + final Class augTarget = 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()) { - if (Augmentation.class.isAssignableFrom(realChild.getBindingClass()) - && isSubstitutionFor(childClass, realChild.getBindingClass())) { + for (var realChild : augmentToPrototype.values()) { + final var realClass = realChild.javaClass(); + if (Augmentation.class.isAssignableFrom(realClass) && isSubstitutionFor(childClass, realClass)) { return cacheMismatched(oldMismatched, childClass, realChild); } } @@ -360,19 +211,18 @@ public abstract class DataObjectCodecContext cacheMismatched( - final @NonNull ImmutableMap, DataContainerCodecPrototype> oldMismatched, - final @NonNull Class childClass, final @NonNull DataContainerCodecPrototype prototype) { - - ImmutableMap, DataContainerCodecPrototype> expected = oldMismatched; + private @NonNull AugmentationCodecPrototype cacheMismatched( + final @NonNull ImmutableMap, AugmentationCodecPrototype> oldMismatched, + final @NonNull Class childClass, final @NonNull AugmentationCodecPrototype prototype) { + var expected = oldMismatched; while (true) { - final Map, DataContainerCodecPrototype> newMismatched = - ImmutableMap., DataContainerCodecPrototype>builderWithExpectedSize(expected.size() + 1) - .putAll(expected) - .put(childClass, prototype) - .build(); + final var newMismatched = + ImmutableMap., CommonDataObjectCodecPrototype>builderWithExpectedSize(expected.size() + 1) + .putAll(expected) + .put(childClass, prototype) + .build(); - final var witness = (ImmutableMap, DataContainerCodecPrototype>) + final var witness = (ImmutableMap, AugmentationCodecPrototype>) MISMATCHED_AUGMENTED.compareAndExchangeRelease(this, expected, newMismatched); if (witness == expected) { LOG.trace("Cached mismatched augmentation {} -> {} in {}", childClass, prototype, this); @@ -380,7 +230,7 @@ public abstract class DataObjectCodecContext existing = expected.get(childClass); + final var existing = expected.get(childClass); if (existing != null) { LOG.trace("Using raced mismatched augmentation {} -> {} in {}", childClass, existing, this); return existing; @@ -389,7 +239,7 @@ public abstract class DataObjectCodecContext cls) { - final BindingRuntimeContext ctx = factory().getRuntimeContext(); + final var ctx = prototype().contextFactory().getRuntimeContext(); final Class loaded; try { loaded = ctx.loadClass(Type.of(cls)); @@ -400,18 +250,19 @@ public abstract class DataObjectCodecContext loadAugmentPrototype(final AugmentRuntimeType augment) { + private @Nullable AugmentationCodecPrototype 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()) { + + if (childPaths.isEmpty()) { return null; } - final var factory = factory(); + final var factory = prototype().contextFactory(); final GeneratedType javaType = augment.javaType(); final Class> augClass; try { @@ -420,95 +271,75 @@ public abstract class DataObjectCodecContext node) { - try { - return (D) proxyConstructor.invokeExact(this, node); - } catch (final Throwable e) { - Throwables.throwIfUnchecked(e); - throw new IllegalStateException(e); - } + return new AugmentationCodecPrototype(augClass, augment, factory, childPaths); } + @Override @SuppressWarnings("unchecked") - Map>, Augmentation> getAllAugmentationsFrom( - final DistinctNodeContainer data) { - - @SuppressWarnings("rawtypes") - final Map map = new HashMap<>(); - - for (final NormalizedNode childValue : data.body()) { - if (childValue instanceof AugmentationNode augDomNode) { - final DataContainerCodecPrototype codecProto = augmentationByYang.get(augDomNode.getIdentifier()); - if (codecProto != null) { - final DataContainerCodecContext codec = codecProto.get(); - map.put(codec.getBindingClass(), codec.deserializeObject(augDomNode)); - } + Map>, Augmentation> getAllAugmentationsFrom(final DataContainerNode data) { + /** + * Due to augmentation fields are at same level as direct children the data of each augmentation needs to be + * aggregated into own container node, then only deserialized using associated prototype. + */ + final var builders = new HashMap, DataContainerNodeBuilder>(); + for (var childValue : data.body()) { + final var bindingClass = yangToAugmentClass.get(childValue.name()); + if (bindingClass != null) { + builders.computeIfAbsent(bindingClass, + key -> Builders.containerBuilder() + .withNodeIdentifier(new NodeIdentifier(data.name().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) { + final var bindingObj = codecProto.getCodecContext().deserializeObject(entry.getValue().build()); + if (bindingObj != null) { + map.put(bindingClass, bindingObj); } } } return map; } - final @NonNull Class> generatedClass() { - return generatedClass; - } - @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(); } - /** - * Scans supplied class and returns an iterable of all data children classes. - * - * @param type YANG Modeled Entity derived from DataContainer - * @return Iterable of all data children, which have YANG modeled entity - */ - // FIXME: MDSAL-780: replace use of this method - private static Map, Method> getChildrenClassToMethod(final Class type) { - return getChildClassToMethod(type, Naming.GETTER_PREFIX); + @Override + public NormalizedNode serialize(final D data) { + return serializeImpl(data); } - // FIXME: MDSAL-780: replace use of this method - private static Map, Method> getChildrenClassToNonnullMethod(final Class type) { - return getChildClassToMethod(type, Naming.NONNULL_PREFIX); + @Override + public final BindingNormalizedNodeCachingCodec createCachingCodec( + final ImmutableCollection> cacheSpecifier) { + return createCachingCodec(this, cacheSpecifier); + } + + final @NonNull Class> generatedClass() { + return generatedClass; } - // FIXME: MDSAL-780: replace use of this method - private static Map, Method> getChildClassToMethod(final Class type, - final String prefix) { - checkArgument(type != null, "Target type must not be null"); - checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type %s must be derived from DataContainer", - type); - final var ret = new HashMap, Method>(); - for (Method method : type.getMethods()) { - getYangModeledReturnType(method, prefix).ifPresent(entity -> ret.put(entity, method)); + @SuppressWarnings("checkstyle:illegalCatch") + final @NonNull D createBindingProxy(final DataContainerNode node) { + try { + return (D) proxyConstructor.invokeExact(this, node); + } catch (final Throwable e) { + Throwables.throwIfUnchecked(e); + throw new IllegalStateException(e); } - return ret; } }