Coverage report

| %line | %branch
--- | --- | ---
org.apache.commons.jexl.util.introspection.MethodMap$AmbiguousException |
0% |
0% |

1 |  |
     /*
2 |  |
      * Copyright 2001,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.util.ArrayList;
21 |  |
     import java.util.Hashtable;
22 |  |
     import java.util.Iterator;
23 |  |
     import java.util.LinkedList;
24 |  |
     import java.util.List;
25 |  |
     import java.util.Map;
26 |  |
27 |  |
     /**
28 |  |
      *
29 |  |
      * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
30 |  |
      * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
31 |  |
      * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
32 |  |
      * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
33 |  |
      * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
34 |  |
      * @version $Id: MethodMap.java,v 1.5 2004/08/19 17:15:59 dion Exp $
35 |  |
      */
36 |  |
     public class MethodMap
37 |  |
     {
38 |  |
         private static final int MORE_SPECIFIC = 0;
39 |  |
         private static final int LESS_SPECIFIC = 1;
40 |  |
         private static final int INCOMPARABLE = 2;
41 |  |
42 |  |
         /**
43 |  |
          * Keep track of all methods with the same name.
44 |  |
          */
45 |  |
         Map methodByNameMap = new Hashtable();
46 |  |
47 |  |
         /**
48 |  |
          * Add a method to a list of methods by name.
49 |  |
          * For a particular class we are keeping track
50 |  |
          * of all the methods with the same name.
51 |  |
          */
52 |  |
         public void add(Method method)
53 |  |
         {
54 |  |
             String methodName = method.getName();
55 |  |
56 |  |
             List l = get( methodName );
57 |  |
58 |  |
             if ( l == null)
59 |  |
             {
60 |  |
                 l = new ArrayList();
61 |  |
                 methodByNameMap.put(methodName, l);
62 |  |
             }
63 |  |
64 |  |
             l.add(method);
65 |  |
         }
66 |  |
67 |  |
         /**
68 |  |
          * Return a list of methods with the same name.
69 |  |
          *
70 |  |
          * @param String key
71 |  |
          * @return List list of methods
72 |  |
          */
73 |  |
         public List get(String key)
74 |  |
         {
75 |  |
             return (List) methodByNameMap.get(key);
76 |  |
         }
77 |  |
78 |  |
         /**
79 |  |
          *  <p>
80 |  |
          *  Find a method.  Attempts to find the
81 |  |
          *  most specific applicable method using the
82 |  |
          *  algorithm described in the JLS section
83 |  |
          *  15.12.2 (with the exception that it can't
84 |  |
          *  distinguish a primitive type argument from
85 |  |
          *  an object type argument, since in reflection
86 |  |
          *  primitive type arguments are represented by
87 |  |
          *  their object counterparts, so for an argument of
88 |  |
          *  type (say) java.lang.Integer, it will not be able
89 |  |
          *  to decide between a method that takes int and a
90 |  |
          *  method that takes java.lang.Integer as a parameter.
91 |  |
          *  </p>
92 |  |
          *
93 |  |
          *  <p>
94 |  |
          *  This turns out to be a relatively rare case
95 |  |
          *  where this is needed - however, functionality
96 |  |
          *  like this is needed.
97 |  |
          *  </p>
98 |  |
          *
99 |  |
          *  @param methodName name of method
100 |  |
          *  @param args the actual arguments with which the method is called
101 |  |
          *  @return the most specific applicable method, or null if no
102 |  |
          *  method is applicable.
103 |  |
          *  @throws AmbiguousException if there is more than one maximally
104 |  |
          *  specific applicable method
105 |  |
          */
106 |  |
         public Method find(String methodName, Object[] args)
107 |  |
             throws AmbiguousException
108 |  |
         {
109 |  |
             List methodList = get(methodName);
110 |  |
111 |  |
             if (methodList == null)
112 |  |
             {
113 |  |
                 return null;
114 |  |
             }
115 |  |
116 |  |
             int l = args.length;
117 |  |
             Class[] classes = new Class[l];
118 |  |
119 |  |
             for(int i = 0; i < l; ++i)
120 |  |
             {
121 |  |
                 Object arg = args[i];
122 |  |
123 |  |
                 /*
124 |  |
                  * if we are careful down below, a null argument goes in there
125 |  |
                  * so we can know that the null was passed to the method
126 |  |
                  */
127 |  |
                 classes[i] =
128 |  |
                         arg == null ? class="keyword">null : arg.getClass();
129 |  |
             }
130 |  |
131 |  |
             return getMostSpecific(methodList, classes);
132 |  |
         }
133 |  |
134 |  |
         /**
135 |  |
          *  simple distinguishable exception, used when
136 |  |
          *  we run across ambiguous overloading
137 |  |
          */
138 | 0 |
         public static class AmbiguousException extends Exception
139 |  |
         {
140 |  |
         }
141 |  |
142 |  |
143 |  |
         private static Method getMostSpecific(List methods, Class[] classes)
144 |  |
             throws AmbiguousException
145 |  |
         {
146 |  |
             LinkedList applicables = getApplicables(methods, classes);
147 |  |
148 |  |
             if(applicables.isEmpty())
149 |  |
             {
150 |  |
                 return null;
151 |  |
             }
152 |  |
153 |  |
             if(applicables.size() == 1)
154 |  |
             {
155 |  |
                 return (Method)applicables.getFirst();
156 |  |
             }
157 |  |
158 |  |
             /*
159 |  |
              * This list will contain the maximally specific methods. Hopefully at
160 |  |
              * the end of the below loop, the list will contain exactly one method,
161 |  |
              * (the most specific method) otherwise we have ambiguity.
162 |  |
              */
163 |  |
164 |  |
             LinkedList maximals = new LinkedList();
165 |  |
166 |  |
             for (Iterator applicable = applicables.iterator();
167 |  |
                  applicable.hasNext();)
168 |  |
             {
169 |  |
                 Method app = (Method) applicable.next();
170 |  |
                 Class[] appArgs = app.getParameterTypes();
171 |  |
                 boolean lessSpecific = false;
172 |  |
173 |  |
                 for (Iterator maximal = maximals.iterator();
174 |  |
                      !lessSpecific && maximal.hasNext();)
175 |  |
                 {
176 |  |
                     Method max = (Method) maximal.next();
177 |  |
178 |  |
                     switch(moreSpecific(appArgs, max.getParameterTypes()))
179 |  |
                     {
180 |  |
                         case MORE_SPECIFIC:
181 |  |
                         {
182 |  |
                             /*
183 |  |
                              * This method is more specific than the previously
184 |  |
                              * known maximally specific, so remove the old maximum.
185 |  |
                              */
186 |  |
187 |  |
                             maximal.remove();
188 |  |
                             break;
189 |  |
                         }
190 |  |
191 |  |
                         case LESS_SPECIFIC:
192 |  |
                         {
193 |  |
                             /*
194 |  |
                              * This method is less specific than some of the
195 |  |
                              * currently known maximally specific methods, so we
196 |  |
                              * won't add it into the set of maximally specific
197 |  |
                              * methods
198 |  |
                              */
199 |  |
200 |  |
                             lessSpecific = true;
201 |  |
                             break;
202 |  |
                         }
203 |  |
                     }
204 |  |
                 }
205 |  |
206 |  |
                 if(!lessSpecclass="keyword">ific)
207 |  |
                 {
208 |  |
                     maximals.addLast(app);
209 |  |
                 }
210 |  |
             }
211 |  |
212 |  |
             if(maximals.size() > 1)
213 |  |
             {
214 |  |
                 // We have more than one maximally specific method
215 |  |
                 throw new AmbiguousException();
216 |  |
             }
217 |  |
218 |  |
             return (Method)maximals.getFirst();
219 |  |
         }
220 |  |
221 |  |
         /**
222 |  |
          * Determines which method signature (represented by a class array) is more
223 |  |
          * specific. This defines a partial ordering on the method signatures.
224 |  |
          * @param c1 first signature to compare
225 |  |
          * @param c2 second signature to compare
226 |  |
          * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
227 |  |
          * c1 is less specific than c2, INCOMPARABLE if they are incomparable.
228 |  |
          */
229 |  |
         private static int moreSpecific(Class[] c1, Class[] c2)
230 |  |
         {
231 |  |
             boolean c1MoreSpecific = false;
232 |  |
             boolean c2MoreSpecific = false;
233 |  |
234 |  |
             for(int i = 0; i < c1.length; ++i)
235 |  |
             {
236 |  |
                 if(c1[i] != c2[i])
237 |  |
                 {
238 |  |
                     c1MoreSpecific =
239 |  |
                         c1MoreSpecific ||
240 |  |
                         isStrictMethodInvocationConvertible(c2[i], c1[i]);
241 |  |
                     c2MoreSpecific =
242 |  |
                         c2MoreSpecific ||
243 |  |
                         isStrictMethodInvocationConvertible(c1[i], c2[i]);
244 |  |
                 }
245 |  |
             }
246 |  |
247 |  |
             if(c1MoreSpecclass="keyword">ific)
248 |  |
             {
249 |  |
                 if(c2MoreSpecclass="keyword">ific)
250 |  |
                 {
251 |  |
                     /*
252 |  |
                      *  Incomparable due to cross-assignable arguments (i.e.
253 |  |
                      * foo(String, Object) vs. foo(Object, String))
254 |  |
                      */
255 |  |
256 |  |
                     return INCOMPARABLE;
257 |  |
                 }
258 |  |
259 |  |
                 return MORE_SPECIFIC;
260 |  |
             }
261 |  |
262 |  |
             if(c2MoreSpecclass="keyword">ific)
263 |  |
             {
264 |  |
                 return LESS_SPECIFIC;
265 |  |
             }
266 |  |
267 |  |
             /*
268 |  |
              * Incomparable due to non-related arguments (i.e.
269 |  |
              * foo(Runnable) vs. foo(Serializable))
270 |  |
              */
271 |  |
272 |  |
             return INCOMPARABLE;
273 |  |
         }
274 |  |
275 |  |
         /**
276 |  |
          * Returns all methods that are applicable to actual argument types.
277 |  |
          * @param methods list of all candidate methods
278 |  |
          * @param classes the actual types of the arguments
279 |  |
          * @return a list that contains only applicable methods (number of
280 |  |
          * formal and actual arguments matches, and argument types are assignable
281 |  |
          * to formal types through a method invocation conversion).
282 |  |
          */
283 |  |
         private static LinkedList getApplicables(List methods, Class[] classes)
284 |  |
         {
285 |  |
             LinkedList list = new LinkedList();
286 |  |
287 |  |
             for (Iterator imethod = methods.iterator(); imethod.hasNext();)
288 |  |
             {
289 |  |
                 Method method = (Method) imethod.next();
290 |  |
291 |  |
                 if(isApplicable(method, classes))
292 |  |
                 {
293 |  |
                     list.add(method);
294 |  |
                 }
295 |  |
296 |  |
             }
297 |  |
             return list;
298 |  |
         }
299 |  |
300 |  |
         /**
301 |  |
          * Returns true if the supplied method is applicable to actual
302 |  |
          * argument types.
303 |  |
          */
304 |  |
         private static boolean isApplicable(Method method, Class[] classes)
305 |  |
         {
306 |  |
             Class[] methodArgs = method.getParameterTypes();
307 |  |
308 |  |
             if(methodArgs.length != classes.length)
309 |  |
             {
310 |  |
                 return false;
311 |  |
             }
312 |  |
313 |  |
             for(int i = 0; i < classes.length; ++i)
314 |  |
             {
315 |  |
                 if(!isMethodInvocationConvertible(methodArgs[i], classes[i]))
316 |  |
                 {
317 |  |
                     return false;
318 |  |
                 }
319 |  |
             }
320 |  |
321 |  |
             return true;
322 |  |
         }
323 |  |
324 |  |
         /**
325 |  |
          * Determines whether a type represented by a class object is
326 |  |
          * convertible to another type represented by a class object using a
327 |  |
          * method invocation conversion, treating object types of primitive
328 |  |
          * types as if they were primitive types (that is, a Boolean actual
329 |  |
          * parameter type matches boolean primitive formal type). This behavior
330 |  |
          * is because this method is used to determine applicable methods for
331 |  |
          * an actual parameter list, and primitive types are represented by
332 |  |
          * their object duals in reflective method calls.
333 |  |
          *
334 |  |
          * @param formal the formal parameter type to which the actual
335 |  |
          * parameter type should be convertible
336 |  |
          * @param actual the actual parameter type.
337 |  |
          * @return true if either formal type is assignable from actual type,
338 |  |
          * or formal is a primitive type and actual is its corresponding object
339 |  |
          * type or an object type of a primitive type that can be converted to
340 |  |
          * the formal type.
341 |  |
          */
342 |  |
         private static boolean isMethodInvocationConvertible(Class formal,
343 |  |
                                                              Class actual)
344 |  |
         {
345 |  |
             /*
346 |  |
              * if it's a null, it means the arg was null
347 |  |
              */
348 |  |
             if (actual == null && !formal.isPrimitive())
349 |  |
             {
350 |  |
                 return true;
351 |  |
             }
352 |  |
353 |  |
             /*
354 |  |
              *  Check for identity or widening reference conversion
355 |  |
              */
356 |  |
357 |  |
             if (actual != null && formal.isAssignableFrom(actual))
358 |  |
             {
359 |  |
                 return true;
360 |  |
             }
361 |  |
362 |  |
             /*
363 |  |
              * Check for boxing with widening primitive conversion. Note that
364 |  |
              * actual parameters are never primitives.
365 |  |
              */
366 |  |
367 |  |
             if (formal.isPrimitive())
368 |  |
             {
369 |  |
                 if(formal == Boolean.TYPE && actual == Boolean.class)
370 |  |
                     return true;
371 |  |
                 if(formal == Character.TYPE && actual == Character.class)
372 |  |
                     return true;
373 |  |
                 if(formal == Byte.TYPE && actual == Byte.class)
374 |  |
                     return true;
375 |  |
                 if(formal == Short.TYPE &&
376 |  |
                    (actual == Short.class || actual == Byte.class))
377 |  |
                     return true;
378 |  |
                 if(formal == Integer.TYPE &&
379 |  |
                    (actual == Integer.class || actual == Short.class ||
380 |  |
                     actual == Byte.class))
381 |  |
                     return true;
382 |  |
                 if(formal == Long.TYPE &&
383 |  |
                    (actual == Long.class || actual == Integer.class ||
384 |  |
                     actual == Short.class || actual == Byte.class))
385 |  |
                     return true;
386 |  |
                 if(formal == Float.TYPE &&
387 |  |
                    (actual == Float.class || actual == Long.class ||
388 |  |
                     actual == Integer.class || actual == Short.class ||
389 |  |
                     actual == Byte.class))
390 |  |
                     return true;
391 |  |
                 if(formal == Double.TYPE &&
392 |  |
                    (actual == Double.class || actual == Float.class ||
393 |  |
                     actual == Long.class || actual == Integer.class ||
394 |  |
                     actual == Short.class || actual == Byte.class))
395 |  |
                     return true;
396 |  |
             }
397 |  |
398 |  |
             return false;
399 |  |
         }
400 |  |
401 |  |
         /**
402 |  |
          * Determines whether a type represented by a class object is
403 |  |
          * convertible to another type represented by a class object using a
404 |  |
          * method invocation conversion, without matching object and primitive
405 |  |
          * types. This method is used to determine the more specific type when
406 |  |
          * comparing signatures of methods.
407 |  |
          *
408 |  |
          * @param formal the formal parameter type to which the actual
409 |  |
          * parameter type should be convertible
410 |  |
          * @param actual the actual parameter type.
411 |  |
          * @return true if either formal type is assignable from actual type,
412 |  |
          * or formal and actual are both primitive types and actual can be
413 |  |
          * subject to widening conversion to formal.
414 |  |
          */
415 |  |
         private static boolean isStrictMethodInvocationConvertible(Class formal,
416 |  |
                                                                    Class actual)
417 |  |
         {
418 |  |
             /*
419 |  |
              * we shouldn't get a null into, but if so
420 |  |
              */
421 |  |
             if (actual == null && !formal.isPrimitive())
422 |  |
             {
423 |  |
                 return true;
424 |  |
             }
425 |  |
426 |  |
             /*
427 |  |
              *  Check for identity or widening reference conversion
428 |  |
              */
429 |  |
430 |  |
             if(formal.isAssignableFrom(actual))
431 |  |
             {
432 |  |
                 return true;
433 |  |
             }
434 |  |
435 |  |
             /*
436 |  |
              *  Check for widening primitive conversion.
437 |  |
              */
438 |  |
439 |  |
             if(formal.isPrimitive())
440 |  |
             {
441 |  |
                 if(formal == Short.TYPE && (actual == Byte.TYPE))
442 |  |
                     return true;
443 |  |
                 if(formal == Integer.TYPE &&
444 |  |
                    (actual == Short.TYPE || actual == Byte.TYPE))
445 |  |
                     return true;
446 |  |
                 if(formal == Long.TYPE &&
447 |  |
                    (actual == Integer.TYPE || actual == Short.TYPE ||
448 |  |
                     actual == Byte.TYPE))
449 |  |
                     return true;
450 |  |
                 if(formal == Float.TYPE &&
451 |  |
                    (actual == Long.TYPE || actual == Integer.TYPE ||
452 |  |
                     actual == Short.TYPE || actual == Byte.TYPE))
453 |  |
                     return true;
454 |  |
                 if(formal == Double.TYPE &&
455 |  |
                    (actual == Float.TYPE || actual == Long.TYPE ||
456 |  |
                     actual == Integer.TYPE || actual == Short.TYPE ||
457 |  |
                     actual == Byte.TYPE))
458 |  |
                     return true;
459 |  |
             }
460 |  |
             return false;
461 |  |
         }
462 |  |
     }

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