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

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