1
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
48 class ELUtil {
49
50
53 private ELUtil() {
54 }
55
56
60 public static ExpressionFactory exprFactory = ExpressionFactory.newInstance();
61
62
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
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
95 private static void setCurrentInstance(Map<String, ResourceBundle> context) {
96 instance.set(context);
97 }
98
99
107 public static String getExceptionMessageString(ELContext context, String messageId) {
108 return getExceptionMessageString(context, messageId, null);
109 }
110
111
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
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
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
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
288 if (!(paramCount == mParamCount || (w.isVarArgs() && paramCount >= mParamCount - 1))) {
289
290 continue;
291 }
292
293
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
302 if (mParamCount == paramCount) {
303 if (mParamTypes[i] == paramTypes[i]) {
304 continue;
305 }
306 }
307
308
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
346
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
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
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
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
437
438
439
440 result = Boolean.compare(wrapper1.isBridge(), wrapper2.isBridge());
441 }
442
443 return result;
444 }
445
446
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
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
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
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
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
554 static boolean isAssignableFrom(Class<?> src, Class<?> target) {
555
556
557
558 if (src == null) {
559 return true;
560 }
561
562 target = getBoxingTypeIfPrimitive(target);
563
564 return target.isAssignableFrom(src);
565 }
566
567
570 private static boolean isCoercibleFrom(Object src, Class<?> target) {
571
572
573 try {
574 getExpressionFactory().coerceToType(src, target);
575 } catch (Exception e) {
576 return false;
577 }
578
579 return true;
580 }
581
582
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
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
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
637 }
638 }
639 return null;
640 }
641
642
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
660 }
661 }
662 return null;
663 }
664
665
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
677 for (int i = 0; (i < varArgIndex && i < paramCount); i++) {
678 parameters[i] = context.convertToType(params[i], parameterTypes[i]);
679 }
680
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
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
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
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