org.openrewrite.internal.ReflectionUtils Maven / Gradle / Ivy
Show all versions of rewrite-core Show documentation
/*
* Copyright 2022 the original author or 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 org.openrewrite.internal;
import org.jspecify.annotations.Nullable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Clone of Spring Framework's org.springframework.util.ReflectionUtils, but with a few modifications.
*/
public class ReflectionUtils {
private static final Method[] EMPTY_METHOD_ARRAY = new Method[0];
/**
* Cache for {@link Class#getDeclaredMethods()} plus equivalent default methods
* from Java 8 based interfaces, allowing for fast iteration.
*/
private static final Map, Method[]> declaredMethodsCache = new ConcurrentHashMap<>(256);
public static @Nullable Method findMethod(Class> clazz, String name, Class>... paramTypes) {
Class> searchType = clazz;
while (searchType != null) {
Method[] methods = (searchType.isInterface() ? searchType.getMethods() :
getDeclaredMethods(searchType));
for (Method method : methods) {
if (name.equals(method.getName()) && hasSameParams(method, paramTypes)) {
return method;
}
}
searchType = searchType.getSuperclass();
}
return null;
}
private static Method[] getDeclaredMethods(Class> clazz) {
Method[] result = declaredMethodsCache.get(clazz);
if (result == null) {
try {
Method[] declaredMethods = clazz.getDeclaredMethods();
List defaultMethods = findConcreteMethodsOnInterfaces(clazz);
if (defaultMethods != null) {
result = new Method[declaredMethods.length + defaultMethods.size()];
System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);
int index = declaredMethods.length;
for (Method defaultMethod : defaultMethods) {
result[index] = defaultMethod;
index++;
}
} else {
result = declaredMethods;
}
declaredMethodsCache.put(clazz, (result.length == 0 ? EMPTY_METHOD_ARRAY : result));
} catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
}
}
return result;
}
private static @Nullable List findConcreteMethodsOnInterfaces(Class> clazz) {
List result = null;
for (Class> ifc : clazz.getInterfaces()) {
for (Method ifcMethod : ifc.getMethods()) {
if (!Modifier.isAbstract(ifcMethod.getModifiers())) {
if (result == null) {
result = new ArrayList<>();
}
result.add(ifcMethod);
}
}
}
return result;
}
private static boolean hasSameParams(Method method, Class>[] paramTypes) {
return (paramTypes.length == method.getParameterCount() &&
Arrays.equals(paramTypes, method.getParameterTypes()));
}
}