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=4342ca6691678018014f91ac367f9034ee9dc98c;hpb=e3c47e9a63e9009386b2c3fe05898a40a3f65d7c;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 4342ca6691..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 @@ -10,13 +10,19 @@ package org.opendaylight.mdsal.binding.dom.codec.impl; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.annotations.Beta; +import com.google.common.base.Throwables; +import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMap; 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; import java.lang.invoke.VarHandle; 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.eclipse.jdt.annotation.Nullable; @@ -25,9 +31,9 @@ import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCaching 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.BindingRuntimeContext; +import org.opendaylight.mdsal.binding.runtime.api.AugmentableRuntimeType; 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.BindingObject; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -49,9 +55,13 @@ import org.slf4j.LoggerFactory; @Beta public abstract sealed class DataObjectCodecContext extends AbstractDataObjectCodecContext implements BindingDataObjectCodecTreeNode - permits CaseNodeCodecContext, ContainerNodeCodecContext, ListNodeCodecContext, NotificationCodecContext { + permits CaseCodecContext, ContainerLikeCodecContext, ListCodecContext, NotificationCodecContext { private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class); + private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, + AbstractDataObjectCodecContext.class, DataContainerNode.class); + private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, + DataObjectCodecContext.class, DataContainerNode.class); private static final VarHandle MISMATCHED_AUGMENTED; static { @@ -66,38 +76,69 @@ public abstract sealed class DataObjectCodecContext, 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) { + DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype) { this(prototype, CodecItemFactory.of()); } - DataObjectCodecContext(final DataContainerCodecPrototype prototype, final CodecItemFactory itemFactory) { - this(prototype, new CodecDataObjectAnalysis<>(prototype, itemFactory, null)); + DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, final CodecItemFactory itemFactory) { + this(prototype, new DataContainerAnalysis<>(prototype, itemFactory), null); } - DataObjectCodecContext(final DataContainerCodecPrototype prototype, final Method keyMethod) { - this(prototype, new CodecDataObjectAnalysis<>(prototype, CodecItemFactory.of(), keyMethod)); + DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, final Method keyMethod) { + this(prototype, new DataContainerAnalysis<>(prototype, CodecItemFactory.of()), keyMethod); } - private DataObjectCodecContext(final DataContainerCodecPrototype prototype, - final CodecDataObjectAnalysis analysis) { + private DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, + final DataContainerAnalysis analysis, final Method keyMethod) { super(prototype, analysis); - // Inherit analysis stuff - generatedClass = analysis.generatedClass; + 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 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(loader, bindingClass, analysis.leafContexts, + analysis.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); // Deal with augmentations, which are not something we analysis provides final var augPathToBinding = new HashMap>(); final var augClassToProto = new HashMap, AugmentationCodecPrototype>(); - for (var augment : analysis.possibleAugmentations) { + for (var augment : possibleAugmentations) { final var augProto = loadAugmentPrototype(augment); if (augProto != null) { - final var augBindingClass = augProto.getBindingClass(); + final var augBindingClass = augProto.javaClass(); for (var childPath : augProto.getChildArgs()) { augPathToBinding.putIfAbsent(childPath, augBindingClass); } @@ -109,13 +150,13 @@ public abstract sealed class DataObjectCodecContext pathChildPrototype(final Class argType) { + final DataContainerPrototype pathChildPrototype(final Class argType) { final var child = super.pathChildPrototype(argType); return child != null ? child : augmentToPrototype.get(argType); } @Override - final DataContainerCodecPrototype streamChildPrototype(final Class childClass) { + final DataContainerPrototype streamChildPrototype(final Class childClass) { final var child = super.streamChildPrototype(childClass); if (child == null && Augmentation.class.isAssignableFrom(childClass)) { return getAugmentationProtoByClass(childClass); @@ -155,13 +196,13 @@ public abstract sealed class DataObjectCodecContext, 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 (var realChild : augmentToPrototype.values()) { - if (Augmentation.class.isAssignableFrom(realChild.getBindingClass()) - && isSubstitutionFor(childClass, realChild.getBindingClass())) { + final var realClass = realChild.javaClass(); + if (Augmentation.class.isAssignableFrom(realClass) && isSubstitutionFor(childClass, realClass)) { return cacheMismatched(oldMismatched, childClass, realChild); } } @@ -176,7 +217,7 @@ public abstract sealed class DataObjectCodecContext, DataContainerCodecPrototype>builderWithExpectedSize(expected.size() + 1) + ImmutableMap., CommonDataObjectCodecPrototype>builderWithExpectedSize(expected.size() + 1) .putAll(expected) .put(childClass, prototype) .build(); @@ -198,7 +239,7 @@ public abstract sealed class DataObjectCodecContext cls) { - final BindingRuntimeContext ctx = factory().getRuntimeContext(); + final var ctx = prototype().contextFactory().getRuntimeContext(); final Class loaded; try { loaded = ctx.loadClass(Type.of(cls)); @@ -217,13 +258,11 @@ public abstract sealed class DataObjectCodecContext new NodeIdentifier((QName) stmt.argument())) .collect(ImmutableSet.toImmutableSet()); - final var it = childPaths.iterator(); - if (!it.hasNext()) { + if (childPaths.isEmpty()) { return null; } - final var namespace = it.next().getNodeType().getModule(); - final var factory = factory(); + final var factory = prototype().contextFactory(); final GeneratedType javaType = augment.javaType(); final Class> augClass; try { @@ -232,7 +271,7 @@ public abstract sealed class DataObjectCodecContext> generatedClass() { - return generatedClass; - } - @Override public InstanceIdentifier.PathArgument deserializePathArgument(final PathArgument arg) { checkArgument(getDomPathArgument().equals(arg)); @@ -293,4 +328,18 @@ public abstract sealed class DataObjectCodecContext> cacheSpecifier) { return createCachingCodec(this, cacheSpecifier); } + + final @NonNull Class> generatedClass() { + return generatedClass; + } + + @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); + } + } }