001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.jxpath.util; 018 019import java.lang.reflect.Constructor; 020import java.lang.reflect.Method; 021import java.lang.reflect.Modifier; 022import java.util.Arrays; 023 024import org.apache.commons.jxpath.ExpressionContext; 025import org.apache.commons.jxpath.JXPathException; 026 027/** 028 * Method lookup utilities, which find static and non-static methods as well 029 * as constructors based on a name and list of parameters. 030 * 031 * @author Dmitri Plotnikov 032 * @version $Revision: 917195 $ $Date: 2010-02-28 17:21:14 +0100 (So, 28 Feb 2010) $ 033 */ 034public class MethodLookupUtils { 035 036 private static final int NO_MATCH = 0; 037 private static final int APPROXIMATE_MATCH = 1; 038 private static final int EXACT_MATCH = 2; 039 040 /** 041 * Look up a constructor. 042 * @param targetClass the class constructed 043 * @param parameters arguments 044 * @return Constructor found if any. 045 */ 046 public static Constructor lookupConstructor( 047 Class targetClass, 048 Object[] parameters) { 049 boolean tryExact = true; 050 int count = parameters == null ? 0 : parameters.length; 051 Class[] types = new Class[count]; 052 for (int i = 0; i < count; i++) { 053 Object param = parameters[i]; 054 if (param != null) { 055 types[i] = param.getClass(); 056 } 057 else { 058 types[i] = null; 059 tryExact = false; 060 } 061 } 062 063 Constructor constructor = null; 064 065 if (tryExact) { 066 // First - without type conversion 067 try { 068 constructor = targetClass.getConstructor(types); 069 if (constructor != null) { 070 return constructor; 071 } 072 } 073 catch (NoSuchMethodException ex) { //NOPMD 074 // Ignore 075 } 076 } 077 078 int currentMatch = 0; 079 boolean ambiguous = false; 080 081 // Then - with type conversion 082 Constructor[] constructors = targetClass.getConstructors(); 083 for (int i = 0; i < constructors.length; i++) { 084 int match = 085 matchParameterTypes( 086 constructors[i].getParameterTypes(), 087 parameters); 088 if (match != NO_MATCH) { 089 if (match > currentMatch) { 090 constructor = constructors[i]; 091 currentMatch = match; 092 ambiguous = false; 093 } 094 else if (match == currentMatch) { 095 ambiguous = true; 096 } 097 } 098 } 099 if (ambiguous) { 100 throw new JXPathException( 101 "Ambiguous constructor " + Arrays.asList(parameters)); 102 } 103 return constructor; 104 } 105 106 /** 107 * Look up a static method. 108 * @param targetClass the owning class 109 * @param name method name 110 * @param parameters method parameters 111 * @return Method found if any 112 */ 113 public static Method lookupStaticMethod( 114 Class targetClass, 115 String name, 116 Object[] parameters) { 117 boolean tryExact = true; 118 int count = parameters == null ? 0 : parameters.length; 119 Class[] types = new Class[count]; 120 for (int i = 0; i < count; i++) { 121 Object param = parameters[i]; 122 if (param != null) { 123 types[i] = param.getClass(); 124 } 125 else { 126 types[i] = null; 127 tryExact = false; 128 } 129 } 130 131 Method method = null; 132 133 if (tryExact) { 134 // First - without type conversion 135 try { 136 method = targetClass.getMethod(name, types); 137 if (method != null 138 && Modifier.isStatic(method.getModifiers())) { 139 return method; 140 } 141 } 142 catch (NoSuchMethodException ex) { //NOPMD 143 // Ignore 144 } 145 } 146 147 int currentMatch = 0; 148 boolean ambiguous = false; 149 150 // Then - with type conversion 151 Method[] methods = targetClass.getMethods(); 152 for (int i = 0; i < methods.length; i++) { 153 if (Modifier.isStatic(methods[i].getModifiers()) 154 && methods[i].getName().equals(name)) { 155 int match = 156 matchParameterTypes( 157 methods[i].getParameterTypes(), 158 parameters); 159 if (match != NO_MATCH) { 160 if (match > currentMatch) { 161 method = methods[i]; 162 currentMatch = match; 163 ambiguous = false; 164 } 165 else if (match == currentMatch) { 166 ambiguous = true; 167 } 168 } 169 } 170 } 171 if (ambiguous) { 172 throw new JXPathException("Ambiguous method call: " + name); 173 } 174 return method; 175 } 176 177 /** 178 * Look up a method. 179 * @param targetClass owning class 180 * @param name method name 181 * @param parameters method parameters 182 * @return Method found if any 183 */ 184 public static Method lookupMethod( 185 Class targetClass, 186 String name, 187 Object[] parameters) { 188 if (parameters == null 189 || parameters.length < 1 190 || parameters[0] == null) { 191 return null; 192 } 193 194 if (matchType(targetClass, parameters[0]) == NO_MATCH) { 195 return null; 196 } 197 198 targetClass = TypeUtils.convert(parameters[0], targetClass).getClass(); 199 200 boolean tryExact = true; 201 int count = parameters.length - 1; 202 Class[] types = new Class[count]; 203 Object[] arguments = new Object[count]; 204 for (int i = 0; i < count; i++) { 205 Object param = parameters[i + 1]; 206 arguments[i] = param; 207 if (param != null) { 208 types[i] = param.getClass(); 209 } 210 else { 211 types[i] = null; 212 tryExact = false; 213 } 214 } 215 216 Method method = null; 217 218 if (tryExact) { 219 // First - without type conversion 220 try { 221 method = targetClass.getMethod(name, types); 222 if (method != null 223 && !Modifier.isStatic(method.getModifiers())) { 224 return method; 225 } 226 } 227 catch (NoSuchMethodException ex) { //NOPMD 228 // Ignore 229 } 230 } 231 232 int currentMatch = 0; 233 boolean ambiguous = false; 234 235 // Then - with type conversion 236 Method[] methods = targetClass.getMethods(); 237 for (int i = 0; i < methods.length; i++) { 238 if (!Modifier.isStatic(methods[i].getModifiers()) 239 && methods[i].getName().equals(name)) { 240 int match = 241 matchParameterTypes( 242 methods[i].getParameterTypes(), 243 arguments); 244 if (match != NO_MATCH) { 245 if (match > currentMatch) { 246 method = methods[i]; 247 currentMatch = match; 248 ambiguous = false; 249 } 250 else if (match == currentMatch) { 251 ambiguous = true; 252 } 253 } 254 } 255 } 256 if (ambiguous) { 257 throw new JXPathException("Ambiguous method call: " + name); 258 } 259 return method; 260 } 261 262 /** 263 * Return a match code of objects to types. 264 * @param types Class[] of expected types 265 * @param parameters Object[] to attempt to match 266 * @return int code 267 */ 268 private static int matchParameterTypes( 269 Class[] types, 270 Object[] parameters) { 271 int pi = 0; 272 if (types.length >= 1 273 && ExpressionContext.class.isAssignableFrom(types[0])) { 274 pi++; 275 } 276 int length = parameters == null ? 0 : parameters.length; 277 if (types.length != length + pi) { 278 return NO_MATCH; 279 } 280 int totalMatch = EXACT_MATCH; 281 for (int i = 0; i < length; i++) { 282 int match = matchType(types[i + pi], parameters[i]); 283 if (match == NO_MATCH) { 284 return NO_MATCH; 285 } 286 if (match < totalMatch) { 287 totalMatch = match; 288 } 289 } 290 return totalMatch; 291 } 292 293 /** 294 * Return a match code between an object and type. 295 * @param expected class to test 296 * @param object object to test 297 * @return int code 298 */ 299 private static int matchType(Class expected, Object object) { 300 if (object == null) { 301 return APPROXIMATE_MATCH; 302 } 303 304 Class actual = object.getClass(); 305 306 if (expected.equals(actual)) { 307 return EXACT_MATCH; 308 } 309 if (expected.isAssignableFrom(actual)) { 310 return EXACT_MATCH; 311 } 312 313 if (TypeUtils.canConvert(object, expected)) { 314 return APPROXIMATE_MATCH; 315 } 316 317 return NO_MATCH; 318 } 319}