Path: blob/master/src/java.desktop/share/classes/com/sun/beans/finder/AbstractFinder.java
41161 views
/*1* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package com.sun.beans.finder;2526import java.lang.reflect.Executable;27import java.lang.reflect.Modifier;2829import java.util.HashMap;30import java.util.Map;3132/**33* This abstract class provides functionality34* to find a public method or constructor35* with specified parameter types.36* It supports a variable number of parameters.37*38* @since 1.739*40* @author Sergey A. Malenkov41*/42abstract class AbstractFinder<T extends Executable> {43private final Class<?>[] args;4445/**46* Creates finder for array of classes of arguments.47* If a particular element of array equals {@code null},48* than the appropriate pair of classes49* does not take into consideration.50*51* @param args array of classes of arguments52*/53protected AbstractFinder(Class<?>[] args) {54this.args = args;55}5657/**58* Checks validness of the method.59* At least the valid method should be public.60*61* @param method the object that represents method62* @return {@code true} if the method is valid,63* {@code false} otherwise64*/65protected boolean isValid(T method) {66return Modifier.isPublic(method.getModifiers());67}6869/**70* Performs a search in the {@code methods} array.71* The one method is selected from the array of the valid methods.72* The list of parameters of the selected method shows73* the best correlation with the list of arguments74* specified at class initialization.75* If more than one method is both accessible and applicable76* to a method invocation, it is necessary to choose one77* to provide the descriptor for the run-time method dispatch.78* The most specific method should be chosen.79*80* @param methods the array of methods to search within81* @return the object that represents found method82* @throws NoSuchMethodException if no method was found or several83* methods meet the search criteria84* @see #isAssignable85*/86final T find(T[] methods) throws NoSuchMethodException {87Map<T, Class<?>[]> map = new HashMap<T, Class<?>[]>();8889T oldMethod = null;90Class<?>[] oldParams = null;91boolean ambiguous = false;9293for (T newMethod : methods) {94if (isValid(newMethod)) {95Class<?>[] newParams = newMethod.getParameterTypes();96if (newParams.length == this.args.length) {97PrimitiveWrapperMap.replacePrimitivesWithWrappers(newParams);98if (isAssignable(newParams, this.args)) {99if (oldMethod == null) {100oldMethod = newMethod;101oldParams = newParams;102} else {103boolean useNew = isAssignable(oldParams, newParams);104boolean useOld = isAssignable(newParams, oldParams);105106if (useOld && useNew) {107// only if parameters are equal108useNew = !newMethod.isSynthetic();109useOld = !oldMethod.isSynthetic();110}111if (useOld == useNew) {112ambiguous = true;113} else if (useNew) {114oldMethod = newMethod;115oldParams = newParams;116ambiguous = false;117}118}119}120}121if (newMethod.isVarArgs()) {122int length = newParams.length - 1;123if (length <= this.args.length) {124Class<?>[] array = new Class<?>[this.args.length];125System.arraycopy(newParams, 0, array, 0, length);126if (length < this.args.length) {127Class<?> type = newParams[length].getComponentType();128if (type.isPrimitive()) {129type = PrimitiveWrapperMap.getType(type.getName());130}131for (int i = length; i < this.args.length; i++) {132array[i] = type;133}134}135map.put(newMethod, array);136}137}138}139}140for (T newMethod : methods) {141Class<?>[] newParams = map.get(newMethod);142if (newParams != null) {143if (isAssignable(newParams, this.args)) {144if (oldMethod == null) {145oldMethod = newMethod;146oldParams = newParams;147} else {148boolean useNew = isAssignable(oldParams, newParams);149boolean useOld = isAssignable(newParams, oldParams);150151if (useOld && useNew) {152// only if parameters are equal153useNew = !newMethod.isSynthetic();154useOld = !oldMethod.isSynthetic();155}156if (useOld == useNew) {157if (oldParams == map.get(oldMethod)) {158ambiguous = true;159}160} else if (useNew) {161oldMethod = newMethod;162oldParams = newParams;163ambiguous = false;164}165}166}167}168}169170if (ambiguous) {171throw new NoSuchMethodException("Ambiguous methods are found");172}173if (oldMethod == null) {174throw new NoSuchMethodException("Method is not found");175}176return oldMethod;177}178179/**180* Determines if every class in {@code min} array is either the same as,181* or is a superclass of, the corresponding class in {@code max} array.182* The length of every array must equal the number of arguments.183* This comparison is performed in the {@link #find} method184* before the first call of the isAssignable method.185* If an argument equals {@code null}186* the appropriate pair of classes does not take into consideration.187*188* @param min the array of classes to be checked189* @param max the array of classes that is used to check190* @return {@code true} if all classes in {@code min} array191* are assignable from corresponding classes in {@code max} array,192* {@code false} otherwise193*194* @see Class#isAssignableFrom195*/196private boolean isAssignable(Class<?>[] min, Class<?>[] max) {197for (int i = 0; i < this.args.length; i++) {198if (null != this.args[i]) {199if (!min[i].isAssignableFrom(max[i])) {200return false;201}202}203}204return true;205}206}207208209