827N/A/*
6116N/A * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
827N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
827N/A *
827N/A * This code is free software; you can redistribute it and/or modify it
827N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
827N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
827N/A *
827N/A * This code is distributed in the hope that it will be useful, but WITHOUT
827N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
827N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
827N/A * version 2 for more details (a copy is included in the LICENSE file that
827N/A * accompanied this code).
827N/A *
827N/A * You should have received a copy of the GNU General Public License version
827N/A * 2 along with this work; if not, write to the Free Software Foundation,
827N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
827N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
827N/A */
827N/Apackage com.sun.beans.finder;
827N/A
834N/Aimport com.sun.beans.TypeResolver;
827N/Aimport com.sun.beans.WeakCache;
827N/A
827N/Aimport java.lang.reflect.Method;
827N/Aimport java.lang.reflect.Modifier;
834N/Aimport java.lang.reflect.ParameterizedType;
834N/Aimport java.lang.reflect.Type;
834N/Aimport java.util.Arrays;
827N/A
5252N/Aimport static sun.reflect.misc.ReflectUtil.isPackageAccessible;
5252N/A
827N/A/**
827N/A * This utility class provides {@code static} methods
827N/A * to find a public method with specified name and parameter types
827N/A * in specified class.
827N/A *
827N/A * @since 1.7
827N/A *
827N/A * @author Sergey A. Malenkov
827N/A */
827N/Apublic final class MethodFinder extends AbstractFinder<Method> {
827N/A private static final WeakCache<Signature, Method> CACHE = new WeakCache<Signature, Method>();
827N/A
827N/A /**
827N/A * Finds public method (static or non-static)
827N/A * that is accessible from public class.
827N/A *
827N/A * @param type the class that can have method
827N/A * @param name the name of method to find
827N/A * @param args parameter types that is used to find method
827N/A * @return object that represents found method
827N/A * @throws NoSuchMethodException if method could not be found
827N/A * or some methods are found
827N/A */
827N/A public static Method findMethod(Class<?> type, String name, Class<?>...args) throws NoSuchMethodException {
827N/A if (name == null) {
827N/A throw new IllegalArgumentException("Method name is not set");
827N/A }
827N/A PrimitiveWrapperMap.replacePrimitivesWithWrappers(args);
827N/A Signature signature = new Signature(type, name, args);
827N/A
827N/A Method method = CACHE.get(signature);
5693N/A boolean cached = method != null;
5693N/A if (cached && isPackageAccessible(method.getDeclaringClass())) {
827N/A return method;
827N/A }
827N/A method = findAccessibleMethod(new MethodFinder(name, args).find(type.getMethods()));
5693N/A if (!cached) {
5693N/A CACHE.put(signature, method);
5693N/A }
827N/A return method;
827N/A }
827N/A
827N/A /**
827N/A * Finds public non-static method
827N/A * that is accessible from public class.
827N/A *
827N/A * @param type the class that can have method
827N/A * @param name the name of method to find
827N/A * @param args parameter types that is used to find method
827N/A * @return object that represents found method
827N/A * @throws NoSuchMethodException if method could not be found
827N/A * or some methods are found
827N/A */
827N/A public static Method findInstanceMethod(Class<?> type, String name, Class<?>... args) throws NoSuchMethodException {
827N/A Method method = findMethod(type, name, args);
827N/A if (Modifier.isStatic(method.getModifiers())) {
827N/A throw new NoSuchMethodException("Method '" + name + "' is static");
827N/A }
827N/A return method;
827N/A }
827N/A
827N/A /**
827N/A * Finds public static method
827N/A * that is accessible from public class.
827N/A *
827N/A * @param type the class that can have method
827N/A * @param name the name of method to find
827N/A * @param args parameter types that is used to find method
827N/A * @return object that represents found method
827N/A * @throws NoSuchMethodException if method could not be found
827N/A * or some methods are found
827N/A */
827N/A public static Method findStaticMethod(Class<?> type, String name, Class<?>...args) throws NoSuchMethodException {
827N/A Method method = findMethod(type, name, args);
827N/A if (!Modifier.isStatic(method.getModifiers())) {
827N/A throw new NoSuchMethodException("Method '" + name + "' is not static");
827N/A }
827N/A return method;
827N/A }
827N/A
827N/A /**
827N/A * Finds method that is accessible from public class or interface through class hierarchy.
827N/A *
827N/A * @param method object that represents found method
827N/A * @return object that represents accessible method
827N/A * @throws NoSuchMethodException if method is not accessible or is not found
827N/A * in specified superclass or interface
827N/A */
2523N/A public static Method findAccessibleMethod(Method method) throws NoSuchMethodException {
827N/A Class<?> type = method.getDeclaringClass();
5252N/A if (Modifier.isPublic(type.getModifiers()) && isPackageAccessible(type)) {
827N/A return method;
827N/A }
827N/A if (Modifier.isStatic(method.getModifiers())) {
827N/A throw new NoSuchMethodException("Method '" + method.getName() + "' is not accessible");
827N/A }
834N/A for (Type generic : type.getGenericInterfaces()) {
827N/A try {
834N/A return findAccessibleMethod(method, generic);
827N/A }
827N/A catch (NoSuchMethodException exception) {
827N/A // try to find in superclass or another interface
827N/A }
827N/A }
834N/A return findAccessibleMethod(method, type.getGenericSuperclass());
827N/A }
827N/A
827N/A /**
827N/A * Finds method that accessible from specified class.
827N/A *
827N/A * @param method object that represents found method
834N/A * @param generic generic type that is used to find accessible method
827N/A * @return object that represents accessible method
827N/A * @throws NoSuchMethodException if method is not accessible or is not found
827N/A * in specified superclass or interface
827N/A */
834N/A private static Method findAccessibleMethod(Method method, Type generic) throws NoSuchMethodException {
827N/A String name = method.getName();
827N/A Class<?>[] params = method.getParameterTypes();
834N/A if (generic instanceof Class) {
834N/A Class<?> type = (Class<?>) generic;
834N/A return findAccessibleMethod(type.getMethod(name, params));
834N/A }
834N/A if (generic instanceof ParameterizedType) {
834N/A ParameterizedType pt = (ParameterizedType) generic;
834N/A Class<?> type = (Class<?>) pt.getRawType();
834N/A for (Method m : type.getMethods()) {
834N/A if (m.getName().equals(name)) {
834N/A Class<?>[] pts = m.getParameterTypes();
834N/A if (pts.length == params.length) {
834N/A if (Arrays.equals(params, pts)) {
834N/A return findAccessibleMethod(m);
834N/A }
834N/A Type[] gpts = m.getGenericParameterTypes();
5235N/A if (params.length == gpts.length) {
5235N/A if (Arrays.equals(params, TypeResolver.erase(TypeResolver.resolve(pt, gpts)))) {
5235N/A return findAccessibleMethod(m);
5235N/A }
834N/A }
834N/A }
834N/A }
834N/A }
834N/A }
834N/A throw new NoSuchMethodException("Method '" + name + "' is not accessible");
827N/A }
827N/A
827N/A
827N/A private final String name;
827N/A
827N/A /**
827N/A * Creates method finder with specified array of parameter types.
827N/A *
827N/A * @param name the name of method to find
827N/A * @param args the array of parameter types
827N/A */
827N/A private MethodFinder(String name, Class<?>[] args) {
827N/A super(args);
827N/A this.name = name;
827N/A }
827N/A
827N/A /**
827N/A * Returns an array of {@code Class} objects
4513N/A * that represent the formal parameter types of the method.
827N/A * Returns an empty array if the method takes no parameters.
827N/A *
827N/A * @param method the object that represents method
827N/A * @return the parameter types of the method
827N/A */
827N/A @Override
827N/A protected Class<?>[] getParameters(Method method) {
827N/A return method.getParameterTypes();
827N/A }
827N/A
827N/A /**
827N/A * Returns {@code true} if and only if the method
827N/A * was declared to take a variable number of arguments.
827N/A *
827N/A * @param method the object that represents method
827N/A * @return {@code true} if the method was declared
827N/A * to take a variable number of arguments;
827N/A * {@code false} otherwise
827N/A */
827N/A @Override
827N/A protected boolean isVarArgs(Method method) {
827N/A return method.isVarArgs();
827N/A }
827N/A
827N/A /**
827N/A * Checks validness of the method.
827N/A * The valid method should be public and
827N/A * should have the specified name.
827N/A *
827N/A * @param method the object that represents method
827N/A * @return {@code true} if the method is valid,
827N/A * {@code false} otherwise
827N/A */
827N/A @Override
827N/A protected boolean isValid(Method method) {
6116N/A return super.isValid(method) && method.getName().equals(this.name);
827N/A }
827N/A}