Specialize JSONCodec to JSONValueWriter
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / ClassLoaderUtils.java
index 44dfc4526e6f596ca42e1a08e5fcd5a67939ff68..db136459a6aa6288156a50d63465b77076750f2c 100644 (file)
  */
 package org.opendaylight.yangtools.util;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
 
-import com.google.common.base.Joiner;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Supplier;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.Callable;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.eclipse.jdt.annotation.NonNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Utility methods for working with ClassLoaders and classes.
+ */
 public final class ClassLoaderUtils {
     private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderUtils.class);
 
     private ClassLoaderUtils() {
-        throw new UnsupportedOperationException("Utility class");
+        // Hidden on purpose
     }
 
     /**
+     * Immediately call {@link Function#apply(Object)} with provided {@link ClassLoader}. This method safely switches
+     * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
+     * method.
      *
-     * Runs {@link Supplier} with provided {@link ClassLoader}.
-     *
-     * Invokes supplies function and makes sure that original {@link ClassLoader}
-     * is context {@link ClassLoader} after execution.
-     *
+     * @param <T> the type of the input to the function
+     * @param <R> the type of the result of the function
      * @param cls {@link ClassLoader} to be used.
-     * @param function Function to be executed.
-     * @return Result of supplier invocation.
-     *
+     * @param function Function to be applied.
+     * @param input Function input
+     * @return Result of function invocation.
+     * @throws NullPointerException if class loader or function is null
      */
-    public static <V> V withClassLoader(final ClassLoader cls, final Supplier<V> function) {
-        checkNotNull(cls, "Classloader should not be null");
-        checkNotNull(function, "Function should not be null");
-
-        final ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
+    public static <T, R> R applyWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Function<T, R> function,
+            final T input) {
+        final var currentThread = Thread.currentThread();
+        final var oldCls = currentThread.getContextClassLoader();
+        currentThread.setContextClassLoader(requireNonNull(cls));
         try {
-            Thread.currentThread().setContextClassLoader(cls);
-            return function.get();
+            return requireNonNull(function).apply(input);
         } finally {
-            Thread.currentThread().setContextClassLoader(oldCls);
+            currentThread.setContextClassLoader(oldCls);
         }
     }
 
     /**
+     * Immediately call {@link Callable#call()} with provided {@link ClassLoader}. This method safely switches
+     * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
+     * method.
      *
-     * Runs {@link Callable} with provided {@link ClassLoader}.
-     *
-     * Invokes supplies function and makes sure that original {@link ClassLoader}
-     * is context {@link ClassLoader} after execution.
-     *
+     * @param <V> the result type of the callable
      * @param cls {@link ClassLoader} to be used.
-     * @param function Function to be executed.
+     * @param callable Function to be executed.
      * @return Result of callable invocation.
-     *
+     * @throws NullPointerException if class loader or callable is null
+     * @throws Exception if the callable fails
      */
-    public static <V> V withClassLoader(final ClassLoader cls, final Callable<V> function) throws Exception {
-        checkNotNull(cls, "Classloader should not be null");
-        checkNotNull(function, "Function should not be null");
-
-        final ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
+    public static <V> V callWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Callable<V> callable)
+            throws Exception {
+        final var currentThread = Thread.currentThread();
+        final var oldCls = currentThread.getContextClassLoader();
+        currentThread.setContextClassLoader(requireNonNull(cls));
         try {
-            Thread.currentThread().setContextClassLoader(cls);
-            return function.call();
+            return requireNonNull(callable).call();
         } finally {
-            Thread.currentThread().setContextClassLoader(oldCls);
+            currentThread.setContextClassLoader(oldCls);
         }
     }
 
-    public static Object construct(final Constructor<? extends Object> constructor, final List<Object> objects)
-            throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
-        final Object[] initargs = objects.toArray();
-        return constructor.newInstance(initargs);
-    }
-
     /**
+     * Immediately call {@link Supplier#get()} with provided {@link ClassLoader}. This method safely switches
+     * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
+     * method.
      *
-     * Loads class using this supplied classloader.
-     *
-     *
-     * @param cls
-     * @param name String name of class.
-     * @return
-     * @throws ClassNotFoundException
+     * @param <V> the result type of the supplier
+     * @param cls {@link ClassLoader} to be used.
+     * @param supplier Function to be executed.
+     * @return Result of supplier invocation.
+     * @throws NullPointerException if class loader or supplier is null
      */
-    public static Class<?> loadClass(final ClassLoader cls, final String name) throws ClassNotFoundException {
-        if ("byte[]".equals(name)) {
-            return byte[].class;
-        }
-        if ("char[]".equals(name)) {
-            return char[].class;
-        }
-        return loadClass0(cls,name);
-    }
-
-    private static Class<?> loadClass0(final ClassLoader cls, final String name) throws ClassNotFoundException {
+    public static <V> V getWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Supplier<V> supplier) {
+        final var currentThread = Thread.currentThread();
+        final var oldCls = currentThread.getContextClassLoader();
+        currentThread.setContextClassLoader(requireNonNull(cls));
         try {
-            return cls.loadClass(name);
-        } catch (final ClassNotFoundException e) {
-            final String[] components = name.split("\\.");
-
-            if (isInnerClass(components)) {
-                final int length = components.length;
-                final String outerName = Joiner.on(".").join(Arrays.asList(components).subList(0, length - 1));
-                final String innerName = outerName + "$" + components[length-1];
-                return cls.loadClass(innerName);
-            } else {
-                throw e;
-            }
-        }
-    }
-
-    private static boolean isInnerClass(final String[] components) {
-        final int length = components.length;
-        if(length < 2) {
-            return false;
-        }
-        final String potentialOuter = components[length - 2];
-        if(potentialOuter == null) {
-            return false;
+            return requireNonNull(supplier).get();
+        } finally {
+            currentThread.setContextClassLoader(oldCls);
         }
-        return Character.isUpperCase(potentialOuter.charAt(0));
     }
 
-    public static Class<?> loadClassWithTCCL(final String name) throws ClassNotFoundException {
-        return loadClass(Thread.currentThread().getContextClassLoader(), name);
-    }
-
-    public static Class<?> tryToLoadClassWithTCCL(final String fullyQualifiedName) {
+    /**
+     * Immediately call {@link Runnable#run()} with provided {@link ClassLoader}. This method safely switches
+     * the thread's Thread Context Class Loader to the specified class loader for the duration of execution of that
+     * method.
+     *
+     * @param cls {@link ClassLoader} to be used.
+     * @param runnable Function to be executed.
+     * @throws NullPointerException if class loader or runnable is null
+     */
+    public static void runWithClassLoader(final @NonNull ClassLoader cls, final @NonNull Runnable runnable) {
+        final var currentThread = Thread.currentThread();
+        final var oldCls = currentThread.getContextClassLoader();
+        currentThread.setContextClassLoader(requireNonNull(cls));
         try {
-            return loadClassWithTCCL(fullyQualifiedName);
-        } catch (final ClassNotFoundException e) {
-            LOG.debug("Failed to load class {}", fullyQualifiedName, e);
-            return null;
+            requireNonNull(runnable).run();
+        } finally {
+            currentThread.setContextClassLoader(oldCls);
         }
     }
 
-    public static <S,G,P> Class<P> findFirstGenericArgument(final Class<S> scannedClass, final Class<G> genericType) {
-        return withClassLoader(scannedClass.getClassLoader(), ClassLoaderUtils.<S,G,P>findFirstGenericArgumentTask(scannedClass, genericType));
-    }
-
-    private static <S,G,P> Supplier<Class<P>> findFirstGenericArgumentTask(final Class<S> scannedClass, final Class<G> genericType) {
-        return new Supplier<Class<P>>() {
-            @Override
-            @SuppressWarnings("unchecked")
-            public Class<P> get() {
-                final ParameterizedType augmentationGeneric = findParameterizedType(scannedClass, genericType);
-                if (augmentationGeneric != null) {
-                    return (Class<P>) augmentationGeneric.getActualTypeArguments()[0];
-                }
-                return null;
-            }
-        };
+    @SuppressWarnings("unchecked")
+    public static <S, G, P> Optional<Class<P>> findFirstGenericArgument(final Class<S> scannedClass,
+            final Class<G> genericType) {
+        return getWithClassLoader(scannedClass.getClassLoader(),
+            () -> findParameterizedType(scannedClass, genericType)
+                .map(ptype -> (Class<P>) ptype.getActualTypeArguments()[0]));
     }
 
-    public static ParameterizedType findParameterizedType(final Class<?> subclass, final Class<?> genericType) {
-        Preconditions.checkNotNull(subclass);
-        Preconditions.checkNotNull(genericType);
+    /**
+     * Find the parameterized instantiation of a particular interface implemented by a class.
+     *
+     * @param subclass Implementing class
+     * @param genericType Interface to search for
+     * @return Parameterized interface as implemented by the class, if present
+     */
+    public static Optional<ParameterizedType> findParameterizedType(final Class<?> subclass,
+            final Class<?> genericType) {
+        requireNonNull(genericType);
 
-        for (final Type type : subclass.getGenericInterfaces()) {
-            if (type instanceof ParameterizedType && genericType.equals(((ParameterizedType) type).getRawType())) {
-                return (ParameterizedType) type;
+        for (var type : subclass.getGenericInterfaces()) {
+            if (type instanceof ParameterizedType ptype && genericType.equals(ptype.getRawType())) {
+                return Optional.of(ptype);
             }
         }
 
         LOG.debug("Class {} does not declare interface {}", subclass, genericType);
-        return null;
+        return Optional.empty();
     }
 
-    public static Type getFirstGenericParameter(final Type type) {
-        if (type instanceof ParameterizedType) {
-            return ((ParameterizedType) type).getActualTypeArguments()[0];
-        }
-        return null;
+    /**
+     * Extract the first generic type argument for a Type. If the type is not parameterized, this method returns empty.
+     *
+     * @param type Type to examine
+     * @return First generic type argument, if present
+     * @throws NullPointerException if {@code type} is null
+     */
+    public static Optional<Type> getFirstGenericParameter(final Type type) {
+        requireNonNull(type);
+        return type instanceof ParameterizedType ptype ? Optional.of(ptype.getActualTypeArguments()[0])
+            : Optional.empty();
     }
 }