| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.jxpath.util; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.Arrays; |
| |
| import org.apache.commons.jxpath.ExpressionContext; |
| import org.apache.commons.jxpath.JXPathException; |
| |
| /** |
| * Method lookup utilities, which find static and non-static methods as well |
| * as constructors based on a name and list of parameters. |
| * |
| * @author Dmitri Plotnikov |
| * @version $Revision$ $Date$ |
| */ |
| public class MethodLookupUtils { |
| |
| private static final int NO_MATCH = 0; |
| private static final int APPROXIMATE_MATCH = 1; |
| private static final int EXACT_MATCH = 2; |
| |
| /** |
| * Look up a constructor. |
| * @param targetClass the class constructed |
| * @param parameters arguments |
| * @return Constructor found if any. |
| */ |
| public static Constructor lookupConstructor( |
| Class targetClass, |
| Object[] parameters) { |
| boolean tryExact = true; |
| int count = parameters == null ? 0 : parameters.length; |
| Class[] types = new Class[count]; |
| for (int i = 0; i < count; i++) { |
| Object param = parameters[i]; |
| if (param != null) { |
| types[i] = param.getClass(); |
| } |
| else { |
| types[i] = null; |
| tryExact = false; |
| } |
| } |
| |
| Constructor constructor = null; |
| |
| if (tryExact) { |
| // First - without type conversion |
| try { |
| constructor = targetClass.getConstructor(types); |
| if (constructor != null) { |
| return constructor; |
| } |
| } |
| catch (NoSuchMethodException ex) { |
| // Ignore |
| } |
| } |
| |
| int currentMatch = 0; |
| boolean ambiguous = false; |
| |
| // Then - with type conversion |
| Constructor[] constructors = targetClass.getConstructors(); |
| for (int i = 0; i < constructors.length; i++) { |
| int match = |
| matchParameterTypes( |
| constructors[i].getParameterTypes(), |
| parameters); |
| if (match != NO_MATCH) { |
| if (match > currentMatch) { |
| constructor = constructors[i]; |
| currentMatch = match; |
| ambiguous = false; |
| } |
| else if (match == currentMatch) { |
| ambiguous = true; |
| } |
| } |
| } |
| if (ambiguous) { |
| throw new JXPathException( |
| "Ambigous constructor " + Arrays.asList(parameters)); |
| } |
| return constructor; |
| } |
| |
| /** |
| * Look up a static method. |
| * @param targetClass the owning class |
| * @param name method name |
| * @param parameters method parameters |
| * @return Method found if any |
| */ |
| public static Method lookupStaticMethod( |
| Class targetClass, |
| String name, |
| Object[] parameters) { |
| boolean tryExact = true; |
| int count = parameters == null ? 0 : parameters.length; |
| Class[] types = new Class[count]; |
| for (int i = 0; i < count; i++) { |
| Object param = parameters[i]; |
| if (param != null) { |
| types[i] = param.getClass(); |
| } |
| else { |
| types[i] = null; |
| tryExact = false; |
| } |
| } |
| |
| Method method = null; |
| |
| if (tryExact) { |
| // First - without type conversion |
| try { |
| method = targetClass.getMethod(name, types); |
| if (method != null |
| && Modifier.isStatic(method.getModifiers())) { |
| return method; |
| } |
| } |
| catch (NoSuchMethodException ex) { |
| // Ignore |
| } |
| } |
| |
| int currentMatch = 0; |
| boolean ambiguous = false; |
| |
| // Then - with type conversion |
| Method[] methods = targetClass.getMethods(); |
| for (int i = 0; i < methods.length; i++) { |
| if (Modifier.isStatic(methods[i].getModifiers()) |
| && methods[i].getName().equals(name)) { |
| int match = |
| matchParameterTypes( |
| methods[i].getParameterTypes(), |
| parameters); |
| if (match != NO_MATCH) { |
| if (match > currentMatch) { |
| method = methods[i]; |
| currentMatch = match; |
| ambiguous = false; |
| } |
| else if (match == currentMatch) { |
| ambiguous = true; |
| } |
| } |
| } |
| } |
| if (ambiguous) { |
| throw new JXPathException("Ambigous method call: " + name); |
| } |
| return method; |
| } |
| |
| /** |
| * Look up a method. |
| * @param targetClass owning class |
| * @param name method name |
| * @param parameters method parameters |
| * @return Method found if any |
| */ |
| public static Method lookupMethod( |
| Class targetClass, |
| String name, |
| Object[] parameters) { |
| if (parameters == null |
| || parameters.length < 1 |
| || parameters[0] == null) { |
| return null; |
| } |
| |
| if (matchType(targetClass, parameters[0]) == NO_MATCH) { |
| return null; |
| } |
| |
| targetClass = TypeUtils.convert(parameters[0], targetClass).getClass(); |
| |
| boolean tryExact = true; |
| int count = parameters.length - 1; |
| Class[] types = new Class[count]; |
| Object[] arguments = new Object[count]; |
| for (int i = 0; i < count; i++) { |
| Object param = parameters[i + 1]; |
| arguments[i] = param; |
| if (param != null) { |
| types[i] = param.getClass(); |
| } |
| else { |
| types[i] = null; |
| tryExact = false; |
| } |
| } |
| |
| Method method = null; |
| |
| if (tryExact) { |
| // First - without type conversion |
| try { |
| method = targetClass.getMethod(name, types); |
| if (method != null |
| && !Modifier.isStatic(method.getModifiers())) { |
| return method; |
| } |
| } |
| catch (NoSuchMethodException ex) { |
| // Ignore |
| } |
| } |
| |
| int currentMatch = 0; |
| boolean ambiguous = false; |
| |
| // Then - with type conversion |
| Method[] methods = targetClass.getMethods(); |
| for (int i = 0; i < methods.length; i++) { |
| if (!Modifier.isStatic(methods[i].getModifiers()) |
| && methods[i].getName().equals(name)) { |
| int match = |
| matchParameterTypes( |
| methods[i].getParameterTypes(), |
| arguments); |
| if (match != NO_MATCH) { |
| if (match > currentMatch) { |
| method = methods[i]; |
| currentMatch = match; |
| ambiguous = false; |
| } |
| else if (match == currentMatch) { |
| ambiguous = true; |
| } |
| } |
| } |
| } |
| if (ambiguous) { |
| throw new JXPathException("Ambigous method call: " + name); |
| } |
| return method; |
| } |
| |
| /** |
| * Return a match code of objects to types. |
| * @param types Class[] of expected types |
| * @param parameters Object[] to attempt to match |
| * @return int code |
| */ |
| private static int matchParameterTypes( |
| Class[] types, |
| Object[] parameters) { |
| int pi = 0; |
| if (types.length >= 1 |
| && ExpressionContext.class.isAssignableFrom(types[0])) { |
| pi++; |
| } |
| int length = parameters == null ? 0 : parameters.length; |
| if (types.length != length + pi) { |
| return NO_MATCH; |
| } |
| int totalMatch = EXACT_MATCH; |
| for (int i = 0; i < length; i++) { |
| int match = matchType(types[i + pi], parameters[i]); |
| if (match == NO_MATCH) { |
| return NO_MATCH; |
| } |
| if (match < totalMatch) { |
| totalMatch = match; |
| } |
| } |
| return totalMatch; |
| } |
| |
| /** |
| * Return a match code between an object and type. |
| * @param expected class to test |
| * @param object object to test |
| * @return int code |
| */ |
| private static int matchType(Class expected, Object object) { |
| if (object == null) { |
| return APPROXIMATE_MATCH; |
| } |
| |
| Class actual = object.getClass(); |
| |
| if (expected.equals(actual)) { |
| return EXACT_MATCH; |
| } |
| if (expected.isAssignableFrom(actual)) { |
| return EXACT_MATCH; |
| } |
| |
| if (TypeUtils.canConvert(object, expected)) { |
| return APPROXIMATE_MATCH; |
| } |
| |
| return NO_MATCH; |
| } |
| } |