Eliminate CodecDataObjectAnalysis 56/106856/4
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 10 Jul 2023 14:29:15 +0000 (16:29 +0200)
committerRobert Varga <nite@hq.sk>
Fri, 29 Dec 2023 05:15:07 +0000 (05:15 +0000)
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 <robert.varga@pantheon.tech>
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectAnalysis.java [deleted file]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerAnalysis.java [moved from binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataContainerAnalysis.java with 95% similarity]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java

index d0b87666a43a61509f3c6367f3380ffa4b825af9..6748f6bb979e4bfbec189f962593dcab835e97a2 100644 (file)
@@ -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<D extends DataObject
     private final ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byStreamClass;
     private final ImmutableMap<NodeIdentifier, CodecContextSupplier> byYang;
     private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
-    private final MethodHandle proxyConstructor;
 
     AbstractDataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype,
-            final CodecDataObjectAnalysis<T> analysis) {
+            final DataContainerAnalysis<T> 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<D extends DataObject
         return byYang.get(arg);
     }
 
-    @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);
-        }
-    }
-
     final ValueNodeCodecContext getLeafChild(final String name) {
         final ValueNodeCodecContext value = leafChild.get(name);
         if (value == null) {
index 5552daea8ddb36a60522403ce3bf6b8593ad41f1..2d61c85456e952c376ad49e3fc02e5f31b169a30 100644 (file)
@@ -7,7 +7,11 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableSet;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
 import java.util.List;
 import java.util.Map;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingAugmentationCodecTreeNode;
@@ -22,8 +26,32 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 final class AugmentationCodecContext<D extends DataObject & Augmentation<?>>
         extends AbstractDataObjectCodecContext<D, AugmentRuntimeType> implements BindingAugmentationCodecTreeNode<D> {
+    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<AugmentRuntimeType> 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<D extends DataObject & Augmentation<?>>
         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;
index fb20296e4a14843af58da00d01c77366c1e422d9..9e55f2da29a157b1e58fcbefdad02380f14f2c19 100644 (file)
@@ -305,7 +305,7 @@ final class ChoiceCodecContext<D extends DataObject> extends CommonDataObjectCod
         checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type must be derived from DataContainer");
         final var ret = new LinkedList<Class<? extends DataObject>>();
         for (var method : type.getMethods()) {
-            AbstractDataContainerAnalysis.getYangModeledReturnType(method, Naming.GETTER_PREFIX)
+            DataContainerAnalysis.getYangModeledReturnType(method, Naming.GETTER_PREFIX)
                 .ifPresent(entity -> ret.add((Class<? extends DataObject>) 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 (file)
index 9ecf706..0000000
+++ /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<R extends CompositeRuntimeType> extends AbstractDataContainerAnalysis<R> {
-    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<? extends CodecDataObject<?>> generatedClass;
-    final @NonNull List<AugmentRuntimeType> possibleAugmentations;
-    final @NonNull MethodHandle proxyConstructor;
-
-    CodecDataObjectAnalysis(final CommonDataObjectCodecPrototype<R> 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);
-    }
-}
@@ -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<R extends CompositeRuntimeType> {
-    private static final Logger LOG = LoggerFactory.getLogger(AbstractDataContainerAnalysis.class);
+final class DataContainerAnalysis<R extends CompositeRuntimeType> {
+    private static final Logger LOG = LoggerFactory.getLogger(DataContainerAnalysis.class);
 
     // Needed for DataContainerCodecContext
     final @NonNull ImmutableMap<Class<?>, CommonDataObjectCodecPrototype<?>> byStreamClass;
@@ -51,7 +51,11 @@ abstract class AbstractDataContainerAnalysis<R extends CompositeRuntimeType> {
     final @NonNull ImmutableMap<Method, ValueNodeCodecContext> leafContexts;
     final @NonNull ImmutableMap<Class<?>, PropertyInfo> daoProperties;
 
-    AbstractDataContainerAnalysis(final Class<?> bindingClass, final R runtimeType, final CodecContextFactory factory,
+    DataContainerAnalysis(final CommonDataObjectCodecPrototype<R> 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<R extends CompositeRuntimeType> {
         return ret;
     }
 
-    static final Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method,
+    static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method,
             final String prefix) {
         final String methodName = method.getName();
         if ("getClass".equals(methodName) || !methodName.startsWith(prefix) || method.getParameterCount() > 0) {
index b63befcbe96f7ef77125fe1664cbf69ff309e555..f076958cf33c1652e0161c21d59fcd563553ce89 100644 (file)
@@ -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<D extends DataObject, T exte
         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,6 +77,7 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
     private final ImmutableMap<Class<?>, AugmentationCodecPrototype> augmentToPrototype;
     private final ImmutableMap<NodeIdentifier, Class<?>> yangToAugmentClass;
     private final @NonNull Class<? extends CodecDataObject<?>> 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<D extends DataObject, T exte
     }
 
     DataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype, final CodecItemFactory itemFactory) {
-        this(prototype, new CodecDataObjectAnalysis<>(prototype, itemFactory, null));
+        this(prototype, new DataContainerAnalysis<>(prototype, itemFactory), null);
     }
 
     DataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype, final Method keyMethod) {
-        this(prototype, new CodecDataObjectAnalysis<>(prototype, CodecItemFactory.of(), keyMethod));
+        this(prototype, new DataContainerAnalysis<>(prototype, CodecItemFactory.of()), keyMethod);
     }
 
     private DataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype,
-            final CodecDataObjectAnalysis<T> analysis) {
+            final DataContainerAnalysis<T> 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<AugmentRuntimeType> 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<NodeIdentifier, Class<?>>();
         final var augClassToProto = new HashMap<Class<?>, 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<D extends DataObject, T exte
         return map;
     }
 
-    final @NonNull Class<? extends CodecDataObject<?>> generatedClass() {
-        return generatedClass;
-    }
-
     @Override
     public InstanceIdentifier.PathArgument deserializePathArgument(final PathArgument arg) {
         checkArgument(getDomPathArgument().equals(arg));
@@ -292,4 +328,18 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
             final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
         return createCachingCodec(this, cacheSpecifier);
     }
+
+    final @NonNull Class<? extends CodecDataObject<?>> 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);
+        }
+    }
 }