io.micronaut.core.reflect.ReflectionUtils Maven / Gradle / Ivy
Show all versions of micronaut-core Show documentation
/*
* Copyright 2017-2020 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.core.reflect;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.reflect.exception.InvocationException;
import io.micronaut.core.util.StringUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Utility methods for reflection related tasks. Micronaut tries to avoid using reflection wherever possible,
* this class is therefore considered an internal class and covers edge cases needed by Micronaut, often at compile time.
*
* Do not use in application code.
*
* @author Graeme Rocher
* @since 1.0
*/
@Internal
public class ReflectionUtils {
/**
* Constant for empty class array.
*/
@UsedByGeneratedCode
public static final Class>[] EMPTY_CLASS_ARRAY = new Class>[0];
private static final Map, Class>> PRIMITIVES_TO_WRAPPERS;
static {
LinkedHashMap, Class>> m = new LinkedHashMap<>();
m.put(boolean.class, Boolean.class);
m.put(byte.class, Byte.class);
m.put(char.class, Character.class);
m.put(double.class, Double.class);
m.put(float.class, Float.class);
m.put(int.class, Integer.class);
m.put(long.class, Long.class);
m.put(short.class, Short.class);
m.put(void.class, Void.class);
PRIMITIVES_TO_WRAPPERS = Collections.unmodifiableMap(m);
}
private static final Map, Class>> WRAPPER_TO_PRIMITIVE;
static {
LinkedHashMap, Class>> m = new LinkedHashMap<>();
m.put(Boolean.class, boolean.class);
m.put(Byte.class, byte.class);
m.put(Character.class, char.class);
m.put(Double.class, double.class);
m.put(Float.class, float.class);
m.put(Integer.class, int.class);
m.put(Long.class, long.class);
m.put(Short.class, short.class);
m.put(Void.class, void.class);
WRAPPER_TO_PRIMITIVE = Collections.unmodifiableMap(m);
}
/**
* Is the method a setter.
*
* @param name The method name
* @param args The arguments
* @return True if it is
*/
public static boolean isSetter(String name, Class>[] args) {
if (StringUtils.isEmpty(name) || args == null) {
return false;
}
if (args.length != 1) {
return false;
}
return NameUtils.isSetterName(name);
}
/**
* Obtain the wrapper type for the given primitive.
*
* @param primitiveType The primitive type
* @return The wrapper type
*/
public static Class> getWrapperType(Class> primitiveType) {
if (primitiveType.isPrimitive()) {
return PRIMITIVES_TO_WRAPPERS.get(primitiveType);
}
return primitiveType;
}
/**
* Obtain the primitive type for the given wrapper type.
*
* @param wrapperType The primitive type
* @return The wrapper type
*/
public static Class> getPrimitiveType(Class> wrapperType) {
Class> wrapper = WRAPPER_TO_PRIMITIVE.get(wrapperType);
if (wrapper != null) {
return wrapper;
}
return wrapperType;
}
/**
* Obtains a declared method.
*
* @param type The type
* @param methodName The method name
* @param argTypes The argument types
* @return The method
*/
public static Optional getDeclaredMethod(Class> type, String methodName, Class>... argTypes) {
try {
return Optional.of(type.getDeclaredMethod(methodName, argTypes));
} catch (NoSuchMethodException e) {
return Optional.empty();
}
}
/**
* Obtains a method.
*
* @param type The type
* @param methodName The method name
* @param argTypes The argument types
* @return An optional {@link Method}
*/
public static Optional getMethod(Class> type, String methodName, Class>... argTypes) {
try {
return Optional.of(type.getMethod(methodName, argTypes));
} catch (NoSuchMethodException e) {
return findMethod(type, methodName, argTypes);
}
}
/**
* Obtains a declared method.
*
* @param type The type
* @param argTypes The argument types
* @param The generic type
* @return The method
*/
public static Optional> findConstructor(Class type, Class>... argTypes) {
try {
return Optional.of(type.getDeclaredConstructor(argTypes));
} catch (NoSuchMethodException e) {
return Optional.empty();
}
}
/**
* Invokes a method.
*
* @param instance The instance
* @param method The method
* @param arguments The arguments
* @param The return type
* @param The instance type
* @return The result
*/
public static R invokeMethod(T instance, Method method, Object... arguments) {
try {
return (R) method.invoke(instance, arguments);
} catch (IllegalAccessException e) {
throw new InvocationException("Illegal access invoking method [" + method + "]: " + e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new InvocationException("Exception occurred invoking method [" + method + "]: " + e.getMessage(), e);
}
}
/**
* Finds a method on the given type for the given name.
*
* @param type The type
* @param name The name
* @param argumentTypes The argument types
* @return An {@link Optional} contains the method or empty
*/
@Internal
public static Optional findMethod(Class> type, String name, Class>... argumentTypes) {
Class> currentType = type;
while (currentType != null) {
Method[] methods = currentType.isInterface() ? currentType.getMethods() : currentType.getDeclaredMethods();
for (Method method : methods) {
if (name.equals(method.getName()) && Arrays.equals(argumentTypes, method.getParameterTypes())) {
return Optional.of(method);
}
}
currentType = currentType.getSuperclass();
}
return Optional.empty();
}
/**
* Finds a method on the given type for the given name.
*
* @param type The type
* @param name The name
* @param argumentTypes The argument types
* @return An {@link Optional} contains the method or empty
*/
@UsedByGeneratedCode
@Internal
public static Method getRequiredMethod(Class> type, String name, Class>... argumentTypes) {
try {
return type.getDeclaredMethod(name, argumentTypes);
} catch (NoSuchMethodException e) {
return findMethod(type, name, argumentTypes)
.orElseThrow(() -> newNoSuchMethodError(type, name, argumentTypes));
}
}
/**
* Finds an internal method defined by the Micronaut API and throws a {@link NoSuchMethodError} if it doesn't exist.
*
* @param type The type
* @param name The name
* @param argumentTypes The argument types
* @return An {@link Optional} contains the method or empty
* @throws NoSuchMethodError If the method doesn't exist
*/
@Internal
public static Method getRequiredInternalMethod(Class> type, String name, Class>... argumentTypes) {
try {
return type.getDeclaredMethod(name, argumentTypes);
} catch (NoSuchMethodException e) {
return findMethod(type, name, argumentTypes)
.orElseThrow(() -> newNoSuchMethodInternalError(type, name, argumentTypes));
}
}
/**
* Finds an internal constructor defined by the Micronaut API and throws a {@link NoSuchMethodError} if it doesn't exist.
*
* @param type The type
* @param argumentTypes The argument types
* @param The type
* @return An {@link Optional} contains the method or empty
* @throws NoSuchMethodError If the method doesn't exist
*/
@Internal
public static Constructor getRequiredInternalConstructor(Class type, Class>... argumentTypes) {
try {
return type.getDeclaredConstructor(argumentTypes);
} catch (NoSuchMethodException e) {
throw newNoSuchConstructorInternalError(type, argumentTypes);
}
}
/**
* Finds a field on the given type for the given name.
*
* @param type The type
* @param name The name
* @return An {@link Optional} contains the method or empty
*/
@Internal
public static Field getRequiredField(Class> type, String name) {
try {
return type.getDeclaredField(name);
} catch (NoSuchFieldException e) {
Optional field = findField(type, name);
return field.orElseThrow(() -> new NoSuchFieldError("No field '" + name + "' found for type: " + type.getName()));
}
}
/**
* Finds field's value or return an empty if exception occurs or if the value is null.
*
* @param fieldOwnerClass The field owner class
* @param fieldName The field name
* @param instance The instance
* @return An {@link Optional} contains the value or empty of the value is null or an error occurred
* @since 4.0.0
*/
@Internal
public static Optional