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=5ba4e8fd9609a5c6c6ab3c4be97993ce728eae47;hb=3b18d17d6882c14b0f3542717e3ca539c0bc9593;hp=a430a25251a8cd66fd95680ec60d51b390576f2d;hpb=a96e49271c56b223f9e426b34adffe46c172c7ea;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 a430a25251..5ba4e8fd96 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,8 +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.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; @@ -48,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 { @@ -65,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); } @@ -108,13 +150,13 @@ public abstract sealed class DataObjectCodecContext pathChildPrototype(final Class argType) { + final CommonDataObjectCodecPrototype 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 CommonDataObjectCodecPrototype streamChildPrototype(final Class childClass) { final var child = super.streamChildPrototype(childClass); if (child == null && Augmentation.class.isAssignableFrom(childClass)) { return getAugmentationProtoByClass(childClass); @@ -159,8 +201,8 @@ public abstract sealed class DataObjectCodecContext, DataContainerCodecPrototype>builderWithExpectedSize(expected.size() + 1) + ImmutableMap., CommonDataObjectCodecPrototype>builderWithExpectedSize(expected.size() + 1) .putAll(expected) .put(childClass, prototype) .build(); @@ -197,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)); @@ -216,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 { @@ -231,7 +271,7 @@ public abstract sealed class DataObjectCodecContext> generatedClass() { - return generatedClass; - } - @Override public InstanceIdentifier.PathArgument deserializePathArgument(final PathArgument arg) { checkArgument(getDomPathArgument().equals(arg)); @@ -292,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); + } + } }