From f84eb736c3fba4724a1c629a67c34c52557de0a3 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Thu, 29 Jun 2023 16:47:16 +0200 Subject: [PATCH] Split up CodecDataObjectAnalysis CodecDataObjectAnalysis contains two things: the indexing of children and then the generated class. We need to split these up, so we can actually plugin in YangData (which needs to former) into the mix. JIRA: MDSAL-805 Change-Id: Ic38cbf1be4b4011ae5dd983d1826e020a67620c4 Signed-off-by: Robert Varga --- .../impl/AbstractDataContainerAnalysis.java | 246 ++++++++++++++++++ .../dom/codec/impl/ChoiceCodecContext.java | 6 +- .../codec/impl/CodecDataObjectAnalysis.java | 164 +----------- .../codec/impl/DataContainerCodecContext.java | 59 ----- 4 files changed, 254 insertions(+), 221 deletions(-) create mode 100644 binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataContainerAnalysis.java 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/AbstractDataContainerAnalysis.java new file mode 100644 index 0000000000..d65fa37f5c --- /dev/null +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataContainerAnalysis.java @@ -0,0 +1,246 @@ +/* + * 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 static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Verify.verify; +import static com.google.common.base.Verify.verifyNotNull; + +import com.google.common.collect.ImmutableMap; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.mdsal.binding.model.api.JavaTypeName; +import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.ContainerLikeRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.ContainerRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.ListRuntimeType; +import org.opendaylight.yangtools.util.ClassLoaderUtils; +import org.opendaylight.yangtools.yang.binding.DataContainer; +import org.opendaylight.yangtools.yang.binding.OpaqueObject; +import org.opendaylight.yangtools.yang.binding.contract.Naming; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.model.api.stmt.PresenceEffectiveStatement; +import org.slf4j.Logger; +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); + + // Needed for DataContainerCodecContext + final @NonNull ImmutableMap, CommonDataObjectCodecPrototype> byStreamClass; + final @NonNull ImmutableMap, CommonDataObjectCodecPrototype> byBindingArgClass; + final @NonNull ImmutableMap byYang; + final @NonNull ImmutableMap leafNodes; + + // Needed for generated classes + final @NonNull ImmutableMap leafContexts; + final @NonNull ImmutableMap, PropertyInfo> daoProperties; + + AbstractDataContainerAnalysis(final Class bindingClass, final R runtimeType, final CodecContextFactory factory, + final CodecItemFactory itemFactory) { + leafContexts = factory.getLeafNodes(bindingClass, runtimeType.statement()); + + // Reflection-based on the passed class + final var clsToMethod = getChildrenClassToMethod(bindingClass); + + // Indexing part: be very careful around what gets done when + final var byYangBuilder = new HashMap(); + + // Step 1: add leaf children + var leafBuilder = ImmutableMap.builderWithExpectedSize(leafContexts.size()); + for (var leaf : leafContexts.values()) { + leafBuilder.put(leaf.getSchema().getQName().getLocalName(), leaf); + byYangBuilder.put(leaf.getDomPathArgument(), leaf); + } + leafNodes = leafBuilder.build(); + + final var byBindingArgClassBuilder = new HashMap, CommonDataObjectCodecPrototype>(); + final var byStreamClassBuilder = new HashMap, CommonDataObjectCodecPrototype>(); + final var daoPropertiesBuilder = new HashMap, PropertyInfo>(); + for (var childDataObj : clsToMethod.entrySet()) { + final var method = childDataObj.getValue(); + verify(!method.isDefault(), "Unexpected default method %s in %s", method, bindingClass); + + final var retClass = childDataObj.getKey(); + if (OpaqueObject.class.isAssignableFrom(retClass)) { + // Filter OpaqueObjects, they are not containers + continue; + } + + // Record getter method + daoPropertiesBuilder.put(retClass, new PropertyInfo.Getter(method)); + + final var childProto = getChildPrototype(runtimeType, factory, itemFactory, retClass); + byStreamClassBuilder.put(childProto.getBindingClass(), childProto); + byYangBuilder.put(childProto.getYangArg(), childProto); + + // FIXME: It really feels like we should be specializing DataContainerCodecPrototype so as to ditch + // createInstance() and then we could do an instanceof check instead. + if (childProto.getType() instanceof ChoiceRuntimeType) { + final var choice = (ChoiceCodecContext) childProto.get(); + for (var cazeChild : choice.getCaseChildrenClasses()) { + byBindingArgClassBuilder.put(cazeChild, childProto); + } + } + } + + // Snapshot before below processing + byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder); + + // Slight footprint optimization: we do not want to copy byStreamClass, as that would force its entrySet view + // to be instantiated. Furthermore the two maps can easily end up being equal -- hence we can reuse + // byStreamClass for the purposes of both. + byBindingArgClassBuilder.putAll(byStreamClassBuilder); + byBindingArgClass = byStreamClassBuilder.equals(byBindingArgClassBuilder) ? byStreamClass + : ImmutableMap.copyOf(byBindingArgClassBuilder); + + // Find all non-default nonnullFoo() methods and update the corresponding property info + for (var entry : getChildrenClassToNonnullMethod(bindingClass).entrySet()) { + final var method = entry.getValue(); + if (!method.isDefault()) { + daoPropertiesBuilder.compute(entry.getKey(), (key, value) -> new PropertyInfo.GetterAndNonnull( + verifyNotNull(value, "No getter for %s", key).getterMethod(), method)); + } + } + + // At this point all indexing is done: byYangBuilder should not be referenced + byYang = ImmutableMap.copyOf(byYangBuilder); + daoProperties = ImmutableMap.copyOf(daoPropertiesBuilder); + } + + private static @NonNull CommonDataObjectCodecPrototype getChildPrototype(final CompositeRuntimeType type, + final CodecContextFactory factory, final CodecItemFactory itemFactory, + final Class childClass) { + final var child = type.bindingChild(JavaTypeName.create(childClass)); + if (child == null) { + throw DataContainerCodecContext.childNullException(factory.getRuntimeContext(), childClass, + "Node %s does not have child named %s", type, childClass); + } + final var item = itemFactory.createItem(childClass, child.statement()); + if (child instanceof ContainerLikeRuntimeType containerLike) { + if (child instanceof ContainerRuntimeType container + && container.statement().findFirstEffectiveSubstatement(PresenceEffectiveStatement.class).isEmpty()) { + return new StructuralContainerCodecPrototype(item, container, factory); + } + return new ContainerLikeCodecPrototype(item, containerLike, factory); + } else if (child instanceof ListRuntimeType list) { + return list.keyType() != null ? new MapCodecPrototype(item, list, factory) + : new ListCodecPrototype(item, list, factory); + } else if (child instanceof ChoiceRuntimeType choice) { + return new ChoiceCodecPrototype(item, choice, factory); + } else { + throw new UnsupportedOperationException("Unhandled type " + child); + } + } + + + // FIXME: MDSAL-780: these methods perform analytics using java.lang.reflect to acquire the basic shape of the + // class. This is not exactly AOT friendly, as most of the information should be provided by + // CompositeRuntimeType. + // + // As as first cut, CompositeRuntimeType should provide the mapping between YANG children and the + // corresponding GETTER_PREFIX/NONNULL_PREFIX method names, If we have that, the use in this + // class should be fine. + // + // The second cut is binding the actual Method invocations, which is fine here, as this class is + // all about having a run-time generated class. AOT would be providing an alternative, where the + // equivalent class would be generated at compile-time and hence would bind to the methods + // directly -- and AOT equivalent of this class would really be a compile-time generated registry + // to those classes' entry points. + + /** + * Scans supplied class and returns an iterable of all data children classes. + * + * @param type YANG Modeled Entity derived from DataContainer + * @return Iterable of all data children, which have YANG modeled entity + */ + private static Map, Method> getChildrenClassToMethod(final Class type) { + return getChildClassToMethod(type, Naming.GETTER_PREFIX); + } + + private static Map, Method> getChildrenClassToNonnullMethod(final Class type) { + return getChildClassToMethod(type, Naming.NONNULL_PREFIX); + } + + private static Map, Method> getChildClassToMethod(final Class type, + final String prefix) { + checkArgument(type != null, "Target type must not be null"); + checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type %s must be derived from DataContainer", + type); + final var ret = new HashMap, Method>(); + for (Method method : type.getMethods()) { + getYangModeledReturnType(method, prefix).ifPresent(entity -> ret.put(entity, method)); + } + return ret; + } + + static final Optional> getYangModeledReturnType(final Method method, + final String prefix) { + final String methodName = method.getName(); + if ("getClass".equals(methodName) || !methodName.startsWith(prefix) || method.getParameterCount() > 0) { + return Optional.empty(); + } + + final Class returnType = method.getReturnType(); + if (DataContainer.class.isAssignableFrom(returnType)) { + return optionalDataContainer(returnType); + } else if (List.class.isAssignableFrom(returnType)) { + return getYangModeledReturnType(method, 0); + } else if (Map.class.isAssignableFrom(returnType)) { + return getYangModeledReturnType(method, 1); + } + return Optional.empty(); + } + + @SuppressWarnings("checkstyle:illegalCatch") + private static Optional> getYangModeledReturnType(final Method method, + final int parameterOffset) { + try { + return ClassLoaderUtils.callWithClassLoader(method.getDeclaringClass().getClassLoader(), + () -> genericParameter(method.getGenericReturnType(), parameterOffset) + .flatMap(result -> result instanceof Class ? optionalCast((Class) result) : Optional.empty())); + } catch (Exception e) { + /* + * It is safe to log this this exception on debug, since this + * method should not fail. Only failures are possible if the + * runtime / backing. + */ + LOG.debug("Unable to find YANG modeled return type for {}", method, e); + } + return Optional.empty(); + } + + private static Optional genericParameter(final java.lang.reflect.Type type, + final int offset) { + if (type instanceof ParameterizedType parameterized) { + final var parameters = parameterized.getActualTypeArguments(); + if (parameters.length > offset) { + return Optional.of(parameters[offset]); + } + } + return Optional.empty(); + } + + private static Optional> optionalCast(final Class type) { + return DataContainer.class.isAssignableFrom(type) ? optionalDataContainer(type) : Optional.empty(); + } + + private static Optional> optionalDataContainer(final Class type) { + return Optional.of(type.asSubclass(DataContainer.class)); + } +} 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 243d0f795f..95c156a452 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 @@ -317,10 +317,8 @@ 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()) { - final var entity = getYangModeledReturnType(method, Naming.GETTER_PREFIX); - if (entity.isPresent()) { - ret.add((Class) entity.orElseThrow()); - } + AbstractDataContainerAnalysis.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 index dcc4e13a26..9ecf70678e 100644 --- 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 @@ -7,128 +7,43 @@ */ package org.opendaylight.mdsal.binding.dom.codec.impl; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Verify.verify; -import static com.google.common.base.Verify.verifyNotNull; - import com.google.common.base.VerifyException; -import com.google.common.collect.ImmutableMap; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; 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.opendaylight.mdsal.binding.model.api.JavaTypeName; import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType; import org.opendaylight.mdsal.binding.runtime.api.AugmentableRuntimeType; -import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType; import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType; -import org.opendaylight.mdsal.binding.runtime.api.ContainerLikeRuntimeType; -import org.opendaylight.mdsal.binding.runtime.api.ContainerRuntimeType; -import org.opendaylight.mdsal.binding.runtime.api.ListRuntimeType; import org.opendaylight.yangtools.yang.binding.Augmentable; -import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.OpaqueObject; -import org.opendaylight.yangtools.yang.binding.contract.Naming; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; -import org.opendaylight.yangtools.yang.model.api.stmt.PresenceEffectiveStatement; /** * 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 { +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 ImmutableMap, CommonDataObjectCodecPrototype> byStreamClass; - final @NonNull ImmutableMap, CommonDataObjectCodecPrototype> byBindingArgClass; - final @NonNull ImmutableMap byYang; - final @NonNull ImmutableMap leafNodes; final @NonNull Class> generatedClass; final @NonNull List possibleAugmentations; final @NonNull MethodHandle proxyConstructor; CodecDataObjectAnalysis(final CommonDataObjectCodecPrototype prototype, final CodecItemFactory itemFactory, final Method keyMethod) { - // Preliminaries from prototype - final var bindingClass = prototype.getBindingClass(); - final var runtimeType = prototype.getType(); - final var factory = prototype.getFactory(); - final var leafContexts = factory.getLeafNodes(bindingClass, runtimeType.statement()); - - // Reflection-based on the passed class - final var clsToMethod = getChildrenClassToMethod(bindingClass); - - // Indexing part: be very careful around what gets done when - final var byYangBuilder = new HashMap(); - - // Step 1: add leaf children - var leafBuilder = ImmutableMap.builderWithExpectedSize(leafContexts.size()); - for (var leaf : leafContexts.values()) { - leafBuilder.put(leaf.getSchema().getQName().getLocalName(), leaf); - byYangBuilder.put(leaf.getDomPathArgument(), leaf); - } - leafNodes = leafBuilder.build(); - - final var byBindingArgClassBuilder = new HashMap, CommonDataObjectCodecPrototype>(); - final var byStreamClassBuilder = new HashMap, CommonDataObjectCodecPrototype>(); - final var daoProperties = new HashMap, PropertyInfo>(); - for (var childDataObj : clsToMethod.entrySet()) { - final var method = childDataObj.getValue(); - verify(!method.isDefault(), "Unexpected default method %s in %s", method, bindingClass); - - final var retClass = childDataObj.getKey(); - if (OpaqueObject.class.isAssignableFrom(retClass)) { - // Filter OpaqueObjects, they are not containers - continue; - } - - // Record getter method - daoProperties.put(retClass, new PropertyInfo.Getter(method)); - - final var childProto = getChildPrototype(runtimeType, factory, itemFactory, retClass); - byStreamClassBuilder.put(childProto.getBindingClass(), childProto); - byYangBuilder.put(childProto.getYangArg(), childProto); - - // FIXME: It really feels like we should be specializing DataContainerCodecPrototype so as to ditch - // createInstance() and then we could do an instanceof check instead. - if (childProto.getType() instanceof ChoiceRuntimeType) { - final var choice = (ChoiceCodecContext) childProto.get(); - for (var cazeChild : choice.getCaseChildrenClasses()) { - byBindingArgClassBuilder.put(cazeChild, childProto); - } - } - } - - // Snapshot before below processing - byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder); + this(prototype.getBindingClass(), prototype.getType(), prototype.getFactory(), itemFactory, keyMethod); + } - // Slight footprint optimization: we do not want to copy byStreamClass, as that would force its entrySet view - // to be instantiated. Furthermore the two maps can easily end up being equal -- hence we can reuse - // byStreamClass for the purposes of both. - byBindingArgClassBuilder.putAll(byStreamClassBuilder); - byBindingArgClass = byStreamClassBuilder.equals(byBindingArgClassBuilder) ? byStreamClass - : ImmutableMap.copyOf(byBindingArgClassBuilder); - - // Find all non-default nonnullFoo() methods and update the corresponding property info - for (var entry : getChildrenClassToNonnullMethod(bindingClass).entrySet()) { - final var method = entry.getValue(); - if (!method.isDefault()) { - daoProperties.compute(entry.getKey(), (key, value) -> new PropertyInfo.GetterAndNonnull( - verifyNotNull(value, "No getter for %s", key).getterMethod(), method)); - } - } - // At this point all indexing is done: byYangBuilder should not be referenced - byYang = ImmutableMap.copyOf(byYangBuilder); + 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)) { @@ -157,71 +72,4 @@ final class CodecDataObjectAnalysis { proxyConstructor = ctor.asType(DATAOBJECT_TYPE); } - - private static @NonNull CommonDataObjectCodecPrototype getChildPrototype(final CompositeRuntimeType type, - final CodecContextFactory factory, final CodecItemFactory itemFactory, - final Class childClass) { - final var child = type.bindingChild(JavaTypeName.create(childClass)); - if (child == null) { - throw DataContainerCodecContext.childNullException(factory.getRuntimeContext(), childClass, - "Node %s does not have child named %s", type, childClass); - } - final var item = itemFactory.createItem(childClass, child.statement()); - if (child instanceof ContainerLikeRuntimeType containerLike) { - if (child instanceof ContainerRuntimeType container - && container.statement().findFirstEffectiveSubstatement(PresenceEffectiveStatement.class).isEmpty()) { - return new StructuralContainerCodecPrototype(item, container, factory); - } - return new ContainerLikeCodecPrototype(item, containerLike, factory); - } else if (child instanceof ListRuntimeType list) { - return list.keyType() != null ? new MapCodecPrototype(item, list, factory) - : new ListCodecPrototype(item, list, factory); - } else if (child instanceof ChoiceRuntimeType choice) { - return new ChoiceCodecPrototype(item, choice, factory); - } else { - throw new UnsupportedOperationException("Unhandled type " + child); - } - } - - - // FIXME: MDSAL-780: these methods perform analytics using java.lang.reflect to acquire the basic shape of the - // class. This is not exactly AOT friendly, as most of the information should be provided by - // CompositeRuntimeType. - // - // As as first cut, CompositeRuntimeType should provide the mapping between YANG children and the - // corresponding GETTER_PREFIX/NONNULL_PREFIX method names, If we have that, the use in this - // class should be fine. - // - // The second cut is binding the actual Method invocations, which is fine here, as this class is - // all about having a run-time generated class. AOT would be providing an alternative, where the - // equivalent class would be generated at compile-time and hence would bind to the methods - // directly -- and AOT equivalent of this class would really be a compile-time generated registry - // to those classes' entry points. - - /** - * Scans supplied class and returns an iterable of all data children classes. - * - * @param type YANG Modeled Entity derived from DataContainer - * @return Iterable of all data children, which have YANG modeled entity - */ - private static Map, Method> getChildrenClassToMethod(final Class type) { - return getChildClassToMethod(type, Naming.GETTER_PREFIX); - } - - private static Map, Method> getChildrenClassToNonnullMethod(final Class type) { - return getChildClassToMethod(type, Naming.NONNULL_PREFIX); - } - - private static Map, Method> getChildClassToMethod(final Class type, - final String prefix) { - checkArgument(type != null, "Target type must not be null"); - checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type %s must be derived from DataContainer", - type); - final var ret = new HashMap, Method>(); - for (Method method : type.getMethods()) { - DataContainerCodecContext.getYangModeledReturnType(method, prefix) - .ifPresent(entity -> ret.put(entity, method)); - } - return ret; - } } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java index 2caa3667ac..e925b9ecd3 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java @@ -17,11 +17,9 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; import java.util.Arrays; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import org.eclipse.jdt.annotation.NonNull; @@ -267,63 +265,6 @@ abstract sealed class DataContainerCodecContext> getYangModeledReturnType(final Method method, - final String prefix) { - final String methodName = method.getName(); - if ("getClass".equals(methodName) || !methodName.startsWith(prefix) || method.getParameterCount() > 0) { - return Optional.empty(); - } - - final Class returnType = method.getReturnType(); - if (DataContainer.class.isAssignableFrom(returnType)) { - return optionalDataContainer(returnType); - } else if (List.class.isAssignableFrom(returnType)) { - return getYangModeledReturnType(method, 0); - } else if (Map.class.isAssignableFrom(returnType)) { - return getYangModeledReturnType(method, 1); - } - return Optional.empty(); - } - - @SuppressWarnings("checkstyle:illegalCatch") - private static Optional> getYangModeledReturnType(final Method method, - final int parameterOffset) { - try { - return ClassLoaderUtils.callWithClassLoader(method.getDeclaringClass().getClassLoader(), - () -> genericParameter(method.getGenericReturnType(), parameterOffset) - .flatMap(result -> result instanceof Class ? optionalCast((Class) result) : Optional.empty())); - } catch (Exception e) { - /* - * It is safe to log this this exception on debug, since this - * method should not fail. Only failures are possible if the - * runtime / backing. - */ - LOG.debug("Unable to find YANG modeled return type for {}", method, e); - } - return Optional.empty(); - } - - private static Optional genericParameter(final java.lang.reflect.Type type, - final int offset) { - if (type instanceof ParameterizedType parameterized) { - final var parameters = parameterized.getActualTypeArguments(); - if (parameters.length > offset) { - return Optional.of(parameters[offset]); - } - } - return Optional.empty(); - } - - private static Optional> optionalCast(final Class type) { - return DataContainer.class.isAssignableFrom(type) ? optionalDataContainer(type) : Optional.empty(); - } - - // FIXME: MDSAL-780: remove this method - static final Optional> optionalDataContainer(final Class type) { - return Optional.of(type.asSubclass(DataContainer.class)); - } - /** * Determines if two augmentation classes or case classes represents same data. * -- 2.36.6