| /* | |
| * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package com.sun.beans.finder; | |
| import java.lang.reflect.Executable; | |
| import java.lang.reflect.Modifier; | |
| import java.util.HashMap; | |
| import java.util.Map; | |
| /** | |
| * This abstract class provides functionality | |
| * to find a public method or constructor | |
| * with specified parameter types. | |
| * It supports a variable number of parameters. | |
| * | |
| * @since 1.7 | |
| * | |
| * @author Sergey A. Malenkov | |
| */ | |
| abstract class AbstractFinder<T extends Executable> { | |
| private final Class<?>[] args; | |
| /** | |
| * Creates finder for array of classes of arguments. | |
| * If a particular element of array equals {@code null}, | |
| * than the appropriate pair of classes | |
| * does not take into consideration. | |
| * | |
| * @param args array of classes of arguments | |
| */ | |
| protected AbstractFinder(Class<?>[] args) { | |
| this.args = args; | |
| } | |
| /** | |
| * Checks validness of the method. | |
| * At least the valid method should be public. | |
| * | |
| * @param method the object that represents method | |
| * @return {@code true} if the method is valid, | |
| * {@code false} otherwise | |
| */ | |
| protected boolean isValid(T method) { | |
| return Modifier.isPublic(method.getModifiers()); | |
| } | |
| /** | |
| * Performs a search in the {@code methods} array. | |
| * The one method is selected from the array of the valid methods. | |
| * The list of parameters of the selected method shows | |
| * the best correlation with the list of arguments | |
| * specified at class initialization. | |
| * If more than one method is both accessible and applicable | |
| * to a method invocation, it is necessary to choose one | |
| * to provide the descriptor for the run-time method dispatch. | |
| * The most specific method should be chosen. | |
| * | |
| * @param methods the array of methods to search within | |
| * @return the object that represents found method | |
| * @throws NoSuchMethodException if no method was found or several | |
| * methods meet the search criteria | |
| * @see #isAssignable | |
| */ | |
| final T find(T[] methods) throws NoSuchMethodException { | |
| Map<T, Class<?>[]> map = new HashMap<T, Class<?>[]>(); | |
| T oldMethod = null; | |
| Class<?>[] oldParams = null; | |
| boolean ambiguous = false; | |
| for (T newMethod : methods) { | |
| if (isValid(newMethod)) { | |
| Class<?>[] newParams = newMethod.getParameterTypes(); | |
| if (newParams.length == this.args.length) { | |
| PrimitiveWrapperMap.replacePrimitivesWithWrappers(newParams); | |
| if (isAssignable(newParams, this.args)) { | |
| if (oldMethod == null) { | |
| oldMethod = newMethod; | |
| oldParams = newParams; | |
| } else { | |
| boolean useNew = isAssignable(oldParams, newParams); | |
| boolean useOld = isAssignable(newParams, oldParams); | |
| if (useOld && useNew) { | |
| // only if parameters are equal | |
| useNew = !newMethod.isSynthetic(); | |
| useOld = !oldMethod.isSynthetic(); | |
| } | |
| if (useOld == useNew) { | |
| ambiguous = true; | |
| } else if (useNew) { | |
| oldMethod = newMethod; | |
| oldParams = newParams; | |
| ambiguous = false; | |
| } | |
| } | |
| } | |
| } | |
| if (newMethod.isVarArgs()) { | |
| int length = newParams.length - 1; | |
| if (length <= this.args.length) { | |
| Class<?>[] array = new Class<?>[this.args.length]; | |
| System.arraycopy(newParams, 0, array, 0, length); | |
| if (length < this.args.length) { | |
| Class<?> type = newParams[length].getComponentType(); | |
| if (type.isPrimitive()) { | |
| type = PrimitiveWrapperMap.getType(type.getName()); | |
| } | |
| for (int i = length; i < this.args.length; i++) { | |
| array[i] = type; | |
| } | |
| } | |
| map.put(newMethod, array); | |
| } | |
| } | |
| } | |
| } | |
| for (T newMethod : methods) { | |
| Class<?>[] newParams = map.get(newMethod); | |
| if (newParams != null) { | |
| if (isAssignable(newParams, this.args)) { | |
| if (oldMethod == null) { | |
| oldMethod = newMethod; | |
| oldParams = newParams; | |
| } else { | |
| boolean useNew = isAssignable(oldParams, newParams); | |
| boolean useOld = isAssignable(newParams, oldParams); | |
| if (useOld && useNew) { | |
| // only if parameters are equal | |
| useNew = !newMethod.isSynthetic(); | |
| useOld = !oldMethod.isSynthetic(); | |
| } | |
| if (useOld == useNew) { | |
| if (oldParams == map.get(oldMethod)) { | |
| ambiguous = true; | |
| } | |
| } else if (useNew) { | |
| oldMethod = newMethod; | |
| oldParams = newParams; | |
| ambiguous = false; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (ambiguous) { | |
| throw new NoSuchMethodException("Ambiguous methods are found"); | |
| } | |
| if (oldMethod == null) { | |
| throw new NoSuchMethodException("Method is not found"); | |
| } | |
| return oldMethod; | |
| } | |
| /** | |
| * Determines if every class in {@code min} array is either the same as, | |
| * or is a superclass of, the corresponding class in {@code max} array. | |
| * The length of every array must equal the number of arguments. | |
| * This comparison is performed in the {@link #find} method | |
| * before the first call of the isAssignable method. | |
| * If an argument equals {@code null} | |
| * the appropriate pair of classes does not take into consideration. | |
| * | |
| * @param min the array of classes to be checked | |
| * @param max the array of classes that is used to check | |
| * @return {@code true} if all classes in {@code min} array | |
| * are assignable from corresponding classes in {@code max} array, | |
| * {@code false} otherwise | |
| * | |
| * @see Class#isAssignableFrom | |
| */ | |
| private boolean isAssignable(Class<?>[] min, Class<?>[] max) { | |
| for (int i = 0; i < this.args.length; i++) { | |
| if (null != this.args[i]) { | |
| if (!min[i].isAssignableFrom(max[i])) { | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| } |