From 6f6eaca09a69c83e9c7290b1dcc3e7090a5f611d Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 23 May 2023 00:14:55 +0200 Subject: [PATCH] Add CodecDataObjectAnalysis We will be separating out DataObjectCodecContext into two classes, and the new one will need to be able to create proxies. Separate out all the analytics and indexing into a separate DTO, which can be constructed externally, so the new classes can construct them separately. This also necessitates splitting out bindingChildArg() into a separate class -- but we still want to migrate that to RuntimTypes at some point. JIRA: MDSAL-820 Change-Id: I970b8f92443a9faf57c1786239e2f552f7ff938d Signed-off-by: Robert Varga --- .../codec/impl/AugmentationNodeContext.java | 1 - .../dom/codec/impl/CaseNodeCodecContext.java | 14 +- .../codec/impl/CodecDataObjectAnalysis.java | 213 ++++++++++++++++++ .../dom/codec/impl/CodecItemFactory.java | 59 +++++ .../codec/impl/DataContainerCodecContext.java | 2 +- .../codec/impl/DataObjectCodecContext.java | 191 +++------------- 6 files changed, 301 insertions(+), 179 deletions(-) create mode 100644 binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java create mode 100644 binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecItemFactory.java diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationNodeContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationNodeContext.java index 55ceb9b20f..59f2e226e8 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationNodeContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationNodeContext.java @@ -15,7 +15,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; final class AugmentationNodeContext> extends DataObjectCodecContext { - AugmentationNodeContext(final DataContainerCodecPrototype prototype) { super(prototype); } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CaseNodeCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CaseNodeCodecContext.java index 804640f03b..0352a82676 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CaseNodeCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CaseNodeCodecContext.java @@ -12,26 +12,14 @@ import static com.google.common.base.Preconditions.checkArgument; import java.util.List; import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType; import org.opendaylight.yangtools.yang.binding.DataObject; -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.ChoiceNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware; -import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; final class CaseNodeCodecContext extends DataObjectCodecContext { CaseNodeCodecContext(final DataContainerCodecPrototype prototype) { - super(prototype); - } - - @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) - Item createBindingArg(final Class childClass, final EffectiveStatement childSchema) { - // FIXME: MDSAL-697: see overridden method for further guidance - return childSchema instanceof AddedByUsesAware aware && aware.isAddedByUses() - ? Item.of((Class)getBindingClass(), (Class)childClass) - : super.createBindingArg(childClass, childSchema); + super(prototype, CodecItemFactory.of(prototype.getBindingClass())); } @Override diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java new file mode 100644 index 0000000000..71dd5a3ca3 --- /dev/null +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.mdsal.binding.dom.codec.impl; + +import static 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.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory; +import org.opendaylight.mdsal.binding.model.api.JavaTypeName; +import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.AugmentableRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType; +import org.opendaylight.yangtools.yang.binding.Augmentable; +import org.opendaylight.yangtools.yang.binding.DataContainer; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.OpaqueObject; +import org.opendaylight.yangtools.yang.binding.contract.Naming; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer; + +/** + * Analysis of a {@link DataObject} specialization class. The primary point of this class is to separate out creation + * indices needed for {@link #proxyConstructor}. Since we want to perform as much indexing as possible in a single pass, + * we also end up indexing things that are not strictly required to arrive at that constructor. + */ +final class CodecDataObjectAnalysis { + private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, + DataObjectCodecContext.class, DistinctNodeContainer.class); + private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, + DataObjectCodecContext.class, DistinctNodeContainer.class); + + final @NonNull ImmutableMap, DataContainerCodecPrototype> byStreamClass; + final @NonNull ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; + final @NonNull ImmutableMap byYang; + final @NonNull ImmutableMap leafNodes; + final @NonNull Class> generatedClass; + final @NonNull List possibleAugmentations; + final @NonNull MethodHandle proxyConstructor; + + CodecDataObjectAnalysis(final DataContainerCodecPrototype prototype, final CodecItemFactory itemFactory, + final Method keyMethod) { + // Preliminaries from prototype + @SuppressWarnings("unchecked") + final Class bindingClass = Class.class.cast(prototype.getBindingClass()); + final var runtimeType = prototype.getType(); + final var factory = prototype.getFactory(); + final var leafContexts = factory.getLeafNodes(bindingClass, runtimeType.statement()); + + // Reflection-based on the passed class + final var clsToMethod = getChildrenClassToMethod(bindingClass); + + // Indexing part: be very careful around what gets done when + final var byYangBuilder = new HashMap(); + + // Step 1: add leaf children + var leafBuilder = ImmutableMap.builderWithExpectedSize(leafContexts.size()); + for (var leaf : leafContexts.values()) { + leafBuilder.put(leaf.getSchema().getQName().getLocalName(), leaf); + byYangBuilder.put(leaf.getDomPathArgument(), leaf); + } + leafNodes = leafBuilder.build(); + + final var byBindingArgClassBuilder = new HashMap, DataContainerCodecPrototype>(); + final var byStreamClassBuilder = new HashMap, DataContainerCodecPrototype>(); + final var daoProperties = new HashMap, PropertyInfo>(); + for (var childDataObj : clsToMethod.entrySet()) { + final var method = childDataObj.getValue(); + verify(!method.isDefault(), "Unexpected default method %s in %s", method, bindingClass); + + final var 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 var childProto = getChildPrototype(runtimeType, factory, itemFactory, 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 var choice = (ChoiceNodeCodecContext) childProto.get(); + for (var cazeChild : choice.getCaseChildrenClasses()) { + byBindingArgClassBuilder.put(cazeChild, childProto); + } + } + } + + // Snapshot before below processing + byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder); + + // 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); + byBindingArgClass = byStreamClassBuilder.equals(byBindingArgClassBuilder) ? byStreamClass + : ImmutableMap.copyOf(byBindingArgClassBuilder); + + // 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)); + } + } + // At this point all indexing is done: byYangBuilder should not be referenced + byYang = ImmutableMap.copyOf(byYangBuilder); + + // Final bits: generate the appropriate class, As a side effect we identify what Augmentations are possible + if (Augmentable.class.isAssignableFrom(bindingClass)) { + // Verify we have the appropriate backing runtimeType + if (!(runtimeType instanceof AugmentableRuntimeType augmentableRuntimeType)) { + throw new VerifyException( + "Unexpected type %s backing augmenable %s".formatted(runtimeType, bindingClass)); + } + + possibleAugmentations = augmentableRuntimeType.augments(); + generatedClass = CodecDataObjectGenerator.generateAugmentable(prototype.getFactory().getLoader(), + bindingClass, leafContexts, daoProperties, keyMethod); + } else { + possibleAugmentations = List.of(); + generatedClass = CodecDataObjectGenerator.generate(prototype.getFactory().getLoader(), bindingClass, + leafContexts, daoProperties, keyMethod); + } + + // All done: acquire the constructor: it is supposed to be public + final MethodHandle ctor; + try { + ctor = MethodHandles.publicLookup().findConstructor(generatedClass, CONSTRUCTOR_TYPE); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new LinkageError("Failed to find contructor for class " + generatedClass, e); + } + + proxyConstructor = ctor.asType(DATAOBJECT_TYPE); + } + + private static @NonNull DataContainerCodecPrototype getChildPrototype(final CompositeRuntimeType type, + final CodecContextFactory factory, final CodecItemFactory itemFactory, + final Class childClass) { + final var child = type.bindingChild(JavaTypeName.create(childClass)); + if (child == null) { + throw DataContainerCodecContext.childNullException(factory.getRuntimeContext(), childClass, + "Node %s does not have child named %s", type, childClass); + } + + return DataContainerCodecPrototype.from(itemFactory.createItem(childClass, child.statement()), + (CompositeRuntimeType) child, factory); + } + + + // FIXME: MDSAL-780: these methods perform analytics using java.lang.reflect to acquire the basic shape of the + // class. This is not exactly AOT friendly, as most of the information should be provided by + // CompositeRuntimeType. + // + // As as first cut, CompositeRuntimeType should provide the mapping between YANG children and the + // corresponding GETTER_PREFIX/NONNULL_PREFIX method names, If we have that, the use in this + // class should be fine. + // + // The second cut is binding the actual Method invocations, which is fine here, as this class is + // all about having a run-time generated class. AOT would be providing an alternative, where the + // equivalent class would be generated at compile-time and hence would bind to the methods + // directly -- and AOT equivalent of this class would really be a compile-time generated registry + // to those classes' entry points. + + /** + * 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 + */ + private static Map, Method> getChildrenClassToMethod(final Class type) { + return getChildClassToMethod(type, Naming.GETTER_PREFIX); + } + + private static Map, Method> getChildrenClassToNonnullMethod(final Class type) { + return getChildClassToMethod(type, Naming.NONNULL_PREFIX); + } + + 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()) { + DataContainerCodecContext.getYangModeledReturnType(method, prefix) + .ifPresent(entity -> ret.put(entity, method)); + } + return ret; + } +} diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecItemFactory.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecItemFactory.java new file mode 100644 index 0000000000..10a3248a1c --- /dev/null +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecItemFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.mdsal.binding.dom.codec.impl; + +import static java.util.Objects.requireNonNull; + +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item; +import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; + +sealed class CodecItemFactory { + private static final class Case extends CodecItemFactory { + private final Class bindingClass; + + Case(final Class bindingClass) { + this.bindingClass = requireNonNull(bindingClass); + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + Item createItem(final Class childClass, final EffectiveStatement childSchema) { + // FIXME: MDSAL-697: see overridden method for further guidance + return childSchema instanceof AddedByUsesAware aware && aware.isAddedByUses() + ? Item.of((Class) bindingClass, (Class) childClass) : super.createItem(childClass, childSchema); + } + } + + private static final @NonNull CodecItemFactory DEFAULT = new CodecItemFactory(); + + private CodecItemFactory() { + // Hidden on purpose + } + + // 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 createItem(final Class childClass, final EffectiveStatement childSchema) { + return Item.of((Class) childClass); + } + + static @NonNull CodecItemFactory of() { + return DEFAULT; + } + + static @NonNull CodecItemFactory of(final Class bindingClass) { + return new Case(bindingClass); + } +} diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java index 7a92d50219..27a565652f 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java @@ -227,7 +227,7 @@ abstract class DataContainerCodecContext childClass, final String message, final Object... args) { final CompositeRuntimeType schema; if (Augmentation.class.isAssignableFrom(childClass)) { 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 d859ec8f27..9c941089fd 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,44 +8,32 @@ 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.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.ImmutableSet; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; 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.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.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.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; @@ -56,7 +44,6 @@ 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.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,10 +55,7 @@ import org.slf4j.LoggerFactory; public abstract class DataObjectCodecContext extends DataContainerCodecContext { private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class); - private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, - DataObjectCodecContext.class, DistinctNodeContainer.class); - private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, - DataObjectCodecContext.class, DistinctNodeContainer.class); + private static final VarHandle MISMATCHED_AUGMENTED; static { @@ -98,104 +82,40 @@ public abstract class DataObjectCodecContext, DataContainerCodecPrototype> mismatchedAugmented = ImmutableMap.of(); DataObjectCodecContext(final DataContainerCodecPrototype prototype) { - this(prototype, null); + 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 DataContainerCodecPrototype prototype, final CodecItemFactory itemFactory) { + this(prototype, new CodecDataObjectAnalysis<>(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 DataContainerCodecPrototype prototype, final Method keyMethod) { + this(prototype, new CodecDataObjectAnalysis<>(prototype, CodecItemFactory.of(), keyMethod)); + } - this.byYang = ImmutableMap.copyOf(byYangBuilder); - this.byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder); - - // 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 List possibleAugmentations; - 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); - } else { - possibleAugmentations = List.of(); - generatedClass = CodecDataObjectGenerator.generate(prototype.getFactory().getLoader(), bindingClass, - tmpLeaves, daoProperties, keyMethod); - } + private DataObjectCodecContext(final DataContainerCodecPrototype prototype, + final CodecDataObjectAnalysis analysis) { + super(prototype); - // 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); + // Inherit analysis stuff + leafChild = analysis.leafNodes; + proxyConstructor = analysis.proxyConstructor; + generatedClass = analysis.generatedClass; + byBindingArgClass = analysis.byBindingArgClass; + byStreamClass = analysis.byStreamClass; + byYang = analysis.byYang; + + // Deal with augmentations, which are not something we analysis provides + final var augByYang = new HashMap>(); + final var augByStream = new HashMap, DataContainerCodecPrototype>(); + for (var augment : analysis.possibleAugmentations) { + final var augProto = loadAugmentPrototype(augment); if (augProto != null) { - final PathArgument augYangArg = augProto.getYangArg(); + final var augYangArg = augProto.getYangArg(); if (augByYang.putIfAbsent(augYangArg, augProto) == null) { LOG.trace("Discovered new YANG mapping {} -> {} in {}", augYangArg, augProto, this); } - final Class augBindingClass = augProto.getBindingClass(); + final var augBindingClass = augProto.getBindingClass(); if (augByStream.putIfAbsent(augBindingClass, augProto) == null) { LOG.trace("Discovered new class mapping {} -> {} in {}", augBindingClass, augProto, this); } @@ -203,15 +123,6 @@ public abstract class DataObjectCodecContext 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); - } - private @Nullable DataContainerCodecPrototype augmentationByClass(final @NonNull Class childClass) { final DataContainerCodecPrototype childProto = augmentationByStream.get(childClass); return childProto != null ? childProto : mismatchedAugmentationByClass(childClass); @@ -482,32 +373,4 @@ public abstract class DataObjectCodecContext, Method> getChildrenClassToMethod(final Class type) { - return getChildClassToMethod(type, Naming.GETTER_PREFIX); - } - - // FIXME: MDSAL-780: replace use of this method - private static Map, Method> getChildrenClassToNonnullMethod(final Class type) { - return getChildClassToMethod(type, Naming.NONNULL_PREFIX); - } - - // 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)); - } - return ret; - } } -- 2.36.6