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