![JAR search and dependency download from the Maven repository](/logo.png)
io.helidon.config.objectmapping.ReflectionUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of helidon-config-object-mapping Show documentation
Show all versions of helidon-config-object-mapping Show documentation
Support for loading java pojos from configuration.
The newest version!
/*
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
*
* 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
*
* http://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.helidon.config.objectmapping;
import java.lang.System.Logger.Level;
import java.lang.invoke.MethodType;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import io.helidon.config.Config;
import io.helidon.config.ConfigException;
import io.helidon.config.ConfigMappingException;
/**
* Utilities for reflective access.
*/
final class ReflectionUtil {
private static final Map, Class>> REPLACED_TYPES = new HashMap<>();
private static final System.Logger LOGGER = System.getLogger(ReflectionUtil.class.getName());
private static final String METHOD_BUILDER = "builder";
private static final String METHOD_BUILD = "build";
private static final String CLASS_BUILDER = "Builder";
static {
REPLACED_TYPES.put(byte.class, Byte.class);
REPLACED_TYPES.put(short.class, Short.class);
REPLACED_TYPES.put(int.class, Integer.class);
REPLACED_TYPES.put(long.class, Long.class);
REPLACED_TYPES.put(float.class, Float.class);
REPLACED_TYPES.put(double.class, Double.class);
REPLACED_TYPES.put(boolean.class, Boolean.class);
REPLACED_TYPES.put(char.class, Character.class);
}
private ReflectionUtil() {
}
@SuppressWarnings("unchecked")
static Class supportedType(Class type) {
return (Class) REPLACED_TYPES.getOrDefault(type, type);
}
static Optional findStaticMethod(Class> type, String methodName, Class>... parameterTypes) {
try {
Method method = type.getMethod(methodName, parameterTypes);
if (checkMethod(method, true, type, methodName, parameterTypes.length > 0)) {
return Optional.of(HelidonMethodHandle.create(type, method));
} else {
LOGGER.log(Level.TRACE,
() -> "Class " + type.getName() + " method '" + methodName
+ "' with parameters " + Arrays.toString(parameterTypes) + " cannot be used.");
}
} catch (NoSuchMethodException ex) {
LOGGER.log(Level.TRACE,
() -> "Class " + type.getName() + " does not have a method named '" + methodName
+ "' with parameters " + Arrays.toString(parameterTypes) + ".",
ex);
}
return Optional.empty();
}
static Optional findConstructor(Class> type, Class>... parameterTypes) {
try {
Constructor> constructor = type.getConstructor(parameterTypes);
if (checkConstructor(constructor, parameterTypes.length > 0)) {
return Optional.of(HelidonMethodHandle.create(type, constructor));
} else {
LOGGER.log(Level.TRACE,
() -> "Class " + type.getName() + " constructor with parameters "
+ Arrays.toString(parameterTypes)
+ " cannot be used.");
}
} catch (NoSuchMethodException ex) {
LOGGER.log(Level.TRACE,
() -> "Class " + type.getName() + " does not have a constructor with parameters "
+ Arrays.toString(parameterTypes) + ".",
ex);
}
return Optional.empty();
}
/**
* Find builder method that conforms to design pattern of builder.
* e.g. Type t = Type.builder().build();
*/
static Optional> findBuilderMethod(Class type) {
return findMethod(type, METHOD_BUILDER, true, null)
.flatMap(builderMethod -> {
HelidonMethodHandle builderHandler = HelidonMethodHandle.create(type, builderMethod);
return findBuilderBuildHandler(type, builderMethod.getReturnType()).map(
buildHandler ->
new BuilderAccessor<>(builderMethod.getReturnType(),
builderHandler,
type,
buildHandler));
});
}
/**
* Find builder constructor that conforms to design pattern of builder.
* e.g. Type.Builder builder = new Type.Builder();
*/
static Optional> findBuilderConstructor(Class type) {
Class>[] declaredClasses = type.getDeclaredClasses();
for (Class> declaredClass : declaredClasses) {
if (CLASS_BUILDER.equals(declaredClass.getSimpleName())) {
return findConstructor(declaredClass)
.flatMap(constructor -> findBuilderBuildHandler(type, declaredClass)
.map(buildHandler -> new BuilderAccessor<>(declaredClass,
constructor,
type,
buildHandler)
));
}
}
return Optional.empty();
}
static Optional> findStaticMethodWithParameters(Class type, String methodName) {
AtomicReference foundMethod = new AtomicReference<>();
for (Method method : type.getMethods()) {
if (checkMethod(method, true, type, methodName, true)) {
if (foundMethod.get() != null) {
LOGGER.log(Level.WARNING,
() -> "Class " + type.getName() + " contains more than one static factory method '"
+ methodName + "' with parameters. Any will be used to initialize the type.");
return Optional.empty();
}
foundMethod.set(method);
}
}
if (foundMethod.get() == null) {
return Optional.empty();
} else {
return findStaticMethod(type, methodName, foundMethod.get().getParameterTypes())
.map(handle -> new FactoryAccessor<>(type,
handle,
foundMethod.get().getParameters()));
}
}
static Optional> findConstructorWithParameters(Class type) {
AtomicReference> foundConstructor = new AtomicReference<>();
for (Constructor> constructor : type.getConstructors()) {
if (checkConstructor(constructor, true)) {
if (foundConstructor.get() != null) {
LOGGER.log(Level.WARNING,
() -> "Class " + type.getName() + " contains more than one constructor with parameters."
+ " Any will be used to initialize the type.");
return Optional.empty();
}
foundConstructor.set(constructor);
}
}
if (foundConstructor.get() == null) {
return Optional.empty();
} else {
return findConstructor(type, foundConstructor.get().getParameterTypes())
.map(handle -> new FactoryAccessor<>(type,
handle,
foundConstructor.get().getParameters()));
}
}
static Optional findBuilderBuildHandler(Class type, Class> builderType) {
return findMethod(builderType, METHOD_BUILD, false, type)
.map(it -> HelidonMethodHandle.create(type, it));
}
/**
* Check if constructor can be used for deserialization.
*/
private static boolean checkConstructor(Constructor> constructor, boolean hasParams) {
return Modifier.isPublic(constructor.getModifiers())
&& !constructor.isAnnotationPresent(Transient.class)
&& ((constructor.getParameterCount() > 0) == hasParams);
}
/**
* Check if method can be used for deserialization.
*/
private static boolean checkMethod(Method method, boolean isStatic, Class> returnType, String name, boolean hasParams) {
return Modifier.isPublic(method.getModifiers())
&& (Modifier.isStatic(method.getModifiers()) == isStatic)
&& !method.isAnnotationPresent(Transient.class)
&& method.getName().equals(name)
&& ((returnType == null) || returnType.isAssignableFrom(method.getReturnType()))
&& ((method.getParameterCount() > 0) == hasParams);
}
private static Optional findMethod(Class> type, String methodName, boolean isStatic, Class> returnType,
Class>... parameterTypes) {
try {
Method method = type.getMethod(methodName, parameterTypes);
if (checkMethod(method, isStatic, returnType, methodName, parameterTypes.length > 0)) {
return Optional.of(method);
} else {
LOGGER.log(Level.TRACE,
() -> "Class " + type.getName() + " method '" + methodName
+ "' with parameters " + Arrays.toString(parameterTypes) + " cannot be used.");
}
} catch (NoSuchMethodException ex) {
LOGGER.log(Level.TRACE,
() -> "Class " + type.getName() + " does not have a method named '" + methodName
+ "' with parameters " + Arrays.toString(parameterTypes) + ".",
ex);
}
return Optional.empty();
}
/**
* The class covers work with factory method.
*
* @param type of target java bean
*/
static class FactoryAccessor {
private final Class type;
private final HelidonMethodHandle handle;
private final LinkedHashMap> parameterValueProviders;
FactoryAccessor(Class type,
HelidonMethodHandle handle,
Parameter[] parameters) {
this.type = type;
this.handle = handle;
this.parameterValueProviders = initParameterValueProviders(parameters);
}
public T create(Config configNode) {
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy