1 /* 2 * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.beans.finder; 26 27 import com.sun.beans.TypeResolver; 28 import com.sun.beans.WeakCache; 29 30 import java.lang.reflect.Method; 31 import java.lang.reflect.Modifier; 32 import java.lang.reflect.ParameterizedType; 33 import java.lang.reflect.Type; 34 import java.util.Arrays; 35 36 import static sun.reflect.misc.ReflectUtil.isPackageAccessible; 37 38 /** 39 * This utility class provides {@code static} methods 40 * to find a public method with specified name and parameter types 41 * in specified class. 42 * 43 * @since 1.7 44 * 45 * @author Sergey A. Malenkov 46 */ 47 public final class MethodFinder extends AbstractFinder<Method> { 48 private static final WeakCache<Signature, Method> CACHE = new WeakCache<Signature, Method>(); 49 50 /** 51 * Finds public method (static or non-static) 52 * that is accessible from public class. 53 * 54 * @param type the class that can have method 55 * @param name the name of method to find 56 * @param args parameter types that is used to find method 57 * @return object that represents found method 58 * @throws NoSuchMethodException if method could not be found 59 * or some methods are found 60 */ 61 public static Method findMethod(Class<?> type, String name, Class<?>...args) throws NoSuchMethodException { 62 if (name == null) { 63 throw new IllegalArgumentException("Method name is not set"); 64 } 65 PrimitiveWrapperMap.replacePrimitivesWithWrappers(args); 66 Signature signature = new Signature(type, name, args); 67 68 Method method = CACHE.get(signature); 69 if (method != null) { 70 return method; 71 } 72 method = findAccessibleMethod(new MethodFinder(name, args).find(type.getMethods())); 73 CACHE.put(signature, method); 74 return method; 75 } 76 77 /** 78 * Finds public non-static method 79 * that is accessible from public class. 80 * 81 * @param type the class that can have method 82 * @param name the name of method to find 83 * @param args parameter types that is used to find method 84 * @return object that represents found method 85 * @throws NoSuchMethodException if method could not be found 86 * or some methods are found 87 */ 88 public static Method findInstanceMethod(Class<?> type, String name, Class<?>... args) throws NoSuchMethodException { 89 Method method = findMethod(type, name, args); 90 if (Modifier.isStatic(method.getModifiers())) { 91 throw new NoSuchMethodException("Method '" + name + "' is static"); 92 } 93 return method; 94 } 95 96 /** 97 * Finds public static method 98 * that is accessible from public class. 99 * 100 * @param type the class that can have method 101 * @param name the name of method to find 102 * @param args parameter types that is used to find method 103 * @return object that represents found method 104 * @throws NoSuchMethodException if method could not be found 105 * or some methods are found 106 */ 107 public static Method findStaticMethod(Class<?> type, String name, Class<?>...args) throws NoSuchMethodException { 108 Method method = findMethod(type, name, args); 109 if (!Modifier.isStatic(method.getModifiers())) { 110 throw new NoSuchMethodException("Method '" + name + "' is not static"); 111 } 112 return method; 113 } 114 115 /** 116 * Finds method that is accessible from public class or interface through class hierarchy. 117 * 118 * @param method object that represents found method 119 * @return object that represents accessible method 120 * @throws NoSuchMethodException if method is not accessible or is not found 121 * in specified superclass or interface 122 */ 123 public static Method findAccessibleMethod(Method method) throws NoSuchMethodException { 124 Class<?> type = method.getDeclaringClass(); 125 if (Modifier.isPublic(type.getModifiers()) && isPackageAccessible(type)) { 126 return method; 127 } 128 if (Modifier.isStatic(method.getModifiers())) { 129 throw new NoSuchMethodException("Method '" + method.getName() + "' is not accessible"); 130 } 131 for (Type generic : type.getGenericInterfaces()) { 132 try { 133 return findAccessibleMethod(method, generic); 134 } 135 catch (NoSuchMethodException exception) { 136 // try to find in superclass or another interface 137 } 138 } 139 return findAccessibleMethod(method, type.getGenericSuperclass()); 140 } 141 142 /** 143 * Finds method that accessible from specified class. 144 * 145 * @param method object that represents found method 146 * @param generic generic type that is used to find accessible method 147 * @return object that represents accessible method 148 * @throws NoSuchMethodException if method is not accessible or is not found 149 * in specified superclass or interface 150 */ 151 private static Method findAccessibleMethod(Method method, Type generic) throws NoSuchMethodException { 152 String name = method.getName(); 153 Class<?>[] params = method.getParameterTypes(); 154 if (generic instanceof Class) { 155 Class<?> type = (Class<?>) generic; 156 return findAccessibleMethod(type.getMethod(name, params)); 157 } 158 if (generic instanceof ParameterizedType) { 159 ParameterizedType pt = (ParameterizedType) generic; 160 Class<?> type = (Class<?>) pt.getRawType(); 161 for (Method m : type.getMethods()) { 162 if (m.getName().equals(name)) { 163 Class<?>[] pts = m.getParameterTypes(); 164 if (pts.length == params.length) { 165 if (Arrays.equals(params, pts)) { 166 return findAccessibleMethod(m); 167 } 168 Type[] gpts = m.getGenericParameterTypes(); 169 if (params.length == gpts.length) { 170 if (Arrays.equals(params, TypeResolver.erase(TypeResolver.resolve(pt, gpts)))) { 171 return findAccessibleMethod(m); 172 } 173 } 174 } 175 } 176 } 177 } 178 throw new NoSuchMethodException("Method '" + name + "' is not accessible"); 179 } 180 181 182 private final String name; 183 184 /** 185 * Creates method finder with specified array of parameter types. 186 * 187 * @param name the name of method to find 188 * @param args the array of parameter types 189 */ 190 private MethodFinder(String name, Class<?>[] args) { 191 super(args); 192 this.name = name; 193 } 194 195 /** 196 * Returns an array of {@code Class} objects 197 * that represent the formal parameter types of the method. 198 * Returns an empty array if the method takes no parameters. 199 * 200 * @param method the object that represents method 201 * @return the parameter types of the method 202 */ 203 @Override 204 protected Class<?>[] getParameters(Method method) { 205 return method.getParameterTypes(); 206 } 207 208 /** 209 * Returns {@code true} if and only if the method 210 * was declared to take a variable number of arguments. 211 * 212 * @param method the object that represents method 213 * @return {@code true} if the method was declared 214 * to take a variable number of arguments; 215 * {@code false} otherwise 216 */ 217 @Override 218 protected boolean isVarArgs(Method method) { 219 return method.isVarArgs(); 220 } 221 222 /** 223 * Checks validness of the method. 224 * The valid method should be public and 225 * should have the specified name. 226 * 227 * @param method the object that represents method 228 * @return {@code true} if the method is valid, 229 * {@code false} otherwise 230 */ 231 @Override 232 protected boolean isValid(Method method) { 233 return !method.isBridge() && Modifier.isPublic(method.getModifiers()) && method.getName().equals(this.name); 234 } 235 }