From: Robert Varga Date: Mon, 10 Jul 2023 14:29:15 +0000 (+0200) Subject: Eliminate CodecDataObjectAnalysis X-Git-Tag: v13.0.0~51 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=mdsal.git;a=commitdiff_plain;h=fec64f6b8dcb6c2106c0328cc7f308a2493a32c9 Eliminate CodecDataObjectAnalysis We really do not want to have a ton of specializations -- at the end of the day we just need to properly specialize generation and constructor access. While this introduces a bit of code duplication, it also attaches more properly the individual types to their implementations and the contracts therein. JIRA: MDSAL-805 Change-Id: I88b9222835817d2f52469c219844b2f05485dbc2 Signed-off-by: Robert Varga --- diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java index d0b87666a4..6748f6bb97 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java @@ -7,10 +7,8 @@ */ package org.opendaylight.mdsal.binding.dom.codec.impl; -import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import java.lang.invoke.MethodHandle; import java.util.List; import java.util.Map; import org.eclipse.jdt.annotation.NonNull; @@ -49,16 +47,14 @@ public abstract sealed class AbstractDataObjectCodecContext, CommonDataObjectCodecPrototype> byStreamClass; private final ImmutableMap byYang; private final ImmutableMap leafChild; - private final MethodHandle proxyConstructor; AbstractDataObjectCodecContext(final CommonDataObjectCodecPrototype prototype, - final CodecDataObjectAnalysis analysis) { + final DataContainerAnalysis analysis) { super(prototype); byBindingArgClass = analysis.byBindingArgClass; byStreamClass = analysis.byStreamClass; byYang = analysis.byYang; leafChild = analysis.leafNodes; - proxyConstructor = analysis.proxyConstructor; } @Override @@ -108,16 +104,6 @@ public abstract sealed class AbstractDataObjectCodecContext> extends AbstractDataObjectCodecContext implements BindingAugmentationCodecTreeNode { + private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, + AbstractDataObjectCodecContext.class, DataContainerNode.class); + private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, + AugmentationCodecContext.class, DataContainerNode.class); + + private final MethodHandle proxyConstructor; + + private AugmentationCodecContext(final AugmentationCodecPrototype prototype, + final DataContainerAnalysis analysis) { + super(prototype, analysis); + + final var bindingClass = CodecDataObjectGenerator.generate(prototype.getFactory().getLoader(), + prototype.getBindingClass(), analysis.leafContexts, analysis.daoProperties, null); + + final MethodHandle ctor; + try { + ctor = MethodHandles.publicLookup().findConstructor(bindingClass, CONSTRUCTOR_TYPE); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new LinkageError("Failed to find contructor for class " + bindingClass, e); + } + + proxyConstructor = ctor.asType(DATAOBJECT_TYPE); + } + AugmentationCodecContext(final AugmentationCodecPrototype prototype) { - super(prototype, new CodecDataObjectAnalysis<>(prototype, CodecItemFactory.of(), null)); + this(prototype, new DataContainerAnalysis<>(prototype, CodecItemFactory.of())); } @Override @@ -42,11 +70,17 @@ final class AugmentationCodecContext> return bindingArg(); } + @SuppressWarnings("checkstyle:illegalCatch") @Override public D filterFrom(final DataContainerNode parentData) { for (var childArg : ((AugmentationCodecPrototype) prototype).getChildArgs()) { if (parentData.childByArg(childArg) != null) { - return createBindingProxy(parentData); + try { + return (D) proxyConstructor.invokeExact(this, parentData); + } catch (final Throwable e) { + Throwables.throwIfUnchecked(e); + throw new IllegalStateException(e); + } } } return null; diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java index fb20296e4a..9e55f2da29 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java @@ -305,7 +305,7 @@ final class ChoiceCodecContext extends CommonDataObjectCod checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type must be derived from DataContainer"); final var ret = new LinkedList>(); for (var method : type.getMethods()) { - AbstractDataContainerAnalysis.getYangModeledReturnType(method, Naming.GETTER_PREFIX) + DataContainerAnalysis.getYangModeledReturnType(method, Naming.GETTER_PREFIX) .ifPresent(entity -> ret.add((Class) entity)); } return ret; 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 deleted file mode 100644 index 9ecf70678e..0000000000 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 com.google.common.base.VerifyException; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.reflect.Method; -import java.util.List; -import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType; -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.DataObject; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; - -/** - * 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 extends AbstractDataContainerAnalysis { - private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, - AbstractDataObjectCodecContext.class, DataContainerNode.class); - private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, - AbstractDataObjectCodecContext.class, DataContainerNode.class); - - final @NonNull Class> generatedClass; - final @NonNull List possibleAugmentations; - final @NonNull MethodHandle proxyConstructor; - - CodecDataObjectAnalysis(final CommonDataObjectCodecPrototype prototype, final CodecItemFactory itemFactory, - final Method keyMethod) { - this(prototype.getBindingClass(), prototype.getType(), prototype.getFactory(), itemFactory, keyMethod); - } - - CodecDataObjectAnalysis(final Class bindingClass, final R runtimeType, final CodecContextFactory factory, - final CodecItemFactory itemFactory, final Method keyMethod) { - super(bindingClass, runtimeType, factory, itemFactory); - - // 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(factory.getLoader(), bindingClass, - leafContexts, daoProperties, keyMethod); - } else { - possibleAugmentations = List.of(); - generatedClass = CodecDataObjectGenerator.generate(factory.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); - } -} diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataContainerAnalysis.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerAnalysis.java similarity index 95% rename from binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataContainerAnalysis.java rename to binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerAnalysis.java index d65fa37f5c..608445ea03 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataContainerAnalysis.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerAnalysis.java @@ -38,8 +38,8 @@ import org.slf4j.LoggerFactory; * Analysis of a {@link DataContainer} specialization class. This includes things needed for * {@link DataContainerCodecContext}'s methods as well as the appropriate run-time generated class. */ -abstract class AbstractDataContainerAnalysis { - private static final Logger LOG = LoggerFactory.getLogger(AbstractDataContainerAnalysis.class); +final class DataContainerAnalysis { + private static final Logger LOG = LoggerFactory.getLogger(DataContainerAnalysis.class); // Needed for DataContainerCodecContext final @NonNull ImmutableMap, CommonDataObjectCodecPrototype> byStreamClass; @@ -51,7 +51,11 @@ abstract class AbstractDataContainerAnalysis { final @NonNull ImmutableMap leafContexts; final @NonNull ImmutableMap, PropertyInfo> daoProperties; - AbstractDataContainerAnalysis(final Class bindingClass, final R runtimeType, final CodecContextFactory factory, + DataContainerAnalysis(final CommonDataObjectCodecPrototype prototype, final CodecItemFactory itemFactory) { + this(prototype.getBindingClass(), prototype.getType(), prototype.getFactory(), itemFactory); + } + + DataContainerAnalysis(final Class bindingClass, final R runtimeType, final CodecContextFactory factory, final CodecItemFactory itemFactory) { leafContexts = factory.getLeafNodes(bindingClass, runtimeType.statement()); @@ -189,7 +193,7 @@ abstract class AbstractDataContainerAnalysis { return ret; } - static final Optional> getYangModeledReturnType(final Method method, + static Optional> getYangModeledReturnType(final Method method, final String prefix) { final String methodName = method.getName(); if ("getClass".equals(methodName) || !methodName.startsWith(prefix) || method.getParameterCount() > 0) { 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 b63befcbe9..f076958cf3 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,14 +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; @@ -26,8 +31,10 @@ 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.AugmentableRuntimeType; import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext; 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; @@ -52,6 +59,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. @@ -78,24 +90,52 @@ 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; + if (Augmentable.class.isAssignableFrom(bindingClass)) { + // Verify we have the appropriate backing runtimeType + final var runtimeType = prototype.getType(); + if (!(runtimeType instanceof AugmentableRuntimeType augmentableRuntimeType)) { + throw new VerifyException( + "Unexpected type %s backing augmenable %s".formatted(runtimeType, bindingClass)); + } + + possibleAugmentations = augmentableRuntimeType.augments(); + generatedClass = CodecDataObjectGenerator.generateAugmentable(factory().getLoader(), bindingClass, + analysis.leafContexts, analysis.daoProperties, keyMethod); + } else { + possibleAugmentations = List.of(); + generatedClass = CodecDataObjectGenerator.generate(factory().getLoader(), 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(); @@ -266,10 +306,6 @@ 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); + } + } }