Move BindingReflections.getChildrenClass*
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / DataContainerCodecContext.java
index df089fd1fc7fd3f51f3eda606ec300affe7b46ff..c14693f0d6ac3ae143aaaa3d385b99b9af103c9d 100644 (file)
@@ -15,7 +15,10 @@ import com.google.common.collect.ImmutableSet;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import org.eclipse.jdt.annotation.NonNull;
@@ -31,8 +34,10 @@ import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
+import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.BindingObject;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -43,9 +48,12 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 abstract class DataContainerCodecContext<D extends DataObject, T extends RuntimeTypeContainer> extends NodeCodecContext
         implements BindingDataObjectCodecTreeNode<D>  {
+    private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecContext.class);
     private static final VarHandle EVENT_STREAM_SERIALIZER;
 
     static {
@@ -268,4 +276,62 @@ abstract class DataContainerCodecContext<D extends DataObject, T extends Runtime
             throw new IllegalArgumentException("Expected " + expectedType.getSimpleName(), e);
         }
     }
+
+    // FIXME: MDSAL-780 replace this method with BindingRuntimeTypes-driven logic
+    static final 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) {
+            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<Class<? extends DataContainer>> 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<java.lang.reflect.Type> 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<Class<? extends DataContainer>> optionalCast(final Class<?> type) {
+        return DataContainer.class.isAssignableFrom(type) ? optionalDataContainer(type) : Optional.empty();
+    }
+
+
+    // FIXME: MDSAL-780: remove this method
+    static final Optional<Class<? extends DataContainer>> optionalDataContainer(final Class<?> type) {
+        return Optional.of(type.asSubclass(DataContainer.class));
+    }
 }