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=d99da25ddc04f21dd914376af14c9089ea26dcc5;hb=0032cbc207750ee84b76dfc395c29ade7adc76d4;hp=3ba1c785a6a9d49cc9d948ffba76d03f1a78ed1a;hpb=341ad234ee117b58f44a7af0ea4eba074626a756;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 3ba1c785a6..d99da25ddc 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,12 +31,13 @@ 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; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.DataObjectStep; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; @@ -51,6 +58,10 @@ 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") + @SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "https://github.com/spotbugs/spotbugs/issues/2749") private volatile ImmutableMap, CommonDataObjectCodecPrototype> mismatchedAugmented = ImmutableMap.of(); DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype) { @@ -76,27 +89,56 @@ public abstract sealed class DataObjectCodecContext prototype, final CodecItemFactory itemFactory) { - this(prototype, new CodecDataObjectAnalysis<>(prototype, itemFactory, null)); + this(prototype, new DataContainerAnalysis<>(prototype, itemFactory), null); } DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, final Method keyMethod) { - this(prototype, new CodecDataObjectAnalysis<>(prototype, CodecItemFactory.of(), keyMethod)); + this(prototype, new DataContainerAnalysis<>(prototype, CodecItemFactory.of()), keyMethod); } private DataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, - final CodecDataObjectAnalysis analysis) { + 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) { + final var augClassToProto = new HashMap, AugmentationCodecPrototype>(); + 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 DataContainerPrototype pathChildPrototype(final Class argType) { final var child = super.pathChildPrototype(argType); return child != null ? child : augmentToPrototype.get(argType); } @Override - final CommonDataObjectCodecPrototype 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); @@ -145,13 +187,13 @@ public abstract sealed class DataObjectCodecContext, AugmentationCodecPrototype>) MISMATCHED_AUGMENTED.getAcquire(this); + final var local = (ImmutableMap, AugmentationCodecPrototype>) MISMATCHED_AUGMENTED.getAcquire(this); final var mismatched = local.get(childClass); return mismatched != null ? mismatched : loadMismatchedAugmentation(local, childClass); } - private @Nullable AugmentationCodecPrototype loadMismatchedAugmentation( - final ImmutableMap, AugmentationCodecPrototype> oldMismatched, + private @Nullable AugmentationCodecPrototype loadMismatchedAugmentation( + final ImmutableMap, AugmentationCodecPrototype> oldMismatched, final @NonNull Class childClass) { @SuppressWarnings("rawtypes") final Class augTarget = findAugmentationTarget((Class) childClass); @@ -159,8 +201,8 @@ public abstract sealed class DataObjectCodecContext, AugmentationCodecPrototype> oldMismatched, - final @NonNull Class childClass, final @NonNull AugmentationCodecPrototype prototype) { + private @NonNull AugmentationCodecPrototype cacheMismatched( + final @NonNull ImmutableMap, AugmentationCodecPrototype> oldMismatched, + final @NonNull Class childClass, final @NonNull AugmentationCodecPrototype prototype) { var expected = oldMismatched; while (true) { final var newMismatched = @@ -180,7 +222,7 @@ public abstract sealed class DataObjectCodecContext, AugmentationCodecPrototype>) + final var witness = (ImmutableMap, AugmentationCodecPrototype>) MISMATCHED_AUGMENTED.compareAndExchangeRelease(this, expected, newMismatched); if (witness == expected) { LOG.trace("Cached mismatched augmentation {} -> {} in {}", childClass, prototype, this); @@ -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)); @@ -208,7 +250,7 @@ public abstract sealed class DataObjectCodecContext 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 childPaths = augment.statement() @@ -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(augClass, augment, factory, childPaths); } @Override @@ -257,7 +297,7 @@ public abstract sealed class DataObjectCodecContext> generatedClass() { - return generatedClass; - } - @Override - public InstanceIdentifier.PathArgument deserializePathArgument(final PathArgument arg) { + public DataObjectStep deserializePathArgument(final PathArgument arg) { checkArgument(getDomPathArgument().equals(arg)); return bindingArg(); } @Override - public PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) { - checkArgument(bindingArg().equals(arg)); + public PathArgument serializePathArgument(final DataObjectStep step) { + checkArgument(bindingArg().equals(step)); return getDomPathArgument(); } @@ -292,4 +328,23 @@ 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); + } + } + + @Override + Object deserializeObject(final NormalizedNode normalizedNode) { + return deserialize(normalizedNode); + } }