View Javadoc

1   /*
2    * Copyright 1999-2004 The Apache Software Foundation
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.jxpath.util;
17  
18  import java.lang.reflect.Constructor;
19  import java.lang.reflect.Method;
20  import java.lang.reflect.Modifier;
21  import java.util.Arrays;
22  
23  import org.apache.commons.jxpath.ExpressionContext;
24  import org.apache.commons.jxpath.JXPathException;
25  
26  /***
27   * Method lookup utilities, which find static and non-static methods as well
28   * as constructors based on a name and list of parameters.
29   *
30   * @author Dmitri Plotnikov
31   * @version $Revision: 1.7 $ $Date: 2004/02/29 14:17:43 $
32   */
33  public class MethodLookupUtils {
34  
35      private static final int NO_MATCH = 0;
36      private static final int APPROXIMATE_MATCH = 1;
37      private static final int EXACT_MATCH = 2;
38      private static final Object[] EMPTY_ARRAY = new Object[0];
39  
40       public static Constructor lookupConstructor(
41          Class targetClass,
42          Object[] parameters) 
43       {
44          boolean tryExact = true;
45          int count = parameters == null ? 0 : parameters.length;
46          Class types[] = new Class[count];
47          for (int i = 0; i < count; i++) {
48              Object param = parameters[i];
49              if (param != null) {
50                  types[i] = param.getClass();
51              }
52              else {
53                  types[i] = null;
54                  tryExact = false;
55              }
56          }
57  
58          Constructor constructor = null;
59  
60          if (tryExact) {
61              // First - without type conversion
62              try {
63                  constructor = targetClass.getConstructor(types);
64                  if (constructor != null) {
65                      return constructor;
66                  }
67              }
68              catch (NoSuchMethodException ex) {
69                  // Ignore
70              }
71          }
72  
73          int currentMatch = 0;
74          boolean ambiguous = false;
75  
76          // Then - with type conversion
77          Constructor[] constructors = targetClass.getConstructors();
78          for (int i = 0; i < constructors.length; i++) {
79              int match =
80                  matchParameterTypes(
81                      constructors[i].getParameterTypes(),
82                      parameters);
83              if (match != NO_MATCH) {
84                  if (match > currentMatch) {
85                      constructor = constructors[i];
86                      currentMatch = match;
87                      ambiguous = false;
88                  }
89                  else if (match == currentMatch) {
90                      ambiguous = true;
91                  }
92              }
93          }
94          if (ambiguous) {
95              throw new JXPathException(
96                  "Ambigous constructor " + Arrays.asList(parameters));
97          }
98          return constructor;
99      }
100 
101     public static Method lookupStaticMethod(
102         Class targetClass,
103         String name,
104         Object[] parameters) 
105     {
106         boolean tryExact = true;
107         int count = parameters == null ? 0 : parameters.length;
108         Class types[] = new Class[count];
109         for (int i = 0; i < count; i++) {
110             Object param = parameters[i];
111             if (param != null) {
112                 types[i] = param.getClass();
113             }
114             else {
115                 types[i] = null;
116                 tryExact = false;
117             }
118         }
119 
120         Method method = null;
121 
122         if (tryExact) {
123             // First - without type conversion
124             try {
125                 method = targetClass.getMethod(name, types);
126                 if (method != null
127                     && Modifier.isStatic(method.getModifiers())) {
128                     return method;
129                 }
130             }
131             catch (NoSuchMethodException ex) {
132                 // Ignore
133             }
134         }
135 
136         int currentMatch = 0;
137         boolean ambiguous = false;
138 
139         // Then - with type conversion
140         Method[] methods = targetClass.getMethods();
141         for (int i = 0; i < methods.length; i++) {
142             if (Modifier.isStatic(methods[i].getModifiers())
143                 && methods[i].getName().equals(name)) {
144                 int match =
145                     matchParameterTypes(
146                         methods[i].getParameterTypes(),
147                         parameters);
148                 if (match != NO_MATCH) {
149                     if (match > currentMatch) {
150                         method = methods[i];
151                         currentMatch = match;
152                         ambiguous = false;
153                     }
154                     else if (match == currentMatch) {
155                         ambiguous = true;
156                     }
157                 }
158             }
159         }
160         if (ambiguous) {
161             throw new JXPathException("Ambigous method call: " + name);
162         }
163         return method;
164     }
165 
166     public static Method lookupMethod(
167         Class targetClass,
168         String name,
169         Object[] parameters) 
170     {
171         if (parameters == null
172             || parameters.length < 1
173             || parameters[0] == null) {
174             return null;
175         }
176 
177         if (matchType(targetClass, parameters[0]) == NO_MATCH) {
178             return null;
179         }
180 
181         targetClass = TypeUtils.convert(parameters[0], targetClass).getClass();
182 
183         boolean tryExact = true;
184         int count = parameters.length - 1;
185         Class types[] = new Class[count];
186         Object arguments[] = new Object[count];
187         for (int i = 0; i < count; i++) {
188             Object param = parameters[i + 1];
189             arguments[i] = param;
190             if (param != null) {
191                 types[i] = param.getClass();
192             }
193             else {
194                 types[i] = null;
195                 tryExact = false;
196             }
197         }
198 
199         Method method = null;
200 
201         if (tryExact) {
202             // First - without type conversion
203             try {
204                 method = targetClass.getMethod(name, types);
205                 if (method != null
206                     && !Modifier.isStatic(method.getModifiers())) {
207                     return method;
208                 }
209             }
210             catch (NoSuchMethodException ex) {
211                 // Ignore
212             }
213         }
214 
215         int currentMatch = 0;
216         boolean ambiguous = false;
217 
218         // Then - with type conversion
219         Method[] methods = targetClass.getMethods();
220         for (int i = 0; i < methods.length; i++) {
221             if (!Modifier.isStatic(methods[i].getModifiers())
222                 && methods[i].getName().equals(name)) {
223                 int match =
224                     matchParameterTypes(
225                         methods[i].getParameterTypes(),
226                         arguments);
227                 if (match != NO_MATCH) {
228                     if (match > currentMatch) {
229                         method = methods[i];
230                         currentMatch = match;
231                         ambiguous = false;
232                     }
233                     else if (match == currentMatch) {
234                         ambiguous = true;
235                     }
236                 }
237             }
238         }
239         if (ambiguous) {
240             throw new JXPathException("Ambigous method call: " + name);
241         }
242         return method;
243     }
244 
245     private static int matchParameterTypes(
246         Class types[],
247         Object parameters[]) 
248     {
249         int pi = 0;
250         if (types.length >= 1
251             && ExpressionContext.class.isAssignableFrom(types[0])) {
252             pi++;
253         }
254         int length = parameters == null ? 0 : parameters.length;
255         if (types.length != length + pi) {
256             return NO_MATCH;
257         }
258         int totalMatch = EXACT_MATCH;
259         for (int i = 0; i < length; i++) {
260             int match = matchType(types[i + pi], parameters[i]);
261             if (match == NO_MATCH) {
262                 return NO_MATCH;
263             }
264             if (match < totalMatch) {
265                 totalMatch = match;
266             }
267         }
268         return totalMatch;
269     }
270 
271     private static int matchType(Class expected, Object object) {
272         if (object == null) {
273             return APPROXIMATE_MATCH;
274         }
275 
276         Class actual = object.getClass();
277 
278         if (expected.equals(actual)) {
279             return EXACT_MATCH;
280         }
281         if (expected.isAssignableFrom(actual)) {
282             return EXACT_MATCH;
283         }
284 
285         if (TypeUtils.canConvert(object, expected)) {
286             return APPROXIMATE_MATCH;
287         }
288 
289         return NO_MATCH;
290     }
291 }