| /* |
| * 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; |
| } |
| } |
| } |
| |