1 /* 2 * Copyright (c) 2008, 2013, 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 java.lang.reflect.Executable; 28 import java.lang.reflect.Modifier; 29 30 import java.util.HashMap; 31 import java.util.Map; 32 33 /** 34 * This abstract class provides functionality 35 * to find a public method or constructor 36 * with specified parameter types. 37 * It supports a variable number of parameters. 38 * 39 * @since 1.7 40 * 41 * @author Sergey A. Malenkov 42 */ 43 abstract class AbstractFinder<T extends Executable> { 44 private final Class<?>[] args; 45 46 /** 47 * Creates finder for array of classes of arguments. 48 * If a particular element of array equals {@code null}, 49 * than the appropriate pair of classes 50 * does not take into consideration. 51 * 52 * @param args array of classes of arguments 53 */ 54 protected AbstractFinder(Class<?>[] args) { 55 this.args = args; 56 } 57 58 /** 59 * Checks validness of the method. 60 * At least the valid method should be public. 61 * 62 * @param method the object that represents method 63 * @return {@code true} if the method is valid, 64 * {@code false} otherwise 65 */ 66 protected boolean isValid(T method) { 67 return Modifier.isPublic(method.getModifiers()); 68 } 69 70 /** 71 * Performs a search in the {@code methods} array. 72 * The one method is selected from the array of the valid methods. 73 * The list of parameters of the selected method shows 74 * the best correlation with the list of arguments 75 * specified at class initialization. 76 * If more than one method is both accessible and applicable 77 * to a method invocation, it is necessary to choose one 78 * to provide the descriptor for the run-time method dispatch. 79 * The most specific method should be chosen. 80 * 81 * @param methods the array of methods to search within 82 * @return the object that represents found method 83 * @throws NoSuchMethodException if no method was found or several 84 * methods meet the search criteria 85 * @see #isAssignable 86 */ 87 final T find(T[] methods) throws NoSuchMethodException { 88 Map<T, Class<?>[]> map = new HashMap<T, Class<?>[]>(); 89 90 T oldMethod = null; 91 Class<?>[] oldParams = null; 92 boolean ambiguous = false; 93 94 for (T newMethod : methods) { 95 if (isValid(newMethod)) { 96 Class<?>[] newParams = newMethod.getParameterTypes(); 97 if (newParams.length == this.args.length) { 98 PrimitiveWrapperMap.replacePrimitivesWithWrappers(newParams); 99 if (isAssignable(newParams, this.args)) { 100 if (oldMethod == null) { 101 oldMethod = newMethod; 102 oldParams = newParams; 103 } else { 104 boolean useNew = isAssignable(oldParams, newParams); 105 boolean useOld = isAssignable(newParams, oldParams); 106 107 if (useOld && useNew) { 108 // only if parameters are equal 109 useNew = !newMethod.isSynthetic(); 110 useOld = !oldMethod.isSynthetic(); 111 } 112 if (useOld == useNew) { 113 ambiguous = true; 114 } else if (useNew) { 115 oldMethod = newMethod; 116 oldParams = newParams; 117 ambiguous = false; 118 } 119 } 120 } 121 } 122 if (newMethod.isVarArgs()) { 123 int length = newParams.length - 1; 124 if (length <= this.args.length) { 125 Class<?>[] array = new Class<?>[this.args.length]; 126 System.arraycopy(newParams, 0, array, 0, length); 127 if (length < this.args.length) { 128 Class<?> type = newParams[length].getComponentType(); 129 if (type.isPrimitive()) { 130 type = PrimitiveWrapperMap.getType(type.getName()); 131 } 132 for (int i = length; i < this.args.length; i++) { 133 array[i] = type; 134 } 135 } 136 map.put(newMethod, array); 137 } 138 } 139 } 140 } 141 for (T newMethod : methods) { 142 Class<?>[] newParams = map.get(newMethod); 143 if (newParams != null) { 144 if (isAssignable(newParams, this.args)) { 145 if (oldMethod == null) { 146 oldMethod = newMethod; 147 oldParams = newParams; 148 } else { 149 boolean useNew = isAssignable(oldParams, newParams); 150 boolean useOld = isAssignable(newParams, oldParams); 151 152 if (useOld && useNew) { 153 // only if parameters are equal 154 useNew = !newMethod.isSynthetic(); 155 useOld = !oldMethod.isSynthetic(); 156 } 157 if (useOld == useNew) { 158 if (oldParams == map.get(oldMethod)) { 159 ambiguous = true; 160 } 161 } else if (useNew) { 162 oldMethod = newMethod; 163 oldParams = newParams; 164 ambiguous = false; 165 } 166 } 167 } 168 } 169 } 170 171 if (ambiguous) { 172 throw new NoSuchMethodException("Ambiguous methods are found"); 173 } 174 if (oldMethod == null) { 175 throw new NoSuchMethodException("Method is not found"); 176 } 177 return oldMethod; 178 } 179 180 /** 181 * Determines if every class in {@code min} array is either the same as, 182 * or is a superclass of, the corresponding class in {@code max} array. 183 * The length of every array must equal the number of arguments. 184 * This comparison is performed in the {@link #find} method 185 * before the first call of the isAssignable method. 186 * If an argument equals {@code null} 187 * the appropriate pair of classes does not take into consideration. 188 * 189 * @param min the array of classes to be checked 190 * @param max the array of classes that is used to check 191 * @return {@code true} if all classes in {@code min} array 192 * are assignable from corresponding classes in {@code max} array, 193 * {@code false} otherwise 194 * 195 * @see Class#isAssignableFrom 196 */ 197 private boolean isAssignable(Class<?>[] min, Class<?>[] max) { 198 for (int i = 0; i < this.args.length; i++) { 199 if (null != this.args[i]) { 200 if (!min[i].isAssignableFrom(max[i])) { 201 return false; 202 } 203 } 204 } 205 return true; 206 } 207 }