Coverage report

| %line | %branch
--- | --- | ---
org.apache.commons.jexl.util.introspection.ClassMap$MethodInfo |
0% |
0% |

1 |  |
     /*
2 |  |
      * Copyright 2001-2002,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 |  |
17 |  |
     package org.apache.commons.jexl.util.introspection;
18 |  |
19 |  |
     import java.lang.reflect.Method;
20 |  |
     import java.lang.reflect.Modifier;
21 |  |
     import java.util.Hashtable;
22 |  |
     import java.util.Map;
23 |  |
24 |  |
     /**
25 |  |
      * Taken from the Velocity tree so we can be self-sufficient
26 |  |
      *
27 |  |
      * A cache of introspection information for a specific class instance. Keys
28 |  |
      * {@link java.lang.Method} objects by a concatenation of the method name and
29 |  |
      * the names of classes that make up the parameters.
30 |  |
      *
31 |  |
      * @since 1.0
32 |  |
      * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
33 |  |
      * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
34 |  |
      * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
35 |  |
      * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
36 |  |
      * @version $Id: ClassMap.java 398459 2006-04-30 23:14:30Z dion $
37 |  |
      */
38 |  |
     public class ClassMap {
39 |  |
         /** represents a miss on the cached data. */
40 |  |
         private static final class CacheMiss {
41 |  |
         }
42 |  |
43 |  |
         /** constant for a miss on the cached data. */
44 |  |
         private static final CacheMiss CACHE_MISS = new CacheMiss();
45 |  |
46 |  |
         /** represents null or missing arguments. */
47 |  |
         private static final Object OBJECT = new Object();
48 |  |
49 |  |
         /**
50 |  |
          * Class passed into the constructor used to as the basis for the Method
51 |  |
          * map.
52 |  |
          */
53 |  |
54 |  |
         private Class clazz;
55 |  |
56 |  |
         /**
57 |  |
          * Cache of Methods, or CACHE_MISS, keyed by method name and actual
58 |  |
          * arguments used to find it.
59 |  |
          */
60 |  |
         private final Map methodCache = new Hashtable();
61 |  |
62 |  |
         /** map from method name and args to a {@link Method}. */
63 |  |
         private final MethodMap methodMap = new MethodMap();
64 |  |
65 |  |
         /**
66 |  |
          * Standard constructor.
67 |  |
          * @param aClass the class to deconstruct.
68 |  |
          */
69 |  |
         public ClassMap(Class aClass) {
70 |  |
             clazz = aClass;
71 |  |
             populateMethodCache();
72 |  |
         }
73 |  |
74 |  |
         /**
75 |  |
          * @return the class object whose methods are cached by this map.
76 |  |
          */
77 |  |
         Class getCachedClass() {
78 |  |
             return clazz;
79 |  |
         }
80 |  |
81 |  |
         /**
82 |  |
          * Find a Method using the methodKey provided.
83 |  |
          *
84 |  |
          * Look in the methodMap for an entry. If found, it'll either be a
85 |  |
          * CACHE_MISS, in which case we simply give up, or it'll be a Method, in
86 |  |
          * which case, we return it.
87 |  |
          *
88 |  |
          * If nothing is found, then we must actually go and introspect the method
89 |  |
          * from the MethodMap.
90 |  |
          *
91 |  |
          * @param name method name
92 |  |
          * @param params method parameters
93 |  |
          * @return CACHE_MISS or a {@link Method}
94 |  |
          * @throws MethodMap.AmbiguousException if the method and parameters are ambiguous.
95 |  |
          */
96 |  |
         public Method findMethod(String name, Object[] params) throws MethodMap.AmbiguousException {
97 |  |
             String methodKey = makeMethodKey(name, params);
98 |  |
             Object cacheEntry = methodCache.get(methodKey);
99 |  |
100 |  |
             if (cacheEntry == CACHE_MISS) {
101 |  |
                 return null;
102 |  |
             }
103 |  |
104 |  |
             if (cacheEntry == null) {
105 |  |
                 try {
106 |  |
                     cacheEntry = methodMap.find(name, params);
107 |  |
                 } catch (MethodMap.AmbiguousException ae) {
108 |  |
                     /*
109 |  |
                      * that's a miss :)
110 |  |
                      */
111 |  |
112 |  |
                     methodCache.put(methodKey, CACHE_MISS);
113 |  |
114 |  |
                     throw ae;
115 |  |
                 }
116 |  |
117 |  |
                 if (cacheEntry == null) {
118 |  |
                     methodCache.put(methodKey, CACHE_MISS);
119 |  |
                 } else {
120 |  |
                     methodCache.put(methodKey, cacheEntry);
121 |  |
                 }
122 |  |
             }
123 |  |
124 |  |
             // Yes, this might just be null.
125 |  |
126 |  |
             return (Method) cacheEntry;
127 |  |
         }
128 |  |
129 |  |
         /**
130 |  |
          * Populate the Map of direct hits. These are taken from all the public
131 |  |
          * methods that our class provides.
132 |  |
          */
133 |  |
         private void populateMethodCache() {
134 |  |
135 |  |
             /*
136 |  |
              * get all publicly accessible methods
137 |  |
              */
138 |  |
139 |  |
             Method[] methods = getAccessibleMethods(clazz);
140 |  |
141 |  |
             /*
142 |  |
              * map and cache them
143 |  |
              */
144 |  |
145 |  |
             for (int i = 0; i < methods.length; i++) {
146 |  |
                 Method method = methods[i];
147 |  |
148 |  |
                 /*
149 |  |
                  * now get the 'public method', the method declared by a public
150 |  |
                  * interface or class. (because the actual implementing class may be
151 |  |
                  * a facade...
152 |  |
                  */
153 |  |
154 |  |
                 Method publicMethod = getPublicMethod(method);
155 |  |
156 |  |
                 /*
157 |  |
                  * it is entirely possible that there is no public method for the
158 |  |
                  * methods of this class (i.e. in the facade, a method that isn't on
159 |  |
                  * any of the interfaces or superclass in which case, ignore it.
160 |  |
                  * Otherwise, map and cache
161 |  |
                  */
162 |  |
163 |  |
                 if (publicMethod != null) {
164 |  |
                     methodMap.add(publicMethod);
165 |  |
                     methodCache.put(makeMethodKey(publicMethod), publicMethod);
166 |  |
                 }
167 |  |
             }
168 |  |
         }
169 |  |
170 |  |
         /**
171 |  |
          * Make a methodKey for the given method using the concatenation of the name
172 |  |
          * and the types of the method parameters.
173 |  |
          */
174 |  |
         private String makeMethodKey(Method method) {
175 |  |
             Class[] parameterTypes = method.getParameterTypes();
176 |  |
177 |  |
             StringBuffer methodKey = new StringBuffer(method.getName());
178 |  |
179 |  |
             for (int j = 0; j < parameterTypes.length; j++) {
180 |  |
                 /*
181 |  |
                  * If the argument type is primitive then we want to convert our
182 |  |
                  * primitive type signature to the corresponding Object type so
183 |  |
                  * introspection for methods with primitive types will work
184 |  |
                  * correctly.
185 |  |
                  */
186 |  |
                 if (parameterTypes[j].isPrimitive()) {
187 |  |
                     if (parameterTypes[j].equals(Boolean.TYPE))
188 |  |
                         methodKey.append("java.lang.Boolean");
189 |  |
                     else if (parameterTypes[j].equals(Byte.TYPE))
190 |  |
                         methodKey.append("java.lang.Byte");
191 |  |
                     else if (parameterTypes[j].equals(Character.TYPE))
192 |  |
                         methodKey.append("java.lang.Character");
193 |  |
                     else if (parameterTypes[j].equals(Double.TYPE))
194 |  |
                         methodKey.append("java.lang.Double");
195 |  |
                     else if (parameterTypes[j].equals(Float.TYPE))
196 |  |
                         methodKey.append("java.lang.Float");
197 |  |
                     else if (parameterTypes[j].equals(Integer.TYPE))
198 |  |
                         methodKey.append("java.lang.Integer");
199 |  |
                     else if (parameterTypes[j].equals(Long.TYPE))
200 |  |
                         methodKey.append("java.lang.Long");
201 |  |
                     else if (parameterTypes[j].equals(Short.TYPE))
202 |  |
                         methodKey.append("java.lang.Short");
203 |  |
                 } else {
204 |  |
                     methodKey.append(parameterTypes[j].getName());
205 |  |
                 }
206 |  |
             }
207 |  |
208 |  |
             return methodKey.toString();
209 |  |
         }
210 |  |
211 |  |
         private static String makeMethodKey(String method, Object[] params) {
212 |  |
             StringBuffer methodKey = new StringBuffer().append(method);
213 |  |
214 |  |
             for (int j = 0; j < params.length; j++) {
215 |  |
                 Object arg = params[j];
216 |  |
217 |  |
                 if (arg == null) {
218 |  |
                     arg = OBJECT;
219 |  |
                 }
220 |  |
221 |  |
                 methodKey.append(arg.getClass().getName());
222 |  |
             }
223 |  |
224 |  |
             return methodKey.toString();
225 |  |
         }
226 |  |
227 |  |
         /**
228 |  |
          * Retrieves public methods for a class. In case the class is not public,
229 |  |
          * retrieves methods with same signature as its public methods from public
230 |  |
          * superclasses and interfaces (if they exist). Basically upcasts every
231 |  |
          * method to the nearest acccessible method.
232 |  |
          */
233 |  |
         private static Method[] getAccessibleMethods(Class clazz) {
234 |  |
             Method[] methods = clazz.getMethods();
235 |  |
236 |  |
             /*
237 |  |
              * Short circuit for the (hopefully) majority of cases where the clazz
238 |  |
              * is public
239 |  |
              */
240 |  |
241 |  |
             if (Modclass="keyword">ifier.isPublic(clazz.getModclass="keyword">ifiers())) {
242 |  |
                 return methods;
243 |  |
             }
244 |  |
245 |  |
             /*
246 |  |
              * No luck - the class is not public, so we're going the longer way.
247 |  |
              */
248 |  |
249 |  |
             MethodInfo[] methodInfos = new MethodInfo[methods.length];
250 |  |
251 |  |
             for (int i = methods.length; i-- > 0;) {
252 |  |
                 methodInfos[i] = new MethodInfo(methods[i]);
253 |  |
             }
254 |  |
255 |  |
             int upcastCount = getAccessibleMethods(clazz, methodInfos, 0);
256 |  |
257 |  |
             /*
258 |  |
              * Reallocate array in case some method had no accessible counterpart.
259 |  |
              */
260 |  |
261 |  |
             if (upcastCount < methods.length) {
262 |  |
                 methods = new Method[upcastCount];
263 |  |
             }
264 |  |
265 |  |
             int j = 0;
266 |  |
             for (int i = 0; i < methodInfos.length; ++i) {
267 |  |
                 MethodInfo methodInfo = methodInfos[i];
268 |  |
                 if (methodInfo.upcast) {
269 |  |
                     methods[j++] = methodInfo.method;
270 |  |
                 }
271 |  |
             }
272 |  |
             return methods;
273 |  |
         }
274 |  |
275 |  |
         /**
276 |  |
          * Recursively finds a match for each method, starting with the class, and
277 |  |
          * then searching the superclass and interfaces.
278 |  |
          *
279 |  |
          * @param clazz Class to check
280 |  |
          * @param methodInfos array of methods we are searching to match
281 |  |
          * @param upcastCount current number of methods we have matched
282 |  |
          * @return count of matched methods
283 |  |
          */
284 |  |
         private static int getAccessibleMethods(Class clazz, MethodInfo[] methodInfos, class="keyword">int upcastCount) {
285 |  |
             int l = methodInfos.length;
286 |  |
287 |  |
             /*
288 |  |
              * if this class is public, then check each of the currently
289 |  |
              * 'non-upcasted' methods to see if we have a match
290 |  |
              */
291 |  |
292 |  |
             if (Modclass="keyword">ifier.isPublic(clazz.getModclass="keyword">ifiers())) {
293 |  |
                 for (int i = 0; i < l && upcastCount < l; ++i) {
294 |  |
                     try {
295 |  |
                         MethodInfo methodInfo = methodInfos[i];
296 |  |
297 |  |
                         if (!methodInfo.upcast) {
298 |  |
                             methodInfo.tryUpcasting(clazz);
299 |  |
                             upcastCount++;
300 |  |
                         }
301 |  |
                     } catch (NoSuchMethodException e) {
302 |  |
                         /*
303 |  |
                          * Intentionally ignored - it means it wasn't found in the
304 |  |
                          * current class
305 |  |
                          */
306 |  |
                     }
307 |  |
                 }
308 |  |
309 |  |
                 /*
310 |  |
                  * Short circuit if all methods were upcast
311 |  |
                  */
312 |  |
313 |  |
                 if (upcastCount == l) {
314 |  |
                     return upcastCount;
315 |  |
                 }
316 |  |
             }
317 |  |
318 |  |
             /*
319 |  |
              * Examine superclass
320 |  |
              */
321 |  |
322 |  |
             Class superclazz = clazz.getSuperclass();
323 |  |
324 |  |
             if (superclazz != null) {
325 |  |
                 upcastCount = getAccessibleMethods(superclazz, methodInfos, upcastCount);
326 |  |
327 |  |
                 /*
328 |  |
                  * Short circuit if all methods were upcast
329 |  |
                  */
330 |  |
331 |  |
                 if (upcastCount == l) {
332 |  |
                     return upcastCount;
333 |  |
                 }
334 |  |
             }
335 |  |
336 |  |
             /*
337 |  |
              * Examine interfaces. Note we do it even if superclazz == null. This is
338 |  |
              * redundant as currently java.lang.Object does not implement any
339 |  |
              * interfaces, however nothing guarantees it will not in future.
340 |  |
              */
341 |  |
342 |  |
             Class[] interfaces = clazz.getInterfaces();
343 |  |
344 |  |
             for (int i = class="keyword">interfaces.length; i-- > 0;) {
345 |  |
                 upcastCount = getAccessibleMethods(interfaces[i], methodInfos, upcastCount);
346 |  |
347 |  |
                 /*
348 |  |
                  * Short circuit if all methods were upcast
349 |  |
                  */
350 |  |
351 |  |
                 if (upcastCount == l) {
352 |  |
                     return upcastCount;
353 |  |
                 }
354 |  |
             }
355 |  |
356 |  |
             return upcastCount;
357 |  |
         }
358 |  |
359 |  |
         /**
360 |  |
          * For a given method, retrieves its publicly accessible counterpart. This
361 |  |
          * method will look for a method with same name and signature declared in a
362 |  |
          * public superclass or implemented interface of this method's declaring
363 |  |
          * class. This counterpart method is publicly callable.
364 |  |
          *
365 |  |
          * @param method a method whose publicly callable counterpart is requested.
366 |  |
          * @return the publicly callable counterpart method. Note that if the
367 |  |
          *         parameter method is itself declared by a public class, this
368 |  |
          *         method is an identity function.
369 |  |
          */
370 |  |
         public static Method getPublicMethod(Method method) {
371 |  |
             Class clazz = method.getDeclaringClass();
372 |  |
373 |  |
             /*
374 |  |
              * Short circuit for (hopefully the majority of) cases where the
375 |  |
              * declaring class is public.
376 |  |
              */
377 |  |
378 |  |
             if ((clazz.getModclass="keyword">ifiers() & Modclass="keyword">ifier.PUBLIC) != 0) {
379 |  |
                 return method;
380 |  |
             }
381 |  |
382 |  |
             return getPublicMethod(clazz, method.getName(), method.getParameterTypes());
383 |  |
         }
384 |  |
385 |  |
         /**
386 |  |
          * Looks up the method with specified name and signature in the first public
387 |  |
          * superclass or implemented interface of the class.
388 |  |
          *
389 |  |
          * @param class the class whose method is sought
390 |  |
          * @param name the name of the method
391 |  |
          * @param paramTypes the classes of method parameters
392 |  |
          */
393 |  |
         private static Method getPublicMethod(Class clazz, String name, Class[] paramTypes) {
394 |  |
             /*
395 |  |
              * if this class is public, then try to get it
396 |  |
              */
397 |  |
398 |  |
             if ((clazz.getModclass="keyword">ifiers() & Modclass="keyword">ifier.PUBLIC) != 0) {
399 |  |
                 try {
400 |  |
                     return clazz.getMethod(name, paramTypes);
401 |  |
                 } catch (NoSuchMethodException e) {
402 |  |
                     /*
403 |  |
                      * If the class does not have the method, then neither its
404 |  |
                      * superclass nor any of its interfaces has it so quickly return
405 |  |
                      * null.
406 |  |
                      */
407 |  |
                     return null;
408 |  |
                 }
409 |  |
             }
410 |  |
411 |  |
             /*
412 |  |
              * try the superclass
413 |  |
              */
414 |  |
415 |  |
             Class superclazz = clazz.getSuperclass();
416 |  |
417 |  |
             if (superclazz != null) {
418 |  |
                 Method superclazzMethod = getPublicMethod(superclazz, name, paramTypes);
419 |  |
420 |  |
                 if (superclazzMethod != null) {
421 |  |
                     return superclazzMethod;
422 |  |
                 }
423 |  |
             }
424 |  |
425 |  |
             /*
426 |  |
              * and interfaces
427 |  |
              */
428 |  |
429 |  |
             Class[] interfaces = clazz.getInterfaces();
430 |  |
431 |  |
             for (int i = 0; i < class="keyword">interfaces.length; ++i) {
432 |  |
                 Method interfaceMethod = getPublicMethod(interfaces[i], name, paramTypes);
433 |  |
434 |  |
                 if (interfaceMethod != null) {
435 |  |
                     return interfaceMethod;
436 |  |
                 }
437 |  |
             }
438 |  |
439 |  |
             return null;
440 |  |
         }
441 |  |
442 |  |
         /**
443 |  |
          * Used for the iterative discovery process for public methods.
444 |  |
          */
445 |  |
         private static final class MethodInfo {
446 |  |
             Method method;
447 |  |
448 |  |
             String name;
449 |  |
450 |  |
             Class[] parameterTypes;
451 |  |
452 |  |
             boolean upcast;
453 |  |
454 | 0 |
             MethodInfo(Method method) {
455 | 0 |
                 this.method = null;
456 | 0 |
                 name = method.getName();
457 | 0 |
                 parameterTypes = method.getParameterTypes();
458 | 0 |
                 upcast = false;
459 | 0 |
             }
460 |  |
461 |  |
             void tryUpcasting(Class clazz) throws NoSuchMethodException {
462 | 0 |
                 method = clazz.getMethod(name, parameterTypes);
463 | 0 |
                 name = null;
464 | 0 |
                 parameterTypes = null;
465 | 0 |
                 upcast = true;
466 | 0 |
             }
467 |  |
         }
468 |  |
     }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.