blob: a5ea0d6bffbe113c65af51b1a69276834046be37 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.dubbo.common.utils;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static org.apache.dubbo.common.function.Streams.filterAll;
import static org.apache.dubbo.common.utils.ClassUtils.getAllInheritedTypes;
import static org.apache.dubbo.common.utils.MemberUtils.isPrivate;
import static org.apache.dubbo.common.utils.MemberUtils.isStatic;
import static org.apache.dubbo.common.utils.ReflectUtils.EMPTY_CLASS_ARRAY;
import static org.apache.dubbo.common.utils.ReflectUtils.resolveTypes;
import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
/**
* Miscellaneous method utility methods.
* Mainly for internal use within the framework.
*
* @since 2.7.2
*/
public interface MethodUtils {
/**
* Return {@code true} if the provided method is a set method.
* Otherwise, return {@code false}.
*
* @param method the method to check
* @return whether the given method is setter method
*/
static boolean isSetter(Method method) {
return method.getName().startsWith("set")
&& !"set".equals(method.getName())
&& Modifier.isPublic(method.getModifiers())
&& method.getParameterCount() == 1
&& ClassUtils.isPrimitive(method.getParameterTypes()[0]);
}
/**
* Return {@code true} if the provided method is a get method.
* Otherwise, return {@code false}.
*
* @param method the method to check
* @return whether the given method is getter method
*/
static boolean isGetter(Method method) {
String name = method.getName();
return (name.startsWith("get") || name.startsWith("is"))
&& !"get".equals(name) && !"is".equals(name)
&& !"getClass".equals(name) && !"getObject".equals(name)
&& Modifier.isPublic(method.getModifiers())
&& method.getParameterTypes().length == 0
&& ClassUtils.isPrimitive(method.getReturnType());
}
/**
* Return {@code true} If this method is a meta method.
* Otherwise, return {@code false}.
*
* @param method the method to check
* @return whether the given method is meta method
*/
static boolean isMetaMethod(Method method) {
String name = method.getName();
if (!(name.startsWith("get") || name.startsWith("is"))) {
return false;
}
if ("get".equals(name)) {
return false;
}
if ("getClass".equals(name)) {
return false;
}
if (!Modifier.isPublic(method.getModifiers())) {
return false;
}
if (method.getParameterTypes().length != 0) {
return false;
}
if (!ClassUtils.isPrimitive(method.getReturnType())) {
return false;
}
return true;
}
/**
* Check if the method is a deprecated method. The standard is whether the {@link java.lang.Deprecated} annotation is declared on the class.
* Return {@code true} if this annotation is present.
* Otherwise, return {@code false}.
*
* @param method the method to check
* @return whether the given method is deprecated method
*/
static boolean isDeprecated(Method method) {
return method.getAnnotation(Deprecated.class) != null;
}
/**
* Create an instance of {@link Predicate} for {@link Method} to exclude the specified declared class
*
* @param declaredClass the declared class to exclude
* @return non-null
* @since 2.7.6
*/
static Predicate<Method> excludedDeclaredClass(Class<?> declaredClass) {
return method -> !Objects.equals(declaredClass, method.getDeclaringClass());
}
/**
* Get all {@link Method methods} of the declared class
*
* @param declaringClass the declared class
* @param includeInheritedTypes include the inherited types, e,g. super classes or interfaces
* @param publicOnly only public method
* @param methodsToFilter (optional) the methods to be filtered
* @return non-null read-only {@link List}
* @since 2.7.6
*/
static List<Method> getMethods(Class<?> declaringClass, boolean includeInheritedTypes, boolean publicOnly,
Predicate<Method>... methodsToFilter) {
if (declaringClass == null || declaringClass.isPrimitive()) {
return emptyList();
}
// All declared classes
List<Class<?>> declaredClasses = new LinkedList<>();
// Add the top declaring class
declaredClasses.add(declaringClass);
// If the super classes are resolved, all them into declaredClasses
if (includeInheritedTypes) {
declaredClasses.addAll(getAllInheritedTypes(declaringClass));
}
// All methods
List<Method> allMethods = new LinkedList<>();
for (Class<?> classToSearch : declaredClasses) {
Method[] methods = publicOnly ? classToSearch.getMethods() : classToSearch.getDeclaredMethods();
// Add the declared methods or public methods
for (Method method : methods) {
allMethods.add(method);
}
}
return unmodifiableList(filterAll(allMethods, methodsToFilter));
}
/**
* Get all declared {@link Method methods} of the declared class, excluding the inherited methods
*
* @param declaringClass the declared class
* @param methodsToFilter (optional) the methods to be filtered
* @return non-null read-only {@link List}
* @see #getMethods(Class, boolean, boolean, Predicate[])
* @since 2.7.6
*/
static List<Method> getDeclaredMethods(Class<?> declaringClass, Predicate<Method>... methodsToFilter) {
return getMethods(declaringClass, false, false, methodsToFilter);
}
/**
* Get all public {@link Method methods} of the declared class, including the inherited methods.
*
* @param declaringClass the declared class
* @param methodsToFilter (optional) the methods to be filtered
* @return non-null read-only {@link List}
* @see #getMethods(Class, boolean, boolean, Predicate[])
* @since 2.7.6
*/
static List<Method> getMethods(Class<?> declaringClass, Predicate<Method>... methodsToFilter) {
return getMethods(declaringClass, false, true, methodsToFilter);
}
/**
* Get all declared {@link Method methods} of the declared class, including the inherited methods.
*
* @param declaringClass the declared class
* @param methodsToFilter (optional) the methods to be filtered
* @return non-null read-only {@link List}
* @see #getMethods(Class, boolean, boolean, Predicate[])
* @since 2.7.6
*/
static List<Method> getAllDeclaredMethods(Class<?> declaringClass, Predicate<Method>... methodsToFilter) {
return getMethods(declaringClass, true, false, methodsToFilter);
}
/**
* Get all public {@link Method methods} of the declared class, including the inherited methods.
*
* @param declaringClass the declared class
* @param methodsToFilter (optional) the methods to be filtered
* @return non-null read-only {@link List}
* @see #getMethods(Class, boolean, boolean, Predicate[])
* @since 2.7.6
*/
static List<Method> getAllMethods(Class<?> declaringClass, Predicate<Method>... methodsToFilter) {
return getMethods(declaringClass, true, true, methodsToFilter);
}
// static List<Method> getOverriderMethods(Class<?> implementationClass, Class<?>... superTypes) {
//
// }
/**
* Find the {@link Method} by the the specified type and method name without the parameter types
*
* @param type the target type
* @param methodName the specified method name
* @return if not found, return <code>null</code>
* @since 2.7.6
*/
static Method findMethod(Class type, String methodName) {
return findMethod(type, methodName, EMPTY_CLASS_ARRAY);
}
/**
* Find the {@link Method} by the the specified type, method name and parameter types
*
* @param type the target type
* @param methodName the method name
* @param parameterTypes the parameter types
* @return if not found, return <code>null</code>
* @since 2.7.6
*/
static Method findMethod(Class type, String methodName, Class<?>... parameterTypes) {
Method method = null;
try {
if (type != null && isNotEmpty(methodName)) {
method = type.getDeclaredMethod(methodName, parameterTypes);
}
} catch (NoSuchMethodException e) {
}
return method;
}
/**
* Invoke the target object and method
*
* @param object the target object
* @param methodName the method name
* @param methodParameters the method parameters
* @param <T> the return type
* @return the target method's execution result
* @since 2.7.6
*/
static <T> T invokeMethod(Object object, String methodName, Object... methodParameters) {
Class type = object.getClass();
Class[] parameterTypes = resolveTypes(methodParameters);
Method method = findMethod(type, methodName, parameterTypes);
T value = null;
if (method == null) {
throw new IllegalStateException(String.format("cannot find method %s,class: %s", methodName, type.getName()));
}
try {
final boolean isAccessible = method.isAccessible();
if (!isAccessible) {
method.setAccessible(true);
}
value = (T) method.invoke(object, methodParameters);
method.setAccessible(isAccessible);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
return value;
}
/**
* Tests whether one method, as a member of a given type,
* overrides another method.
*
* @param overrider the first method, possible overrider
* @param overridden the second method, possibly being overridden
* @return {@code true} if and only if the first method overrides
* the second
* @jls 8.4.8 Inheritance, Overriding, and Hiding
* @jls 9.4.1 Inheritance and Overriding
* @see Elements#overrides(ExecutableElement, ExecutableElement, TypeElement)
*/
static boolean overrides(Method overrider, Method overridden) {
if (overrider == null || overridden == null) {
return false;
}
// equality comparison: If two methods are same
if (Objects.equals(overrider, overridden)) {
return false;
}
// Modifiers comparison: Any method must be non-static method
if (isStatic(overrider) || isStatic(overridden)) { //
return false;
}
// Modifiers comparison: the accessibility of any method must not be private
if (isPrivate(overrider) || isPrivate(overridden)) {
return false;
}
// Inheritance comparison: The declaring class of overrider must be inherit from the overridden's
if (!overridden.getDeclaringClass().isAssignableFrom(overrider.getDeclaringClass())) {
return false;
}
// Method comparison: must not be "default" method
if (overrider.isDefault()) {
return false;
}
// Method comparison: The method name must be equal
if (!Objects.equals(overrider.getName(), overridden.getName())) {
return false;
}
// Method comparison: The count of method parameters must be equal
if (!Objects.equals(overrider.getParameterCount(), overridden.getParameterCount())) {
return false;
}
// Method comparison: Any parameter type of overrider must equal the overridden's
for (int i = 0; i < overrider.getParameterCount(); i++) {
if (!Objects.equals(overridden.getParameterTypes()[i], overrider.getParameterTypes()[i])) {
return false;
}
}
// Method comparison: The return type of overrider must be inherit from the overridden's
if (!overridden.getReturnType().isAssignableFrom(overrider.getReturnType())) {
return false;
}
// Throwable comparison: "throws" Throwable list will be ignored, trust the compiler verify
return true;
}
/**
* Find the nearest overridden {@link Method method} from the inherited class
*
* @param overrider the overrider {@link Method method}
* @return if found, the overrider <code>method</code>, or <code>null</code>
*/
static Method findNearestOverriddenMethod(Method overrider) {
Class<?> declaringClass = overrider.getDeclaringClass();
Method overriddenMethod = null;
for (Class<?> inheritedType : getAllInheritedTypes(declaringClass)) {
overriddenMethod = findOverriddenMethod(overrider, inheritedType);
if (overriddenMethod != null) {
break;
}
}
return overriddenMethod;
}
/**
* Find the overridden {@link Method method} from the declaring class
*
* @param overrider the overrider {@link Method method}
* @param declaringClass the class that is declaring the overridden {@link Method method}
* @return if found, the overrider <code>method</code>, or <code>null</code>
*/
static Method findOverriddenMethod(Method overrider, Class<?> declaringClass) {
List<Method> matchedMethods = getAllMethods(declaringClass, method -> overrides(overrider, method));
return matchedMethods.isEmpty() ? null : matchedMethods.get(0);
}
/**
* Extract fieldName from set/get/is method. if it's not a set/get/is method, return empty string.
* If method equals get/is/getClass/getObject, also return empty string.
*
* @param method method
* @return fieldName
*/
static String extractFieldName(Method method) {
List<String> emptyFieldMethod = Arrays.asList("is", "get", "getObject", "getClass");
String methodName = method.getName();
String fieldName = "";
if (emptyFieldMethod.contains(methodName)) {
return fieldName;
} else if (methodName.startsWith("get")) {
fieldName = methodName.substring("get".length());
} else if (methodName.startsWith("set")) {
fieldName = methodName.substring("set".length());
} else if (methodName.startsWith("is")) {
fieldName = methodName.substring("is".length());
} else {
return fieldName;
}
if (StringUtils.isNotEmpty(fieldName)) {
fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
}
return fieldName;
}
/**
* Invoke and return double value.
*
* @param method method
* @param targetObj the object the method is invoked from
* @return double value
*/
static double invokeAndReturnDouble(Method method, Object targetObj) {
try {
return method != null ? (double) method.invoke(targetObj) : Double.NaN;
} catch (Exception e) {
return Double.NaN;
}
}
/**
* Invoke and return long value.
*
* @param method method
* @param targetObj the object the method is invoked from
* @return long value
*/
static long invokeAndReturnLong(Method method, Object targetObj) {
try {
return method != null ? (long) method.invoke(targetObj) : -1;
} catch (Exception e) {
return -1;
}
}
}