1 /*
2  * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
3  * All rights reserved.
4  * Copyright 2004 The Apache Software Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 package javax.el;
20
21 import java.lang.reflect.Array;
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26 import java.text.MessageFormat;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.Map;
33 import java.util.MissingResourceException;
34 import java.util.ResourceBundle;
35
36 /**
37  * Utility methods for this portion of the Jakarta Expression Language implementation
38  *
39  * <p>
40  * Methods on this class use a Map instance stored in ThreadLocal storage to minimize the performance impact on
41  * operations that take place multiple times on a single Thread. The keys and values of the Map are implementation
42  * private.
43  *
44  * @author edburns
45  * @author Kin-man Chung
46  * @author Dongbin Nie
47  */

48 class ELUtil {
49
50     /**
51      * This class may not be constructed.
52      */

53     private ELUtil() {
54     }
55
56     /*
57      * For testing Backward Compatibility option static java.util.Properties properties = new java.util.Properties(); static
58      * { properties.setProperty("javax.el.bc2.2""true"); }
59      */

60     public static ExpressionFactory exprFactory = ExpressionFactory.newInstance(/* properties */);
61
62     /**
63      * <p>
64      * The <code>ThreadLocal</code> variable used to record the <code>javax.faces.context.FacesContext</code> instance for
65      * each processing thread.
66      * </p>
67      */

68     private static ThreadLocal<Map<String, ResourceBundle>> instance = new ThreadLocal<Map<String, ResourceBundle>>() {
69         @Override
70         protected Map<String, ResourceBundle> initialValue() {
71             return (null);
72         }
73     };
74
75     /**
76      * @return a Map stored in ThreadLocal storage. This may be used by methods of this class to minimize the performance
77      * impact for operations that may take place multiple times on a given Thread instance.
78      */

79     private static Map<String, ResourceBundle> getCurrentInstance() {
80         Map<String, ResourceBundle> result = instance.get();
81         if (result == null) {
82             result = new HashMap<>();
83             setCurrentInstance(result);
84         }
85
86         return result;
87
88     }
89
90     /**
91      * Replace the Map with the argument context.
92      *
93      * @param context the Map to be stored in ThreadLocal storage.
94      */

95     private static void setCurrentInstance(Map<String, ResourceBundle> context) {
96         instance.set(context);
97     }
98
99     /**
100      * Convenience method, calls through to getExceptionMessageString(javax.el.ELContext,java.lang.String,Object []).
101      *
102      * @param context the ELContext from which the Locale for this message is extracted.
103      * @param messageId the messageId String in the ResourceBundle
104      *
105      * @return a localized String for the argument messageId
106      */

107     public static String getExceptionMessageString(ELContext context, String messageId) {
108         return getExceptionMessageString(context, messageId, null);
109     }
110
111     /*
112      * <p>Return a Localized message String suitable for use as an Exception message. Examine the argument
113      * <code>context</code> for a <code>Locale</code>. If not present, use <code>Locale.getDefault()</code>. Load the
114      * <code>ResourceBundle</code> "javax.el.Messages" using that locale. Get the message string for argument
115      * <code>messageId</code>. If not found return "Missing Resource in Jakarta Expression Language implementation ??? messageId ???" with messageId
116      * substituted with the runtime value of argument <code>messageId</code>. If found, and argument <code>params</code> is
117      * non-null, format the message using the params. If formatting fails, return a sensible message including the
118      * <code>messageId</code>. If argument <code>params</code> is <code>null</code>, skip formatting and return the message
119      * directly, otherwise return the formatted message.</p>
120      *
121      * @param context the ELContext from which the Locale for this message is extracted.
122      *
123      * @param messageId the messageId String in the ResourceBundle
124      *
125      * @param params parameters to the message
126      *
127      * @return a localized String for the argument messageId
128      */

129     public static String getExceptionMessageString(ELContext context, String messageId, Object[] params) {
130         String result = "";
131         Locale locale = null;
132
133         if (null == context || null == messageId) {
134             return result;
135         }
136
137         if (null == (locale = context.getLocale())) {
138             locale = Locale.getDefault();
139         }
140
141         if (locale != null) {
142             Map<String, ResourceBundle> threadMap = getCurrentInstance();
143             ResourceBundle resourceBundle = null;
144             if (null == (resourceBundle = threadMap.get(locale.toString()))) {
145                 resourceBundle = ResourceBundle.getBundle("javax.el.PrivateMessages", locale);
146                 threadMap.put(locale.toString(), resourceBundle);
147             }
148
149             if (null != resourceBundle) {
150                 try {
151                     result = resourceBundle.getString(messageId);
152                     if (null != params) {
153                         result = MessageFormat.format(result, params);
154                     }
155                 } catch (IllegalArgumentException iae) {
156                     result = "Can't get localized message: parameters to message appear to be incorrect.  Message to format: " + messageId;
157                 } catch (MissingResourceException mre) {
158                     result = "Missing Resource in Jakarta Expression Language implementation: ???" + messageId + "???";
159                 } catch (Exception e) {
160                     result = "Exception resolving message in Jakarta Expression Language implementation: ???" + messageId + "???";
161                 }
162             }
163         }
164
165         return result;
166     }
167
168     static ExpressionFactory getExpressionFactory() {
169         return exprFactory;
170     }
171
172     static Constructor<?> findConstructor(Class<?> klass, Class<?>[] paramTypes, Object[] params) {
173         String methodName = "<init>";
174
175         if (klass == null) {
176             throw new MethodNotFoundException("Method not found: " + klass + "." + methodName + "(" + paramString(paramTypes) + ")");
177         }
178
179         if (paramTypes == null) {
180             paramTypes = getTypesFromValues(params);
181         }
182
183         Constructor<?>[] constructors = klass.getConstructors();
184
185         List<Wrapper> wrappers = Wrapper.wrap(constructors);
186
187         Wrapper result = findWrapper(klass, wrappers, methodName, paramTypes, params);
188
189         if (result == null) {
190             return null;
191         }
192
193         return getConstructor(klass, (Constructor<?>) result.unWrap());
194     }
195
196     static Object invokeConstructor(ELContext context, Constructor<?> constructor, Object[] params) {
197         Object[] parameters = buildParameters(context, constructor.getParameterTypes(), constructor.isVarArgs(), params);
198         try {
199             return constructor.newInstance(parameters);
200         } catch (IllegalAccessException iae) {
201             throw new ELException(iae);
202         } catch (IllegalArgumentException iae) {
203             throw new ELException(iae);
204         } catch (InvocationTargetException ite) {
205             throw new ELException(ite.getCause());
206         } catch (InstantiationException ie) {
207             throw new ELException(ie.getCause());
208         }
209     }
210
211     static Method findMethod(Class<?> klass, String methodName, Class<?>[] paramTypes, Object[] params, boolean staticOnly) {
212         Method method = findMethod(klass, methodName, paramTypes, params);
213         if (staticOnly && !Modifier.isStatic(method.getModifiers())) {
214             throw new MethodNotFoundException("Method " + methodName + "for class " + klass + " not found or accessible");
215         }
216
217         return method;
218     }
219
220     /*
221      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
222      */

223     static Object invokeMethod(ELContext context, Method method, Object base, Object[] params) {
224
225         Object[] parameters = buildParameters(context, method.getParameterTypes(), method.isVarArgs(), params);
226         try {
227             return method.invoke(base, parameters);
228         } catch (IllegalAccessException iae) {
229             throw new ELException(iae);
230         } catch (IllegalArgumentException iae) {
231             throw new ELException(iae);
232         } catch (InvocationTargetException ite) {
233             throw new ELException(ite.getCause());
234         }
235     }
236
237     /*
238      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
239      */

240     static Method findMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes, Object[] paramValues) {
241         if (clazz == null || methodName == null) {
242             throw new MethodNotFoundException("Method not found: " + clazz + "." + methodName + "(" + paramString(paramTypes) + ")");
243         }
244
245         if (paramTypes == null) {
246             paramTypes = getTypesFromValues(paramValues);
247         }
248
249         Method[] methods = clazz.getMethods();
250
251         List<Wrapper> wrappers = Wrapper.wrap(methods, methodName);
252
253         Wrapper result = findWrapper(clazz, wrappers, methodName, paramTypes, paramValues);
254
255         if (result == null) {
256             return null;
257         }
258
259         return getMethod(clazz, (Method) result.unWrap());
260     }
261
262     /*
263      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
264      */

265     @SuppressWarnings("null")
266     private static Wrapper findWrapper(Class<?> clazz, List<Wrapper> wrappers, String name, Class<?>[] paramTypes, Object[] paramValues) {
267         List<Wrapper> assignableCandidates = new ArrayList<>();
268         List<Wrapper> coercibleCandidates = new ArrayList<>();
269         List<Wrapper> varArgsCandidates = new ArrayList<>();
270
271         int paramCount;
272         if (paramTypes == null) {
273             paramCount = 0;
274         } else {
275             paramCount = paramTypes.length;
276         }
277
278         for (Wrapper w : wrappers) {
279             Class<?>[] mParamTypes = w.getParameterTypes();
280             int mParamCount;
281             if (mParamTypes == null) {
282                 mParamCount = 0;
283             } else {
284                 mParamCount = mParamTypes.length;
285             }
286
287             // Check the number of parameters
288             if (!(paramCount == mParamCount || (w.isVarArgs() && paramCount >= mParamCount - 1))) {
289                 // Method has wrong number of parameters
290                 continue;
291             }
292
293             // Check the parameters match
294             boolean assignable = false;
295             boolean coercible = false;
296             boolean varArgs = false;
297             boolean noMatch = false;
298             for (int i = 0; i < mParamCount; i++) {
299                 if (i == (mParamCount - 1) && w.isVarArgs()) {
300                     varArgs = true;
301                     // exact var array type match
302                     if (mParamCount == paramCount) {
303                         if (mParamTypes[i] == paramTypes[i]) {
304                             continue;
305                         }
306                     }
307
308                     // unwrap the array's component type
309                     Class<?> varType = mParamTypes[i].getComponentType();
310                     for (int j = i; j < paramCount; j++) {
311                         if (!isAssignableFrom(paramTypes[j], varType)
312                                 && !(paramValues != null && j < paramValues.length && isCoercibleFrom(paramValues[j], varType))) {
313                             noMatch = true;
314                             break;
315                         }
316                     }
317                 } else if (mParamTypes[i].equals(paramTypes[i])) {
318                 } else if (isAssignableFrom(paramTypes[i], mParamTypes[i])) {
319                     assignable = true;
320                 } else {
321                     if (paramValues == null || i >= paramValues.length) {
322                         noMatch = true;
323                         break;
324                     } else {
325                         if (isCoercibleFrom(paramValues[i], mParamTypes[i])) {
326                             coercible = true;
327                         } else {
328                             noMatch = true;
329                             break;
330                         }
331                     }
332                 }
333             }
334             if (noMatch) {
335                 continue;
336             }
337
338             if (varArgs) {
339                 varArgsCandidates.add(w);
340             } else if (coercible) {
341                 coercibleCandidates.add(w);
342             } else if (assignable) {
343                 assignableCandidates.add(w);
344             } else {
345                 // If a method is found where every parameter matches exactly,
346                 // return it
347                 return w;
348             }
349
350         }
351
352         String errorMsg = "Unable to find unambiguous method: " + clazz + "." + name + "(" + paramString(paramTypes) + ")";
353         if (!assignableCandidates.isEmpty()) {
354             return findMostSpecificWrapper(assignableCandidates, paramTypes, false, errorMsg);
355         } else if (!coercibleCandidates.isEmpty()) {
356             return findMostSpecificWrapper(coercibleCandidates, paramTypes, true, errorMsg);
357         } else if (!varArgsCandidates.isEmpty()) {
358             return findMostSpecificWrapper(varArgsCandidates, paramTypes, true, errorMsg);
359         } else {
360             throw new MethodNotFoundException("Method not found: " + clazz + "." + name + "(" + paramString(paramTypes) + ")");
361         }
362
363     }
364
365     /*
366      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
367      */

368     private static Wrapper findMostSpecificWrapper(List<Wrapper> candidates, Class<?>[] matchingTypes, boolean elSpecific, String errorMsg) {
369         List<Wrapper> ambiguouses = new ArrayList<>();
370         for (Wrapper candidate : candidates) {
371             boolean lessSpecific = false;
372
373             Iterator<Wrapper> it = ambiguouses.iterator();
374             while (it.hasNext()) {
375                 int result = isMoreSpecific(candidate, it.next(), matchingTypes, elSpecific);
376                 if (result == 1) {
377                     it.remove();
378                 } else if (result == -1) {
379                     lessSpecific = true;
380                 }
381             }
382
383             if (!lessSpecific) {
384                 ambiguouses.add(candidate);
385             }
386         }
387
388         if (ambiguouses.size() > 1) {
389             throw new MethodNotFoundException(errorMsg);
390         }
391
392         return ambiguouses.get(0);
393     }
394
395     /*
396      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
397      */

398     private static int isMoreSpecific(Wrapper wrapper1, Wrapper wrapper2, Class<?>[] matchingTypes, boolean elSpecific) {
399         Class<?>[] paramTypes1 = wrapper1.getParameterTypes();
400         Class<?>[] paramTypes2 = wrapper2.getParameterTypes();
401
402         if (wrapper1.isVarArgs()) {
403             // JLS8 15.12.2.5 Choosing the Most Specific Method
404             int length = Math.max(Math.max(paramTypes1.length, paramTypes2.length), matchingTypes.length);
405             paramTypes1 = getComparingParamTypesForVarArgsMethod(paramTypes1, length);
406             paramTypes2 = getComparingParamTypesForVarArgsMethod(paramTypes2, length);
407
408             if (length > matchingTypes.length) {
409                 Class<?>[] matchingTypes2 = new Class<?>[length];
410                 System.arraycopy(matchingTypes, 0, matchingTypes2, 0, matchingTypes.length);
411                 matchingTypes = matchingTypes2;
412             }
413         }
414
415         int result = 0;
416         for (int i = 0; i < paramTypes1.length; i++) {
417             if (paramTypes1[i] != paramTypes2[i]) {
418                 int r2 = isMoreSpecific(paramTypes1[i], paramTypes2[i], matchingTypes[i], elSpecific);
419                 if (r2 == 1) {
420                     if (result == -1) {
421                         return 0;
422                     }
423                     result = 1;
424                 } else if (r2 == -1) {
425                     if (result == 1) {
426                         return 0;
427                     }
428                     result = -1;
429                 } else {
430                     return 0;
431                 }
432             }
433         }
434
435         if (result == 0) {
436             // The nature of bridge methods is such that it actually
437             // doesn't matter which one we pick as long as we pick
438             // one. That said, pick the 'right' one (the non-bridge
439             // one) anyway.
440             result = Boolean.compare(wrapper1.isBridge(), wrapper2.isBridge());
441         }
442
443         return result;
444     }
445
446     /*
447      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
448      */

449     private static int isMoreSpecific(Class<?> type1, Class<?> type2, Class<?> matchingType, boolean elSpecific) {
450         type1 = getBoxingTypeIfPrimitive(type1);
451         type2 = getBoxingTypeIfPrimitive(type2);
452         if (type2.isAssignableFrom(type1)) {
453             return 1;
454         } else if (type1.isAssignableFrom(type2)) {
455             return -1;
456         } else {
457             if (elSpecific) {
458                 /*
459                  * Number will be treated as more specific
460                  *
461                  * ASTInteger only return Long or BigInteger, no Byte / Short / Integer. ASTFloatingPoint also.
462                  *
463                  */

464                 if (matchingType != null && Number.class.isAssignableFrom(matchingType)) {
465                     boolean b1 = Number.class.isAssignableFrom(type1) || type1.isPrimitive();
466                     boolean b2 = Number.class.isAssignableFrom(type2) || type2.isPrimitive();
467                     if (b1 && !b2) {
468                         return 1;
469                     } else if (b2 && !b1) {
470                         return -1;
471                     } else {
472                         return 0;
473                     }
474                 }
475
476                 return 0;
477             } else {
478                 return 0;
479             }
480         }
481     }
482
483     /*
484      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
485      */

486     private static Class<?> getBoxingTypeIfPrimitive(Class<?> clazz) {
487         if (clazz.isPrimitive()) {
488             if (clazz == Boolean.TYPE) {
489                 return Boolean.class;
490             }
491             if (clazz == Character.TYPE) {
492                 return Character.class;
493             }
494             if (clazz == Byte.TYPE) {
495                 return Byte.class;
496             }
497             if (clazz == Short.TYPE) {
498                 return Short.class;
499             }
500             if (clazz == Integer.TYPE) {
501                 return Integer.class;
502             }
503             if (clazz == Long.TYPE) {
504                 return Long.class;
505             }
506             if (clazz == Float.TYPE) {
507                 return Float.class;
508             }
509
510             return Double.class;
511         } else {
512             return clazz;
513         }
514     }
515
516     /*
517      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
518      */

519     private static Class<?>[] getComparingParamTypesForVarArgsMethod(Class<?>[] paramTypes, int length) {
520         Class<?>[] result = new Class<?>[length];
521         System.arraycopy(paramTypes, 0, result, 0, paramTypes.length - 1);
522         Class<?> type = paramTypes[paramTypes.length - 1].getComponentType();
523         for (int i = paramTypes.length - 1; i < length; i++) {
524             result[i] = type;
525         }
526
527         return result;
528     }
529
530     /*
531      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
532      */

533     private static final String paramString(Class<?>[] types) {
534         if (types != null) {
535             StringBuilder sb = new StringBuilder();
536             for (int i = 0; i < types.length; i++) {
537                 if (types[i] == null) {
538                     sb.append("null, ");
539                 } else {
540                     sb.append(types[i].getName()).append(", ");
541                 }
542             }
543             if (sb.length() > 2) {
544                 sb.setLength(sb.length() - 2);
545             }
546             return sb.toString();
547         }
548         return null;
549     }
550
551     /*
552      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
553      */

554     static boolean isAssignableFrom(Class<?> src, Class<?> target) {
555         // src will always be an object
556         // Short-cut. null is always assignable to an object and in Jakarta Expression Language null
557         // can always be coerced to a valid value for a primitive
558         if (src == null) {
559             return true;
560         }
561
562         target = getBoxingTypeIfPrimitive(target);
563
564         return target.isAssignableFrom(src);
565     }
566
567     /*
568      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
569      */

570     private static boolean isCoercibleFrom(Object src, Class<?> target) {
571         // TODO: This isn't pretty but it works. Significant refactoring would
572         // be required to avoid the exception.
573         try {
574             getExpressionFactory().coerceToType(src, target);
575         } catch (Exception e) {
576             return false;
577         }
578
579         return true;
580     }
581
582     /*
583      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
584      */

585     private static Class<?>[] getTypesFromValues(Object[] values) {
586         if (values == null) {
587             return null;
588         }
589
590         Class<?> result[] = new Class<?>[values.length];
591         for (int i = 0; i < values.length; i++) {
592             if (values[i] == null) {
593                 result[i] = null;
594             } else {
595                 result[i] = values[i].getClass();
596             }
597         }
598
599         return result;
600     }
601
602     /*
603      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
604      *
605      * Get a public method form a public class or interface of a given method. Note that if a PropertyDescriptor is obtained
606      * for a non-public class that implements a public interface, the read/write methods will be for the class, and
607      * therefore inaccessible. To correct this, a version of the same method must be found in a superclass or interface.
608      *
609      */

610     static Method getMethod(Class<?> type, Method m) {
611         if (m == null || Modifier.isPublic(type.getModifiers())) {
612             return m;
613         }
614         Class<?>[] inf = type.getInterfaces();
615         Method mp = null;
616         for (int i = 0; i < inf.length; i++) {
617             try {
618                 mp = inf[i].getMethod(m.getName(), m.getParameterTypes());
619                 mp = getMethod(mp.getDeclaringClass(), mp);
620                 if (mp != null) {
621                     return mp;
622                 }
623             } catch (NoSuchMethodException e) {
624                 // Ignore
625             }
626         }
627         Class<?> sup = type.getSuperclass();
628         if (sup != null) {
629             try {
630                 mp = sup.getMethod(m.getName(), m.getParameterTypes());
631                 mp = getMethod(mp.getDeclaringClass(), mp);
632                 if (mp != null) {
633                     return mp;
634                 }
635             } catch (NoSuchMethodException e) {
636                 // Ignore
637             }
638         }
639         return null;
640     }
641
642     /*
643      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
644      */

645     static Constructor<?> getConstructor(Class<?> type, Constructor<?> c) {
646         if (c == null || Modifier.isPublic(type.getModifiers())) {
647             return c;
648         }
649         Constructor<?> cp = null;
650         Class<?> sup = type.getSuperclass();
651         if (sup != null) {
652             try {
653                 cp = sup.getConstructor(c.getParameterTypes());
654                 cp = getConstructor(cp.getDeclaringClass(), cp);
655                 if (cp != null) {
656                     return cp;
657                 }
658             } catch (NoSuchMethodException e) {
659                 // Ignore
660             }
661         }
662         return null;
663     }
664
665     /*
666      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
667      */

668     @SuppressWarnings("null")
669     static Object[] buildParameters(ELContext context, Class<?>[] parameterTypes, boolean isVarArgs, Object[] params) {
670         Object[] parameters = null;
671         if (parameterTypes.length > 0) {
672             parameters = new Object[parameterTypes.length];
673             int paramCount = params == null ? 0 : params.length;
674             if (isVarArgs) {
675                 int varArgIndex = parameterTypes.length - 1;
676                 // First argCount-1 parameters are standard
677                 for (int i = 0; (i < varArgIndex && i < paramCount); i++) {
678                     parameters[i] = context.convertToType(params[i], parameterTypes[i]);
679                 }
680                 // Last parameter is the varargs
681                 if (parameterTypes.length == paramCount && parameterTypes[varArgIndex] == params[varArgIndex].getClass()) {
682                     parameters[varArgIndex] = params[varArgIndex];
683                 } else {
684                     Class<?> varArgClass = parameterTypes[varArgIndex].getComponentType();
685                     final Object varargs = Array.newInstance(varArgClass, (paramCount - varArgIndex));
686                     for (int i = (varArgIndex); i < paramCount; i++) {
687                         Array.set(varargs, i - varArgIndex, context.convertToType(params[i], varArgClass));
688                     }
689                     parameters[varArgIndex] = varargs;
690                 }
691             } else {
692                 for (int i = 0; i < parameterTypes.length && i < paramCount; i++) {
693                     parameters[i] = context.convertToType(params[i], parameterTypes[i]);
694                 }
695             }
696         }
697         return parameters;
698     }
699
700     /*
701      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
702      */

703     private abstract static class Wrapper {
704
705         public static List<Wrapper> wrap(Method[] methods, String name) {
706             List<Wrapper> result = new ArrayList<>();
707             for (Method method : methods) {
708                 if (method.getName().equals(name)) {
709                     result.add(new MethodWrapper(method));
710                 }
711             }
712             return result;
713         }
714
715         public static List<Wrapper> wrap(Constructor<?>[] constructors) {
716             List<Wrapper> result = new ArrayList<>();
717             for (Constructor<?> constructor : constructors) {
718                 result.add(new ConstructorWrapper(constructor));
719             }
720             return result;
721         }
722
723         public abstract Object unWrap();
724
725         public abstract Class<?>[] getParameterTypes();
726
727         public abstract boolean isVarArgs();
728
729         public abstract boolean isBridge();
730     }
731
732     /*
733      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
734      */

735     private static class MethodWrapper extends Wrapper {
736         private final Method m;
737
738         public MethodWrapper(Method m) {
739             this.m = m;
740         }
741
742         @Override
743         public Object unWrap() {
744             return m;
745         }
746
747         @Override
748         public Class<?>[] getParameterTypes() {
749             return m.getParameterTypes();
750         }
751
752         @Override
753         public boolean isVarArgs() {
754             return m.isVarArgs();
755         }
756
757         @Override
758         public boolean isBridge() {
759             return m.isBridge();
760         }
761     }
762
763     /*
764      * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
765      */

766     private static class ConstructorWrapper extends Wrapper {
767         private final Constructor<?> c;
768
769         public ConstructorWrapper(Constructor<?> c) {
770             this.c = c;
771         }
772
773         @Override
774         public Object unWrap() {
775             return c;
776         }
777
778         @Override
779         public Class<?>[] getParameterTypes() {
780             return c.getParameterTypes();
781         }
782
783         @Override
784         public boolean isVarArgs() {
785             return c.isVarArgs();
786         }
787
788         @Override
789         public boolean isBridge() {
790             return false;
791         }
792     }
793
794 }
795