PageRenderTime 117ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/apache-commons-ognl-3.0.2/src/main/java/org/apache/commons/ognl/OgnlRuntime.java

#
Java | 2782 lines | 1988 code | 399 blank | 395 comment | 588 complexity | 09fef185f02459fa6e4611a4ff7c2a06 MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * $Id$
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. */
  20. package org.apache.commons.ognl;
  21. import org.apache.commons.ognl.enhance.ExpressionCompiler;
  22. import org.apache.commons.ognl.enhance.OgnlExpressionCompiler;
  23. import org.apache.commons.ognl.internal.ClassCache;
  24. import org.apache.commons.ognl.internal.ClassCacheImpl;
  25. import java.beans.BeanInfo;
  26. import java.beans.IndexedPropertyDescriptor;
  27. import java.beans.IntrospectionException;
  28. import java.beans.Introspector;
  29. import java.beans.MethodDescriptor;
  30. import java.beans.PropertyDescriptor;
  31. import java.lang.reflect.AccessibleObject;
  32. import java.lang.reflect.Array;
  33. import java.lang.reflect.Constructor;
  34. import java.lang.reflect.Field;
  35. import java.lang.reflect.GenericArrayType;
  36. import java.lang.reflect.InvocationTargetException;
  37. import java.lang.reflect.Member;
  38. import java.lang.reflect.Method;
  39. import java.lang.reflect.Modifier;
  40. import java.lang.reflect.ParameterizedType;
  41. import java.lang.reflect.Proxy;
  42. import java.lang.reflect.Type;
  43. import java.lang.reflect.TypeVariable;
  44. import java.math.BigDecimal;
  45. import java.math.BigInteger;
  46. import java.security.Permission;
  47. import java.util.ArrayList;
  48. import java.util.Arrays;
  49. import java.util.Collection;
  50. import java.util.Collections;
  51. import java.util.Enumeration;
  52. import java.util.HashMap;
  53. import java.util.IdentityHashMap;
  54. import java.util.Iterator;
  55. import java.util.List;
  56. import java.util.Map;
  57. import java.util.Set;
  58. /**
  59. * Utility class used by internal OGNL API to do various things like:
  60. *
  61. * <ul>
  62. * <li>Handles majority of reflection logic / caching. </li>
  63. * <li>Utility methods for casting strings / various numeric types used by {@link OgnlExpressionCompiler}.</li.
  64. * <li>Core runtime configuration point for setting/using global {@link TypeConverter} / {@link OgnlExpressionCompiler} /
  65. * {@link NullHandler} instances / etc.. </li>
  66. *</ul>
  67. *
  68. * @author Luke Blanshard (blanshlu@netscape.net)
  69. * @author Drew Davidson (drew@ognl.org)
  70. */
  71. public class OgnlRuntime {
  72. /**
  73. * Constant expression used to indicate that a given method / property couldn't be found
  74. * during reflection operations.
  75. */
  76. public static final Object NotFound = new Object();
  77. public static final List NotFoundList = new ArrayList();
  78. public static final Map NotFoundMap = new HashMap();
  79. public static final Object[] NoArguments = new Object[]{};
  80. public static final Class[] NoArgumentTypes = new Class[]{};
  81. /**
  82. * Token returned by TypeConverter for no conversion possible
  83. */
  84. public static final Object NoConversionPossible = "ognl.NoConversionPossible";
  85. /**
  86. * Not an indexed property
  87. */
  88. public static int INDEXED_PROPERTY_NONE = 0;
  89. /**
  90. * JavaBeans IndexedProperty
  91. */
  92. public static int INDEXED_PROPERTY_INT = 1;
  93. /**
  94. * OGNL ObjectIndexedProperty
  95. */
  96. public static int INDEXED_PROPERTY_OBJECT = 2;
  97. /**
  98. * Constant string representation of null string.
  99. */
  100. public static final String NULL_STRING = "" + null;
  101. /**
  102. * Java beans standard set method prefix.
  103. */
  104. private static final String SET_PREFIX = "set";
  105. /**
  106. * Java beans standard get method prefix.
  107. */
  108. private static final String GET_PREFIX = "get";
  109. /**
  110. * Java beans standard is<Foo> boolean getter prefix.
  111. */
  112. private static final String IS_PREFIX = "is";
  113. /**
  114. * Prefix padding for hexadecimal numbers to HEX_LENGTH.
  115. */
  116. private static final Map HEX_PADDING = new HashMap();
  117. private static final int HEX_LENGTH = 8;
  118. /**
  119. * Returned by <CODE>getUniqueDescriptor()</CODE> when the object is <CODE>null</CODE>.
  120. */
  121. private static final String NULL_OBJECT_STRING = "<null>";
  122. /**
  123. * Used to store the result of determining if current jvm is 1.5 language compatible.
  124. */
  125. private static boolean _jdk15 = false;
  126. private static boolean _jdkChecked = false;
  127. static final ClassCache _methodAccessors = new ClassCacheImpl();
  128. static final ClassCache _propertyAccessors = new ClassCacheImpl();
  129. static final ClassCache _elementsAccessors = new ClassCacheImpl();
  130. static final ClassCache _nullHandlers = new ClassCacheImpl();
  131. static final ClassCache _propertyDescriptorCache = new ClassCacheImpl();
  132. static final ClassCache _constructorCache = new ClassCacheImpl();
  133. static final ClassCache _staticMethodCache = new ClassCacheImpl();
  134. static final ClassCache _instanceMethodCache = new ClassCacheImpl();
  135. static final ClassCache _invokePermissionCache = new ClassCacheImpl();
  136. static final ClassCache _fieldCache = new ClassCacheImpl();
  137. static final List _superclasses = new ArrayList(); /* Used by fieldCache lookup */
  138. static final ClassCache[] _declaredMethods = new ClassCache[]{new ClassCacheImpl(), new ClassCacheImpl()};
  139. static final Map _primitiveTypes = new HashMap(101);
  140. static final ClassCache _primitiveDefaults = new ClassCacheImpl();
  141. static final Map _methodParameterTypesCache = new HashMap(101);
  142. static final Map _genericMethodParameterTypesCache = new HashMap(101);
  143. static final Map _ctorParameterTypesCache = new HashMap(101);
  144. static SecurityManager _securityManager = System.getSecurityManager();
  145. static final EvaluationPool _evaluationPool = new EvaluationPool();
  146. static final ObjectArrayPool _objectArrayPool = new ObjectArrayPool();
  147. static final IntHashMap _methodAccessCache = new IntHashMap();
  148. static final IntHashMap _methodPermCache = new IntHashMap();
  149. static ClassCacheInspector _cacheInspector;
  150. /**
  151. * Expression compiler used by {@link Ognl#compileExpression(OgnlContext, Object, String)} calls.
  152. */
  153. private static OgnlExpressionCompiler _compiler;
  154. /**
  155. * Lazy loading of Javassist library
  156. */
  157. static {
  158. try {
  159. Class.forName("javassist.ClassPool");
  160. _compiler = new ExpressionCompiler();
  161. } catch (ClassNotFoundException e) {
  162. throw new IllegalArgumentException("Javassist library is missing in classpath! Please add missed dependency!",e);
  163. }
  164. }
  165. private static IdentityHashMap PRIMITIVE_WRAPPER_CLASSES = new IdentityHashMap();
  166. /**
  167. * Used to provide primitive type equivalent conversions into and out of
  168. * native / object types.
  169. */
  170. static {
  171. PRIMITIVE_WRAPPER_CLASSES.put(Boolean.TYPE, Boolean.class);
  172. PRIMITIVE_WRAPPER_CLASSES.put(Boolean.class, Boolean.TYPE);
  173. PRIMITIVE_WRAPPER_CLASSES.put(Byte.TYPE, Byte.class);
  174. PRIMITIVE_WRAPPER_CLASSES.put(Byte.class, Byte.TYPE);
  175. PRIMITIVE_WRAPPER_CLASSES.put(Character.TYPE, Character.class);
  176. PRIMITIVE_WRAPPER_CLASSES.put(Character.class, Character.TYPE);
  177. PRIMITIVE_WRAPPER_CLASSES.put(Short.TYPE, Short.class);
  178. PRIMITIVE_WRAPPER_CLASSES.put(Short.class, Short.TYPE);
  179. PRIMITIVE_WRAPPER_CLASSES.put(Integer.TYPE, Integer.class);
  180. PRIMITIVE_WRAPPER_CLASSES.put(Integer.class, Integer.TYPE);
  181. PRIMITIVE_WRAPPER_CLASSES.put(Long.TYPE, Long.class);
  182. PRIMITIVE_WRAPPER_CLASSES.put(Long.class, Long.TYPE);
  183. PRIMITIVE_WRAPPER_CLASSES.put(Float.TYPE, Float.class);
  184. PRIMITIVE_WRAPPER_CLASSES.put(Float.class, Float.TYPE);
  185. PRIMITIVE_WRAPPER_CLASSES.put(Double.TYPE, Double.class);
  186. PRIMITIVE_WRAPPER_CLASSES.put(Double.class, Double.TYPE);
  187. }
  188. private static final Map NUMERIC_CASTS = new HashMap();
  189. /**
  190. * Constant strings for casting different primitive types.
  191. */
  192. static {
  193. NUMERIC_CASTS.put(Double.class, "(double)");
  194. NUMERIC_CASTS.put(Float.class, "(float)");
  195. NUMERIC_CASTS.put(Integer.class, "(int)");
  196. NUMERIC_CASTS.put(Long.class, "(long)");
  197. NUMERIC_CASTS.put(BigDecimal.class, "(double)");
  198. NUMERIC_CASTS.put(BigInteger.class, "");
  199. }
  200. private static final Map NUMERIC_VALUES = new HashMap();
  201. /**
  202. * Constant strings for getting the primitive value of different
  203. * native types on the generic {@link Number} object interface. (or the less
  204. * generic BigDecimal/BigInteger types)
  205. */
  206. static {
  207. NUMERIC_VALUES.put(Double.class, "doubleValue()");
  208. NUMERIC_VALUES.put(Float.class, "floatValue()");
  209. NUMERIC_VALUES.put(Integer.class, "intValue()");
  210. NUMERIC_VALUES.put(Long.class, "longValue()");
  211. NUMERIC_VALUES.put(Short.class, "shortValue()");
  212. NUMERIC_VALUES.put(Byte.class, "byteValue()");
  213. NUMERIC_VALUES.put(BigDecimal.class, "doubleValue()");
  214. NUMERIC_VALUES.put(BigInteger.class, "doubleValue()");
  215. NUMERIC_VALUES.put(Boolean.class, "booleanValue()");
  216. }
  217. private static final Map NUMERIC_LITERALS = new HashMap();
  218. /**
  219. * Numeric primitive literal string expressions.
  220. */
  221. static {
  222. NUMERIC_LITERALS.put(Integer.class, "");
  223. NUMERIC_LITERALS.put(Integer.TYPE, "");
  224. NUMERIC_LITERALS.put(Long.class, "l");
  225. NUMERIC_LITERALS.put(Long.TYPE, "l");
  226. NUMERIC_LITERALS.put(BigInteger.class, "d");
  227. NUMERIC_LITERALS.put(Float.class, "f");
  228. NUMERIC_LITERALS.put(Float.TYPE, "f");
  229. NUMERIC_LITERALS.put(Double.class, "d");
  230. NUMERIC_LITERALS.put(Double.TYPE, "d");
  231. NUMERIC_LITERALS.put(BigInteger.class, "d");
  232. NUMERIC_LITERALS.put(BigDecimal.class, "d");
  233. }
  234. private static final Map NUMERIC_DEFAULTS = new HashMap();
  235. static {
  236. NUMERIC_DEFAULTS.put(Boolean.class, Boolean.FALSE);
  237. NUMERIC_DEFAULTS.put(Byte.class, new Byte((byte) 0));
  238. NUMERIC_DEFAULTS.put(Short.class, new Short((short) 0));
  239. NUMERIC_DEFAULTS.put(Character.class, new Character((char) 0));
  240. NUMERIC_DEFAULTS.put(Integer.class, new Integer(0));
  241. NUMERIC_DEFAULTS.put(Long.class, new Long(0L));
  242. NUMERIC_DEFAULTS.put(Float.class, new Float(0.0f));
  243. NUMERIC_DEFAULTS.put(Double.class, new Double(0.0));
  244. NUMERIC_DEFAULTS.put(BigInteger.class, new BigInteger("0"));
  245. NUMERIC_DEFAULTS.put(BigDecimal.class, new BigDecimal(0.0));
  246. }
  247. static {
  248. PropertyAccessor p = new ArrayPropertyAccessor();
  249. setPropertyAccessor(Object.class, new ObjectPropertyAccessor());
  250. setPropertyAccessor(byte[].class, p);
  251. setPropertyAccessor(short[].class, p);
  252. setPropertyAccessor(char[].class, p);
  253. setPropertyAccessor(int[].class, p);
  254. setPropertyAccessor(long[].class, p);
  255. setPropertyAccessor(float[].class, p);
  256. setPropertyAccessor(double[].class, p);
  257. setPropertyAccessor(Object[].class, p);
  258. setPropertyAccessor(List.class, new ListPropertyAccessor());
  259. setPropertyAccessor(Map.class, new MapPropertyAccessor());
  260. setPropertyAccessor(Set.class, new SetPropertyAccessor());
  261. setPropertyAccessor(Iterator.class, new IteratorPropertyAccessor());
  262. setPropertyAccessor(Enumeration.class, new EnumerationPropertyAccessor());
  263. ElementsAccessor e = new ArrayElementsAccessor();
  264. setElementsAccessor(Object.class, new ObjectElementsAccessor());
  265. setElementsAccessor(byte[].class, e);
  266. setElementsAccessor(short[].class, e);
  267. setElementsAccessor(char[].class, e);
  268. setElementsAccessor(int[].class, e);
  269. setElementsAccessor(long[].class, e);
  270. setElementsAccessor(float[].class, e);
  271. setElementsAccessor(double[].class, e);
  272. setElementsAccessor(Object[].class, e);
  273. setElementsAccessor(Collection.class, new CollectionElementsAccessor());
  274. setElementsAccessor(Map.class, new MapElementsAccessor());
  275. setElementsAccessor(Iterator.class, new IteratorElementsAccessor());
  276. setElementsAccessor(Enumeration.class, new EnumerationElementsAccessor());
  277. setElementsAccessor(Number.class, new NumberElementsAccessor());
  278. NullHandler nh = new ObjectNullHandler();
  279. setNullHandler(Object.class, nh);
  280. setNullHandler(byte[].class, nh);
  281. setNullHandler(short[].class, nh);
  282. setNullHandler(char[].class, nh);
  283. setNullHandler(int[].class, nh);
  284. setNullHandler(long[].class, nh);
  285. setNullHandler(float[].class, nh);
  286. setNullHandler(double[].class, nh);
  287. setNullHandler(Object[].class, nh);
  288. MethodAccessor ma = new ObjectMethodAccessor();
  289. setMethodAccessor(Object.class, ma);
  290. setMethodAccessor(byte[].class, ma);
  291. setMethodAccessor(short[].class, ma);
  292. setMethodAccessor(char[].class, ma);
  293. setMethodAccessor(int[].class, ma);
  294. setMethodAccessor(long[].class, ma);
  295. setMethodAccessor(float[].class, ma);
  296. setMethodAccessor(double[].class, ma);
  297. setMethodAccessor(Object[].class, ma);
  298. _primitiveTypes.put("boolean", Boolean.TYPE);
  299. _primitiveTypes.put("byte", Byte.TYPE);
  300. _primitiveTypes.put("short", Short.TYPE);
  301. _primitiveTypes.put("char", Character.TYPE);
  302. _primitiveTypes.put("int", Integer.TYPE);
  303. _primitiveTypes.put("long", Long.TYPE);
  304. _primitiveTypes.put("float", Float.TYPE);
  305. _primitiveTypes.put("double", Double.TYPE);
  306. _primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
  307. _primitiveDefaults.put(Boolean.class, Boolean.FALSE);
  308. _primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0));
  309. _primitiveDefaults.put(Byte.class, new Byte((byte) 0));
  310. _primitiveDefaults.put(Short.TYPE, new Short((short) 0));
  311. _primitiveDefaults.put(Short.class, new Short((short) 0));
  312. _primitiveDefaults.put(Character.TYPE, new Character((char) 0));
  313. _primitiveDefaults.put(Integer.TYPE, new Integer(0));
  314. _primitiveDefaults.put(Long.TYPE, new Long(0L));
  315. _primitiveDefaults.put(Float.TYPE, new Float(0.0f));
  316. _primitiveDefaults.put(Double.TYPE, new Double(0.0));
  317. _primitiveDefaults.put(BigInteger.class, new BigInteger("0"));
  318. _primitiveDefaults.put(BigDecimal.class, new BigDecimal(0.0));
  319. }
  320. /**
  321. * Clears all of the cached reflection information normally used
  322. * to improve the speed of expressions that operate on the same classes
  323. * or are executed multiple times.
  324. *
  325. * <p>
  326. * <strong>Warning:</strong> Calling this too often can be a huge performance
  327. * drain on your expressions - use with care.
  328. * </p>
  329. */
  330. public static void clearCache()
  331. {
  332. _methodParameterTypesCache.clear();
  333. _ctorParameterTypesCache.clear();
  334. _propertyDescriptorCache.clear();
  335. _constructorCache.clear();
  336. _staticMethodCache.clear();
  337. _instanceMethodCache.clear();
  338. _invokePermissionCache.clear();
  339. _fieldCache.clear();
  340. _superclasses.clear();
  341. _declaredMethods[0].clear();
  342. _declaredMethods[1].clear();
  343. _methodAccessCache.clear();
  344. }
  345. /**
  346. * Checks if the current jvm is java language >= 1.5 compatible.
  347. *
  348. * @return True if jdk15 features are present.
  349. */
  350. public static boolean isJdk15()
  351. {
  352. if (_jdkChecked)
  353. return _jdk15;
  354. try
  355. {
  356. Class.forName("java.lang.annotation.Annotation");
  357. _jdk15 = true;
  358. } catch (Exception e) { /* ignore */ }
  359. _jdkChecked = true;
  360. return _jdk15;
  361. }
  362. public static String getNumericValueGetter(Class type)
  363. {
  364. return (String) NUMERIC_VALUES.get(type);
  365. }
  366. public static Class getPrimitiveWrapperClass(Class primitiveClass)
  367. {
  368. return (Class) PRIMITIVE_WRAPPER_CLASSES.get(primitiveClass);
  369. }
  370. public static String getNumericCast(Class type)
  371. {
  372. return (String) NUMERIC_CASTS.get(type);
  373. }
  374. public static String getNumericLiteral(Class type)
  375. {
  376. return (String) NUMERIC_LITERALS.get(type);
  377. }
  378. public static void setCompiler(OgnlExpressionCompiler compiler)
  379. {
  380. _compiler = compiler;
  381. }
  382. public static OgnlExpressionCompiler getCompiler()
  383. {
  384. return _compiler;
  385. }
  386. public static void compileExpression(OgnlContext context, Node expression, Object root)
  387. throws Exception
  388. {
  389. _compiler.compileExpression(context, expression, root);
  390. }
  391. /**
  392. * Gets the "target" class of an object for looking up accessors that are registered on the
  393. * target. If the object is a Class object this will return the Class itself, else it will
  394. * return object's getClass() result.
  395. */
  396. public static Class getTargetClass(Object o)
  397. {
  398. return (o == null) ? null : ((o instanceof Class) ? (Class) o : o.getClass());
  399. }
  400. /**
  401. * Returns the base name (the class name without the package name prepended) of the object
  402. * given.
  403. */
  404. public static String getBaseName(Object o)
  405. {
  406. return (o == null) ? null : getClassBaseName(o.getClass());
  407. }
  408. /**
  409. * Returns the base name (the class name without the package name prepended) of the class given.
  410. */
  411. public static String getClassBaseName(Class c)
  412. {
  413. String s = c.getName();
  414. return s.substring(s.lastIndexOf('.') + 1);
  415. }
  416. public static String getClassName(Object o, boolean fullyQualified)
  417. {
  418. if (!(o instanceof Class))
  419. {
  420. o = o.getClass();
  421. }
  422. return getClassName((Class) o, fullyQualified);
  423. }
  424. public static String getClassName(Class c, boolean fullyQualified)
  425. {
  426. return fullyQualified ? c.getName() : getClassBaseName(c);
  427. }
  428. /**
  429. * Returns the package name of the object's class.
  430. */
  431. public static String getPackageName(Object o)
  432. {
  433. return (o == null) ? null : getClassPackageName(o.getClass());
  434. }
  435. /**
  436. * Returns the package name of the class given.
  437. */
  438. public static String getClassPackageName(Class c)
  439. {
  440. String s = c.getName();
  441. int i = s.lastIndexOf('.');
  442. return (i < 0) ? null : s.substring(0, i);
  443. }
  444. /**
  445. * Returns a "pointer" string in the usual format for these things - 0x<hex digits>.
  446. */
  447. public static String getPointerString(int num)
  448. {
  449. StringBuffer result = new StringBuffer();
  450. String hex = Integer.toHexString(num), pad;
  451. Integer l = new Integer(hex.length());
  452. // result.append(HEX_PREFIX);
  453. if ((pad = (String) HEX_PADDING.get(l)) == null) {
  454. StringBuffer pb = new StringBuffer();
  455. for (int i = hex.length(); i < HEX_LENGTH; i++) {
  456. pb.append('0');
  457. }
  458. pad = new String(pb);
  459. HEX_PADDING.put(l, pad);
  460. }
  461. result.append(pad);
  462. result.append(hex);
  463. return new String(result);
  464. }
  465. /**
  466. * Returns a "pointer" string in the usual format for these things - 0x<hex digits> for the
  467. * object given. This will always return a unique value for each object.
  468. */
  469. public static String getPointerString(Object o)
  470. {
  471. return getPointerString((o == null) ? 0 : System.identityHashCode(o));
  472. }
  473. /**
  474. * Returns a unique descriptor string that includes the object's class and a unique integer
  475. * identifier. If fullyQualified is true then the class name will be fully qualified to include
  476. * the package name, else it will be just the class' base name.
  477. */
  478. public static String getUniqueDescriptor(Object object, boolean fullyQualified)
  479. {
  480. StringBuffer result = new StringBuffer();
  481. if (object != null) {
  482. if (object instanceof Proxy) {
  483. Class interfaceClass = object.getClass().getInterfaces()[0];
  484. result.append(getClassName(interfaceClass, fullyQualified));
  485. result.append('^');
  486. object = Proxy.getInvocationHandler(object);
  487. }
  488. result.append(getClassName(object, fullyQualified));
  489. result.append('@');
  490. result.append(getPointerString(object));
  491. } else {
  492. result.append(NULL_OBJECT_STRING);
  493. }
  494. return new String(result);
  495. }
  496. /**
  497. * Returns a unique descriptor string that includes the object's class' base name and a unique
  498. * integer identifier.
  499. */
  500. public static String getUniqueDescriptor(Object object)
  501. {
  502. return getUniqueDescriptor(object, false);
  503. }
  504. /**
  505. * Utility to convert a List into an Object[] array. If the list is zero elements this will
  506. * return a constant array; toArray() on List always returns a new object and this is wasteful
  507. * for our purposes.
  508. */
  509. public static Object[] toArray(List list)
  510. {
  511. Object[] result;
  512. int size = list.size();
  513. if (size == 0) {
  514. result = NoArguments;
  515. } else {
  516. result = getObjectArrayPool().create(list.size());
  517. for (int i = 0; i < size; i++) {
  518. result[i] = list.get(i);
  519. }
  520. }
  521. return result;
  522. }
  523. /**
  524. * Returns the parameter types of the given method.
  525. */
  526. public static Class[] getParameterTypes(Method m)
  527. {
  528. synchronized (_methodParameterTypesCache)
  529. {
  530. Class[] result;
  531. if ((result = (Class[]) _methodParameterTypesCache.get(m)) == null)
  532. {
  533. _methodParameterTypesCache.put(m, result = m.getParameterTypes());
  534. }
  535. return result;
  536. }
  537. }
  538. /**
  539. * Finds the appropriate parameter types for the given {@link Method} and
  540. * {@link Class} instance of the type the method is associated with. Correctly
  541. * finds generic types if running in >= 1.5 jre as well.
  542. *
  543. * @param type The class type the method is being executed against.
  544. * @param m The method to find types for.
  545. * @return Array of parameter types for the given method.
  546. */
  547. public static Class[] findParameterTypes(Class type, Method m)
  548. {
  549. if (type == null)
  550. {
  551. return getParameterTypes(m);
  552. }
  553. if (!isJdk15()
  554. || type.getGenericSuperclass() == null
  555. || !ParameterizedType.class.isInstance(type.getGenericSuperclass())
  556. || m.getDeclaringClass().getTypeParameters() == null)
  557. {
  558. return getParameterTypes(m);
  559. }
  560. synchronized (_genericMethodParameterTypesCache)
  561. {
  562. Class[] types;
  563. if ((types = (Class[]) _genericMethodParameterTypesCache.get(m)) != null)
  564. {
  565. ParameterizedType genericSuperclass = (ParameterizedType) type.getGenericSuperclass();
  566. if (Arrays.equals(types, genericSuperclass.getActualTypeArguments())) {
  567. return types;
  568. }
  569. }
  570. ParameterizedType param = (ParameterizedType)type.getGenericSuperclass();
  571. Type[] genTypes = m.getGenericParameterTypes();
  572. TypeVariable[] declaredTypes = m.getDeclaringClass().getTypeParameters();
  573. types = new Class[genTypes.length];
  574. typeSearch:
  575. for (int i=0; i < genTypes.length; i++)
  576. {
  577. TypeVariable paramType = null;
  578. if (TypeVariable.class.isInstance(genTypes[i]))
  579. {
  580. paramType = (TypeVariable)genTypes[i];
  581. } else if (GenericArrayType.class.isInstance(genTypes[i]))
  582. {
  583. paramType = (TypeVariable) ((GenericArrayType)genTypes[i]).getGenericComponentType();
  584. }
  585. else if (ParameterizedType.class.isInstance(genTypes[i]))
  586. {
  587. types[i] = (Class) ((ParameterizedType) genTypes[i]).getRawType();
  588. continue;
  589. } else if (Class.class.isInstance(genTypes[i]))
  590. {
  591. types[i] = (Class) genTypes[i];
  592. continue;
  593. }
  594. Class resolved = resolveType(param, paramType, declaredTypes);
  595. if (resolved != null)
  596. {
  597. if (GenericArrayType.class.isInstance(genTypes[i]))
  598. {
  599. resolved = Array.newInstance(resolved, 0).getClass();
  600. }
  601. types[i] = resolved;
  602. continue;
  603. }
  604. types[i] = m.getParameterTypes()[i];
  605. }
  606. _genericMethodParameterTypesCache.put(m, types);
  607. return types;
  608. }
  609. }
  610. static Class resolveType(ParameterizedType param, TypeVariable var, TypeVariable[] declaredTypes)
  611. {
  612. if (param.getActualTypeArguments().length < 1)
  613. return null;
  614. for (int i=0; i < declaredTypes.length; i++)
  615. {
  616. if (!TypeVariable.class.isInstance( param.getActualTypeArguments()[i])
  617. && declaredTypes[i].getName().equals(var.getName()))
  618. {
  619. return (Class) param.getActualTypeArguments()[i];
  620. }
  621. }
  622. /*
  623. for (int i=0; i < var.getBounds().length; i++)
  624. {
  625. Type t = var.getBounds()[i];
  626. Class resolvedType = null;
  627. if (ParameterizedType.class.isInstance(t))
  628. {
  629. ParameterizedType pparam = (ParameterizedType)t;
  630. for (int e=0; e < pparam.getActualTypeArguments().length; e++)
  631. {
  632. if (!TypeVariable.class.isInstance(pparam.getActualTypeArguments()[e]))
  633. continue;
  634. resolvedType = resolveType(pparam, (TypeVariable)pparam.getActualTypeArguments()[e], declaredTypes);
  635. }
  636. } else
  637. {
  638. resolvedType = findType(param.getActualTypeArguments(), (Class)t);
  639. }
  640. if (resolvedType != null)
  641. return resolvedType;
  642. }
  643. */
  644. return null;
  645. }
  646. static Class findType(Type[] types, Class type)
  647. {
  648. for (int i = 0; i < types.length; i++)
  649. {
  650. if (Class.class.isInstance(types[i]) && type.isAssignableFrom((Class)types[i]))
  651. return (Class)types[i];
  652. }
  653. return null;
  654. }
  655. /**
  656. * Returns the parameter types of the given method.
  657. */
  658. public static Class[] getParameterTypes(Constructor c)
  659. {
  660. synchronized (_ctorParameterTypesCache) {
  661. Class[] result;
  662. if ((result = (Class[]) _ctorParameterTypesCache.get(c)) == null) {
  663. _ctorParameterTypesCache.put(c, result = c.getParameterTypes());
  664. }
  665. return result;
  666. }
  667. }
  668. /**
  669. * Gets the SecurityManager that OGNL uses to determine permissions for invoking methods.
  670. *
  671. * @return SecurityManager for OGNL
  672. */
  673. public static SecurityManager getSecurityManager()
  674. {
  675. return _securityManager;
  676. }
  677. /**
  678. * Sets the SecurityManager that OGNL uses to determine permissions for invoking methods.
  679. *
  680. * @param value SecurityManager to set
  681. */
  682. public static void setSecurityManager(SecurityManager value)
  683. {
  684. _securityManager = value;
  685. }
  686. /**
  687. * Permission will be named "invoke.<declaring-class>.<method-name>".
  688. */
  689. public static Permission getPermission(Method method)
  690. {
  691. Permission result = null;
  692. Class mc = method.getDeclaringClass();
  693. synchronized (_invokePermissionCache) {
  694. Map permissions = (Map) _invokePermissionCache.get(mc);
  695. if (permissions == null) {
  696. _invokePermissionCache.put(mc, permissions = new HashMap(101));
  697. }
  698. if ((result = (Permission) permissions.get(method.getName())) == null) {
  699. result = new OgnlInvokePermission("invoke." + mc.getName() + "." + method.getName());
  700. permissions.put(method.getName(), result);
  701. }
  702. }
  703. return result;
  704. }
  705. public static Object invokeMethod(Object target, Method method, Object[] argsArray)
  706. throws InvocationTargetException, IllegalAccessException
  707. {
  708. boolean syncInvoke = false;
  709. boolean checkPermission = false;
  710. int mHash = method.hashCode();
  711. // only synchronize method invocation if it actually requires it
  712. synchronized(method) {
  713. if (_methodAccessCache.get(mHash) == null
  714. || _methodAccessCache.get(mHash) == Boolean.TRUE) {
  715. syncInvoke = true;
  716. }
  717. if (_securityManager != null && _methodPermCache.get(mHash) == null
  718. || _methodPermCache.get(mHash) == Boolean.FALSE) {
  719. checkPermission = true;
  720. }
  721. }
  722. Object result;
  723. boolean wasAccessible = true;
  724. if (syncInvoke)
  725. {
  726. synchronized(method)
  727. {
  728. if (checkPermission)
  729. {
  730. try
  731. {
  732. _securityManager.checkPermission(getPermission(method));
  733. _methodPermCache.put(mHash, Boolean.TRUE);
  734. } catch (SecurityException ex) {
  735. _methodPermCache.put(mHash, Boolean.FALSE);
  736. throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
  737. }
  738. }
  739. if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
  740. {
  741. if (!(wasAccessible = ((AccessibleObject) method).isAccessible()))
  742. {
  743. ((AccessibleObject) method).setAccessible(true);
  744. _methodAccessCache.put(mHash, Boolean.TRUE);
  745. } else
  746. {
  747. _methodAccessCache.put(mHash, Boolean.FALSE);
  748. }
  749. } else
  750. {
  751. _methodAccessCache.put(mHash, Boolean.FALSE);
  752. }
  753. result = method.invoke(target, argsArray);
  754. if (!wasAccessible)
  755. {
  756. ((AccessibleObject) method).setAccessible(false);
  757. }
  758. }
  759. } else
  760. {
  761. if (checkPermission)
  762. {
  763. try
  764. {
  765. _securityManager.checkPermission(getPermission(method));
  766. _methodPermCache.put(mHash, Boolean.TRUE);
  767. } catch (SecurityException ex) {
  768. _methodPermCache.put(mHash, Boolean.FALSE);
  769. throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
  770. }
  771. }
  772. result = method.invoke(target, argsArray);
  773. }
  774. return result;
  775. }
  776. /**
  777. * Gets the class for a method argument that is appropriate for looking up methods by
  778. * reflection, by looking for the standard primitive wrapper classes and exchanging for them
  779. * their underlying primitive class objects. Other classes are passed through unchanged.
  780. *
  781. * @param arg an object that is being passed to a method
  782. * @return the class to use to look up the method
  783. */
  784. public static final Class getArgClass(Object arg)
  785. {
  786. if (arg == null)
  787. return null;
  788. Class c = arg.getClass();
  789. if (c == Boolean.class)
  790. return Boolean.TYPE;
  791. else if (c.getSuperclass() == Number.class) {
  792. if (c == Integer.class)
  793. return Integer.TYPE;
  794. if (c == Double.class)
  795. return Double.TYPE;
  796. if (c == Byte.class)
  797. return Byte.TYPE;
  798. if (c == Long.class)
  799. return Long.TYPE;
  800. if (c == Float.class)
  801. return Float.TYPE;
  802. if (c == Short.class)
  803. return Short.TYPE;
  804. } else if (c == Character.class)
  805. return Character.TYPE;
  806. return c;
  807. }
  808. /**
  809. * Tells whether the given object is compatible with the given class ---that is, whether the
  810. * given object can be passed as an argument to a method or constructor whose parameter type is
  811. * the given class. If object is null this will return true because null is compatible with any
  812. * type.
  813. */
  814. public static final boolean isTypeCompatible(Object object, Class c)
  815. {
  816. boolean result = true;
  817. if (object != null) {
  818. if (c.isPrimitive()) {
  819. if (getArgClass(object) != c) {
  820. result = false;
  821. }
  822. } else if (!c.isInstance(object)) {
  823. result = false;
  824. }
  825. }
  826. return result;
  827. }
  828. /**
  829. * Tells whether the given array of objects is compatible with the given array of classes---that
  830. * is, whether the given array of objects can be passed as arguments to a method or constructor
  831. * whose parameter types are the given array of classes.
  832. */
  833. public static boolean areArgsCompatible(Object[] args, Class[] classes)
  834. {
  835. return areArgsCompatible(args, classes, null);
  836. }
  837. public static boolean areArgsCompatible(Object[] args, Class[] classes, Method m)
  838. {
  839. boolean result = true;
  840. boolean varArgs = m != null && isJdk15() && m.isVarArgs();
  841. if (args.length != classes.length && !varArgs) {
  842. result = false;
  843. } else if (varArgs) {
  844. for (int index = 0, count = args.length; result && (index < count); ++index) {
  845. if (index >= classes.length){
  846. break;
  847. }
  848. result = isTypeCompatible(args[index], classes[index]);
  849. if (!result && classes[index].isArray()) {
  850. result = isTypeCompatible(args[index], classes[index].getComponentType());
  851. }
  852. }
  853. } else {
  854. for (int index = 0, count = args.length; result && (index < count); ++index) {
  855. result = isTypeCompatible(args[index], classes[index]);
  856. }
  857. }
  858. return result;
  859. }
  860. /**
  861. * Tells whether the first array of classes is more specific than the second. Assumes that the
  862. * two arrays are of the same length.
  863. */
  864. public static final boolean isMoreSpecific(Class[] classes1, Class[] classes2)
  865. {
  866. for (int index = 0, count = classes1.length; index < count; ++index) {
  867. Class c1 = classes1[index], c2 = classes2[index];
  868. if (c1 == c2)
  869. continue;
  870. else if (c1.isPrimitive())
  871. return true;
  872. else if (c1.isAssignableFrom(c2))
  873. return false;
  874. else if (c2.isAssignableFrom(c1))
  875. return true;
  876. }
  877. // They are the same! So the first is not more specific than the second.
  878. return false;
  879. }
  880. public static String getModifierString(int modifiers)
  881. {
  882. String result;
  883. if (Modifier.isPublic(modifiers))
  884. result = "public";
  885. else if (Modifier.isProtected(modifiers))
  886. result = "protected";
  887. else if (Modifier.isPrivate(modifiers))
  888. result = "private";
  889. else
  890. result = "";
  891. if (Modifier.isStatic(modifiers))
  892. result = "static " + result;
  893. if (Modifier.isFinal(modifiers))
  894. result = "final " + result;
  895. if (Modifier.isNative(modifiers))
  896. result = "native " + result;
  897. if (Modifier.isSynchronized(modifiers))
  898. result = "synchronized " + result;
  899. if (Modifier.isTransient(modifiers))
  900. result = "transient " + result;
  901. return result;
  902. }
  903. public static Class classForName(OgnlContext context, String className)
  904. throws ClassNotFoundException
  905. {
  906. Class result = (Class) _primitiveTypes.get(className);
  907. if (result == null) {
  908. ClassResolver resolver;
  909. if ((context == null) || ((resolver = context.getClassResolver()) == null)) {
  910. resolver = OgnlContext.DEFAULT_CLASS_RESOLVER;
  911. }
  912. result = resolver.classForName(className, context);
  913. }
  914. if (result == null)
  915. throw new ClassNotFoundException("Unable to resolve class: " + className);
  916. return result;
  917. }
  918. public static boolean isInstance(OgnlContext context, Object value, String className)
  919. throws OgnlException
  920. {
  921. try {
  922. Class c = classForName(context, className);
  923. return c.isInstance(value);
  924. } catch (ClassNotFoundException e) {
  925. throw new OgnlException("No such class: " + className, e);
  926. }
  927. }
  928. public static Object getPrimitiveDefaultValue(Class forClass)
  929. {
  930. return _primitiveDefaults.get(forClass);
  931. }
  932. public static Object getNumericDefaultValue(Class forClass)
  933. {
  934. return NUMERIC_DEFAULTS.get(forClass);
  935. }
  936. public static Object getConvertedType(OgnlContext context, Object target, Member member, String propertyName,
  937. Object value, Class type)
  938. {
  939. return context.getTypeConverter().convertValue(context, target, member, propertyName, value, type);
  940. }
  941. public static boolean getConvertedTypes(OgnlContext context, Object target, Member member, String propertyName,
  942. Class[] parameterTypes, Object[] args, Object[] newArgs)
  943. {
  944. boolean result = false;
  945. if (parameterTypes.length == args.length) {
  946. result = true;
  947. for (int i = 0, ilast = parameterTypes.length - 1; result && (i <= ilast); i++) {
  948. Object arg = args[i];
  949. Class type = parameterTypes[i];
  950. if (isTypeCompatible(arg, type)) {
  951. newArgs[i] = arg;
  952. } else {
  953. Object v = getConvertedType(context, target, member, propertyName, arg, type);
  954. if (v == OgnlRuntime.NoConversionPossible) {
  955. result = false;
  956. } else {
  957. newArgs[i] = v;
  958. }
  959. }
  960. }
  961. }
  962. return result;
  963. }
  964. public static Method getConvertedMethodAndArgs(OgnlContext context, Object target, String propertyName,
  965. List methods, Object[] args, Object[] newArgs)
  966. {
  967. Method result = null;
  968. TypeConverter converter = context.getTypeConverter();
  969. if ((converter != null) && (methods != null))
  970. {
  971. for (int i = 0, icount = methods.size(); (result == null) && (i < icount); i++)
  972. {
  973. Method m = (Method) methods.get(i);
  974. Class[] parameterTypes = findParameterTypes(target != null ? target.getClass() : null, m);//getParameterTypes(m);
  975. if (getConvertedTypes(context, target, m, propertyName, parameterTypes, args, newArgs))
  976. {
  977. result = m;
  978. }
  979. }
  980. }
  981. return result;
  982. }
  983. public static Constructor getConvertedConstructorAndArgs(OgnlContext context, Object target, List constructors,
  984. Object[] args, Object[] newArgs)
  985. {
  986. Constructor result = null;
  987. TypeConverter converter = context.getTypeConverter();
  988. if ((converter != null) && (constructors != null)) {
  989. for (int i = 0, icount = constructors.size(); (result == null) && (i < icount); i++) {
  990. Constructor ctor = (Constructor) constructors.get(i);
  991. Class[] parameterTypes = getParameterTypes(ctor);
  992. if (getConvertedTypes(context, target, ctor, null, parameterTypes, args, newArgs)) {
  993. result = ctor;
  994. }
  995. }
  996. }
  997. return result;
  998. }
  999. /**
  1000. * Gets the appropriate method to be called for the given target, method name and arguments. If
  1001. * successful this method will return the Method within the target that can be called and the
  1002. * converted arguments in actualArgs. If unsuccessful this method will return null and the
  1003. * actualArgs will be empty.
  1004. *
  1005. * @param context The current execution context.
  1006. * @param source Target object to run against or method name.
  1007. * @param target Instance of object to be run against.
  1008. * @param propertyName Name of property to get method of.
  1009. * @param methods List of current known methods.
  1010. * @param args Arguments originally passed in.
  1011. * @param actualArgs Converted arguments.
  1012. *
  1013. * @return Best method match or null if none could be found.
  1014. */
  1015. public static Method getAppropriateMethod(OgnlContext context, Object source, Object target, String propertyName,
  1016. List methods, Object[] args, Object[] actualArgs)
  1017. {
  1018. Method result = null;
  1019. Class[] resultParameterTypes = null;
  1020. if (methods != null)
  1021. {
  1022. for (int i = 0, icount = methods.size(); i < icount; i++)
  1023. {
  1024. Method m = (Method) methods.get(i);
  1025. Class typeClass = target != null ? target.getClass() : null;
  1026. if (typeClass == null && source != null && Class.class.isInstance(source))
  1027. {
  1028. typeClass = (Class)source;
  1029. }
  1030. Class[] mParameterTypes = findParameterTypes(typeClass, m);
  1031. if (areArgsCompatible(args, mParameterTypes, m)
  1032. && ((result == null) || isMoreSpecific(mParameterTypes, resultParameterTypes)))
  1033. {
  1034. result = m;
  1035. resultParameterTypes = mParameterTypes;
  1036. System.arraycopy(args, 0, actualArgs, 0, args.length);
  1037. for (int j = 0; j < mParameterTypes.length; j++)
  1038. {
  1039. Class type = mParameterTypes[j];
  1040. if (type.isPrimitive() && (actualArgs[j] == null))
  1041. {
  1042. actualArgs[j] = getConvertedType(context, source, result, propertyName, null, type);
  1043. }
  1044. }
  1045. }
  1046. }
  1047. }
  1048. if (result == null)
  1049. {
  1050. result = getConvertedMethodAndArgs(context, target, propertyName, methods, args, actualArgs);
  1051. }
  1052. return result;
  1053. }
  1054. public static Object callAppropriateMethod(OgnlContext context, Object source, Object target, String methodName,
  1055. String propertyName, List methods, Object[] args)
  1056. throws MethodFailedException
  1057. {
  1058. Throwable reason = null;
  1059. Object[] actualArgs = _objectArrayPool.create(args.length);
  1060. try {
  1061. Method method = getAppropriateMethod(context, source, target, propertyName, methods, args, actualArgs);
  1062. if ((method == null) || !isMethodAccessible(context, source, method, propertyName))
  1063. {
  1064. StringBuffer buffer = new StringBuffer();
  1065. String className = "";
  1066. if (target != null)
  1067. {
  1068. className = target.getClass().getName() + ".";
  1069. }
  1070. for (int i = 0, ilast = args.length - 1; i <= ilast; i++)
  1071. {
  1072. Object arg = args[i];
  1073. buffer.append((arg == null) ? NULL_STRING : arg.getClass().getName());
  1074. if (i < ilast)
  1075. {
  1076. buffer.append(", ");
  1077. }
  1078. }
  1079. throw new NoSuchMethodException(className + methodName + "(" + buffer + ")");
  1080. }
  1081. Object[] convertedArgs = actualArgs;
  1082. if (isJdk15() && method.isVarArgs())
  1083. {
  1084. Class[] parmTypes = method.getParameterTypes();
  1085. // split arguments in to two dimensional array for varargs reflection invocation
  1086. // where it is expected that the parameter passed in to invoke the method
  1087. // will look like "new Object[] { arrayOfNonVarArgsArguments, arrayOfVarArgsArguments }"
  1088. for (int i=0; i < parmTypes.length; i++)
  1089. {
  1090. if (parmTypes[i].isArray())
  1091. {
  1092. convertedArgs = new Object[i + 1];
  1093. System.arraycopy(actualArgs, 0, convertedArgs, 0, convertedArgs.length);
  1094. Object[] varArgs;
  1095. // if they passed in varargs arguments grab them and dump in to new varargs array
  1096. if (actualArgs.length > i)
  1097. {
  1098. ArrayList varArgsList = new ArrayList();
  1099. for (int j=i; j < actualArgs.length; j++)
  1100. {
  1101. if (actualArgs[j] != null)
  1102. {
  1103. varArgsList.add(actualArgs[j]);
  1104. }
  1105. }
  1106. varArgs = varArgsList.toArray();
  1107. } else
  1108. {
  1109. varArgs = new Object[0];
  1110. }
  1111. convertedArgs[i] = varArgs;
  1112. break;
  1113. }
  1114. }
  1115. }
  1116. return invokeMethod(target, method, convertedArgs);
  1117. } catch (NoSuchMethodException e) {
  1118. reason = e;
  1119. } catch (IllegalAccessException e) {
  1120. reason = e;
  1121. } catch (InvocationTargetException e) {
  1122. reason = e.getTargetException();
  1123. } finally {
  1124. _objectArrayPool.recycle(actualArgs);
  1125. }
  1126. throw new MethodFailedException(source, methodName, reason);
  1127. }
  1128. public static Object callStaticMethod(OgnlContext context, String className, String methodName, Object[] args)
  1129. throws OgnlException
  1130. {
  1131. try {
  1132. Class targetClass = classForName(context, className);
  1133. if (targetClass == null)
  1134. throw new ClassNotFoundException("Unable to resolve class with name " + className);
  1135. MethodAccessor ma = getMethodAccessor(targetClass);
  1136. return ma.callStaticMethod(context, targetClass, methodName, args);
  1137. } catch (ClassNotFoundException ex) {
  1138. throw new MethodFailedException(className, methodName, ex);
  1139. }
  1140. }
  1141. /**
  1142. * Invokes the specified method against the target object.
  1143. *
  1144. * @param context
  1145. * The current execution context.
  1146. * @param target
  1147. * The object to invoke the method on.
  1148. * @param methodName
  1149. * Name of the method - as in "getValue" or "add", etc..
  1150. * @param propertyName
  1151. * Name of the property to call instead?
  1152. * @param args
  1153. * Optional arguments needed for method.
  1154. * @return Result of invoking method.
  1155. *
  1156. * @deprecated Use {@link #callMethod(OgnlContext, Object, String, Object[])} instead.
  1157. * @throws OgnlException For lots of different reasons.
  1158. */
  1159. public static Object callMethod(OgnlContext context, Object target, String methodName, String propertyName, Object[] args)
  1160. throws OgnlException
  1161. {
  1162. return callMethod(context, target, methodName == null ? propertyName : methodName, args);
  1163. }
  1164. /**
  1165. * Invokes the specified method against the target object.
  1166. *
  1167. * @param context
  1168. * The current execution context.
  1169. * @param target
  1170. * The object to invoke the method on.
  1171. * @param methodName
  1172. * Name of the method - as in "getValue" or "add", etc..
  1173. * @param args
  1174. * Optional arguments needed for method.
  1175. * @return Result of invoking method.
  1176. *
  1177. * @throws OgnlException For lots of different reasons.
  1178. */
  1179. public static Object callMethod(OgnlContext context, Object target, String methodName, Object[] args)
  1180. throws OgnlException
  1181. {
  1182. if (target == null)
  1183. throw new NullPointerException("target is null for method " + methodName);
  1184. return getMethodAccessor(target.getClass()).callMethod(context, target, methodName, args);
  1185. }
  1186. public static Object callConstructor(OgnlContext context, String className, Object[] args)
  1187. throws OgnlException
  1188. {
  1189. Throwable reason = null;
  1190. Object[] actualArgs = args;
  1191. try {
  1192. Constructor ctor = null;
  1193. Class[] ctorParameterTypes = null;
  1194. Class target = classForName(context, className);
  1195. List constructors = getConstructors(target);
  1196. for (int i = 0, icount = constructors.size(); i < icount; i++) {
  1197. Constructor c = (Constructor) constructors.get(i);
  1198. Class[] cParameterTypes = getParameterTypes(c);
  1199. if (areArgsCompatible(args, cParameterTypes)
  1200. && (ctor == null || isMoreSpecific(cParameterTypes, ctorParameterTypes))) {
  1201. ctor = c;
  1202. ctorParameterTypes = cParameterTypes;
  1203. }
  1204. }
  1205. if (ctor == null) {
  1206. actualArgs = _objectArrayPool.create(args.length);
  1207. if ((ctor = getConvertedConstructorAndArgs(context, target, constructors, args, actualArgs)) == null) {
  1208. throw new NoSuchMethodException();
  1209. }
  1210. }
  1211. if (!context.getMemberAccess().isAccessible(context, target, ctor, null)) {
  1212. throw new IllegalAccessException(
  1213. "access denied to " + target.getName() + "()");
  1214. }
  1215. return ctor.newInstance(actualArgs);
  1216. } catch (ClassNotFoundException e) {
  1217. reason = e;
  1218. } catch (NoSuchMethodException e) {
  1219. reason = e;
  1220. } catch (IllegalAccessException e) {
  1221. reason = e;
  1222. } catch (InvocationTargetException e) {
  1223. reason = e.getTargetException();
  1224. } catch (InstantiationException e) {
  1225. reason = e;
  1226. } finally {
  1227. if (actualArgs != args) {
  1228. _objectArrayPool.recycle(actualArgs);
  1229. }
  1230. }
  1231. throw new MethodFailedException(className, "new", reason);
  1232. }
  1233. public static final Object getMethodValue(OgnlContext context, Object target, String propertyName)
  1234. throws OgnlException, IllegalAccessException, NoSuchMethodException, IntrospectionException
  1235. {
  1236. return getMethodValue(context, target, propertyName, false);
  1237. }
  1238. /**
  1239. * If the checkAccessAndExistence flag is true this method will check to see if the method
  1240. * exists and if it is accessible according to the context's MemberAccess. If neither test
  1241. * passes this will return NotFound.
  1242. */
  1243. public static final Object getMethodValue(OgnlContext context, Object target, String propertyName,
  1244. boolean checkAccessAndExistence)
  1245. throws OgnlException, IllegalAccessException, NoSuchMethodException, IntrospectionException
  1246. {
  1247. Object result = null;
  1248. Method m = getGetMethod(context, (target == null) ? null : target.getClass() , propertyName);
  1249. if (m == null)
  1250. m = getReadMethod((target == null) ? null : target.getClass(), propertyName, 0);
  1251. if (checkAccessAndExistence)
  1252. {
  1253. if ((m == null) || !context.getMemberAccess().isAccessible(context, target, m, propertyName))
  1254. {
  1255. result = NotFound;
  1256. }
  1257. }
  1258. if (result == null) {
  1259. if (m != null) {
  1260. try {
  1261. result = invokeMethod(target, m, NoArguments);
  1262. } catch (InvocationTargetException ex) {
  1263. throw new OgnlException(propertyName, ex.getTargetException());
  1264. }
  1265. } else {
  1266. throw new NoSuchMethodException(propertyName);
  1267. }
  1268. }
  1269. return result;
  1270. }
  1271. public static boolean setMethodValue(OgnlContext context, Object target, String propertyName, Object value)
  1272. throws OgnlException, IllegalAccessException, NoSuchMethodException, IntrospectionException
  1273. {
  1274. return setMethodValue(context, target, propertyName, value, false);
  1275. }
  1276. public static boolean setMethodValue(OgnlContext context, Object target, String propertyName, Object value,
  1277. boolean checkAccessAndExistence)
  1278. throws OgnlException, IllegalAccessException, NoSuchMethodException, IntrospectionException
  1279. {
  1280. boolean result = true;
  1281. Method m = getSetMethod(context, (target == null) ? null : target.getClass(), propertyName);
  1282. if (checkAccessAndExistence)
  1283. {
  1284. if ((m == null) || !context.getMemberAccess().isAccessible(context, target, m, propertyName))
  1285. {
  1286. result = false;
  1287. }
  1288. }
  1289. if (result)
  1290. {
  1291. if (m != null)
  1292. {
  1293. Object[] args = _objectArrayPool.create(value);
  1294. try
  1295. {
  1296. callAppropriateMethod(context, target, target, m.getName(), propertyName,
  1297. Collections.nCopies(1, m), args);
  1298. } finally
  1299. {
  1300. _objectArrayPool.recycle(args);
  1301. }
  1302. } else
  1303. {
  1304. result = false;
  1305. }
  1306. }
  1307. return result;
  1308. }
  1309. public static List getConstructors(Class targetClass)
  1310. {
  1311. List result;
  1312. synchronized (_constructorCache) {
  1313. if ((result = (List) _constructorCache.get(targetClass)) == null) {
  1314. _constructorCache.put(targetClass, result = Arrays.asList(targetClass.getConstructors()));
  1315. }
  1316. }
  1317. return result;
  1318. }
  1319. public static Map getMethods(Class targetClass, boolean staticMethods)
  1320. {
  1321. ClassCache cache = (staticMethods ? _staticMethodCache : _instanceMethodCache);
  1322. Map result;
  1323. synchronized (cache)
  1324. {
  1325. if ((result = (Map) cache.get(targetClass)) == null)
  1326. {
  1327. cache.put(targetClass, result = new HashMap(23));
  1328. for (Class c = targetClass; c != null; c = c.getSuperclass())
  1329. {
  1330. Method[] ma = c.getDeclaredMethods();
  1331. for (int i = 0, icount = ma.length; i < icount; i++)
  1332. {
  1333. // skip over synthetic methods
  1334. if (!isMethodCallable(ma[i]))
  1335. continue;
  1336. if (Modifier.isStatic(ma[i].getModifiers()) == staticMethods)
  1337. {
  1338. List ml = (List) result.get(ma[i].getName());
  1339. if (ml == null)
  1340. result.put(ma[i].getName(), ml = new ArrayList());
  1341. ml.add(ma[i]);
  1342. }
  1343. }
  1344. }
  1345. }
  1346. }
  1347. return result;
  1348. }
  1349. public static List getMethods(Class targetClass, String name, boolean staticMethods)
  1350. {
  1351. return (List) getMethods(targetClass, staticMethods).get(name);
  1352. }
  1353. public static Map getFields(Class targetClass)
  1354. {
  1355. Map result;
  1356. synchronized (_fieldCache) {
  1357. if ((result = (Map) _fieldCache.get(targetClass)) == null) {
  1358. Field fa[];
  1359. result = new HashMap(23);
  1360. fa = targetClass.getDeclaredFields();
  1361. for (int i = 0; i < fa.length; i++) {
  1362. result.put(fa[i].getName(), fa[i]);
  1363. }
  1364. _fieldCache.put(targetClass, result);
  1365. }
  1366. }
  1367. return result;
  1368. }
  1369. public static Field getField(Class inClass, String name)
  1370. {
  1371. Field result = null;
  1372. synchronized (_fieldCache) {
  1373. Object o = getFields(inClass).get(name);
  1374. if (o == null) {
  1375. _superclasses.clear();
  1376. for (Class sc = inClass; (sc != null); sc = sc.getSuperclass()) {
  1377. if ((o = getFields(sc).get(name)) == NotFound)
  1378. break;
  1379. _superclasses.add(sc);
  1380. if ((result = (Field) o) != null)
  1381. break;
  1382. }
  1383. /*
  1384. * Bubble the found value (either cache miss or actual field) to all supeclasses
  1385. * that we saw for quicker access next time.
  1386. */
  1387. for (int i = 0, icount = _superclasses.size(); i < icount; i++) {
  1388. getFields((Class) _superclasses.get(i)).put(name, (result == null) ? NotFound : result);
  1389. }
  1390. } else {
  1391. if (o instanceof Field) {
  1392. result = (Field) o;
  1393. } else {
  1394. if (result == NotFound)
  1395. result = null;
  1396. }
  1397. }
  1398. }
  1399. return result;
  1400. }
  1401. public static Object getFieldValue(OgnlContext context, Object target, String propertyName)
  1402. throws NoSuchFieldException
  1403. {
  1404. return getFieldValue(context, target, propertyName, false);
  1405. }
  1406. public static Object getFieldValue(OgnlContext context, Object target, String propertyName,
  1407. boolean checkAccessAndExistence)
  1408. throws NoSuchFieldException
  1409. {
  1410. Object result = null;
  1411. Field f = getField((target == null) ? null : target.getClass(), propertyName);
  1412. if (checkAccessAndExistence) {
  1413. if ((f == null) || !context.getMemberAccess().isAccessible(context, target, f, propertyName)) {
  1414. result = NotFound;
  1415. }
  1416. }
  1417. if (result == null) {
  1418. if (f == null) {
  1419. throw new NoSuchFieldException(propertyName);
  1420. } else {
  1421. try {
  1422. Object state = null;
  1423. if (!Modifier.isStatic(f.getModifiers())) {
  1424. state = context.getMemberAccess().setup(context, target, f, propertyName);
  1425. result = f.get(target);
  1426. context.getMemberAccess().restore(context, target, f, propertyName, state);
  1427. } else
  1428. throw new NoSuchFieldException(propertyName);
  1429. } catch (IllegalAccessException ex) {
  1430. throw new NoSuchFieldException(propertyName);
  1431. }
  1432. }
  1433. }
  1434. return result;
  1435. }
  1436. public static boolean setFieldValue(OgnlContext context, Object target, String propertyName, Object value)
  1437. throws OgnlException
  1438. {
  1439. boolean result = false;
  1440. try {
  1441. Field f = getField((target == null) ? null : target.getClass(), propertyName);
  1442. Object state;
  1443. if ((f != null) && !Modifier.isStatic(f.getModifiers())) {
  1444. state = context.getMemberAccess().setup(context, target, f, propertyName);
  1445. try {
  1446. if (isTypeCompatible(value, f.getType())
  1447. || ((value = getConvertedType(context, target, f, propertyName, value, f.getType())) != null)) {
  1448. f.set(target, value);
  1449. result = true;
  1450. }
  1451. } finally {
  1452. context.getMemberAccess().restore(context, target, f, propertyName, state);
  1453. }
  1454. }
  1455. } catch (IllegalAccessException ex) {
  1456. throw new NoSuchPropertyException(target, propertyName, ex);
  1457. }
  1458. return result;
  1459. }
  1460. public static boolean isFieldAccessible(OgnlContext context, Object target, Class inClass, String propertyName)
  1461. {
  1462. return isFieldAccessible(context, target, getField(inClass, propertyName), propertyName);
  1463. }
  1464. public static boolean isFieldAccessible(OgnlContext context, Object target, Field field, String propertyName)
  1465. {
  1466. return context.getMemberAccess().isAccessible(context, target, field, propertyName);
  1467. }
  1468. public static boolean hasField(OgnlContext context, Object target, Class inClass, String propertyName)
  1469. {
  1470. Field f = getField(inClass, propertyName);
  1471. return (f != null) && isFieldAccessible(context, target, f, propertyName);
  1472. }
  1473. public static Object getStaticField(OgnlContext context, String className, String fieldName)
  1474. throws OgnlException
  1475. {
  1476. Exception reason = null;
  1477. try {
  1478. Class c = classForName(context, className);
  1479. if (c == null)
  1480. throw new OgnlException("Unable to find class " + className + " when resolving field name of " + fieldName);
  1481. /*
  1482. * Check for virtual static field "class"; this cannot interfere with normal static
  1483. * fields because it is a reserved word.
  1484. */
  1485. if (fieldName.equals("class"))
  1486. {
  1487. return c;
  1488. } else if (OgnlRuntime.isJdk15() && c.isEnum())
  1489. {
  1490. return Enum.valueOf(c, fieldName);
  1491. } else
  1492. {
  1493. Field f = c.getField(fieldName);
  1494. if (!Modifier.isStatic(f.getModifiers()))
  1495. throw new OgnlException("Field " + fieldName + " of class " + className + " is not static");
  1496. return f.get(null);
  1497. }
  1498. } catch (ClassNotFoundException e) {
  1499. reason = e;
  1500. } catch (NoSuchFieldException e) {
  1501. reason = e;
  1502. } catch (SecurityException e) {
  1503. reason = e;
  1504. } catch (IllegalAccessException e) {
  1505. reason = e;
  1506. }
  1507. throw new OgnlException("Could not get static field " + fieldName + " from class " + className, reason);
  1508. }
  1509. public static List getDeclaredMethods(Class targetClass, String propertyName, boolean findSets)
  1510. {
  1511. List result = null;
  1512. ClassCache cache = _declaredMethods[findSets ? 0 : 1];
  1513. synchronized (cache) {
  1514. Map propertyCache = (Map) cache.get(targetClass);
  1515. if ((propertyCache == null) || ((result = (List) propertyCache.get(propertyName)) == null)) {
  1516. String baseName = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
  1517. for (Class c = targetClass; c != null; c = c.getSuperclass()) {
  1518. Method[] methods = c.getDeclaredMethods();
  1519. for (int i = 0; i < methods.length; i++) {
  1520. if (!isMethodCallable(methods[i]))
  1521. continue;
  1522. String ms = methods[i].getName();
  1523. if (ms.endsWith(baseName)) {
  1524. boolean isSet = false, isIs = false;
  1525. if ((isSet = ms.startsWith(SET_PREFIX)) || ms.startsWith(GET_PREFIX)
  1526. || (isIs = ms.startsWith(IS_PREFIX))) {
  1527. int prefixLength = (isIs ? 2 : 3);
  1528. if (isSet == findSets) {
  1529. if (baseName.length() == (ms.length() - prefixLength)) {
  1530. if (result == null) {
  1531. result = new ArrayList();
  1532. }
  1533. result.add(methods[i]);
  1534. }
  1535. }
  1536. }
  1537. }
  1538. }
  1539. }
  1540. if (propertyCache == null) {
  1541. cache.put(targetClass, propertyCache = new HashMap(101));
  1542. }
  1543. propertyCache.put(propertyName, (result == null) ? NotFoundList : result);
  1544. }
  1545. return (result == NotFoundList) ? null : result;
  1546. }
  1547. }
  1548. /**
  1549. * Convenience used to check if a method is volatile or synthetic so as to avoid
  1550. * calling un-callable methods.
  1551. *
  1552. * @param m The method to check.
  1553. * @return True if the method should be callable, false otherwise.
  1554. */
  1555. static boolean isMethodCallable(Method m)
  1556. {
  1557. if ((isJdk15() && m.isSynthetic()) || Modifier.isVolatile(m.getModifiers()))
  1558. return false;
  1559. return true;
  1560. }
  1561. public static Method getGetMethod(OgnlContext context, Class targetClass, String propertyName)
  1562. throws IntrospectionException, OgnlException
  1563. {
  1564. Method result = null;
  1565. List methods = getDeclaredMethods(targetClass, propertyName, false /* find 'get' methods */);
  1566. if (methods != null)
  1567. {
  1568. for (int i = 0, icount = methods.size(); i < icount; i++)
  1569. {
  1570. Method m = (Method) methods.get(i);
  1571. Class[] mParameterTypes = findParameterTypes(targetClass, m); //getParameterTypes(m);
  1572. if (mParameterTypes.length == 0)
  1573. {
  1574. result = m;
  1575. break;
  1576. }
  1577. }
  1578. }
  1579. return result;
  1580. }
  1581. public static boolean isMethodAccessible(OgnlContext context, Object target, Method method, String propertyName)
  1582. {
  1583. return (method != null) && context.getMemberAccess().isAccessible(context, target, method, propertyName);
  1584. }
  1585. public static boolean hasGetMethod(OgnlContext context, Object target, Class targetClass, String propertyName)
  1586. throws IntrospectionException, OgnlException
  1587. {
  1588. return isMethodAccessible(context, target, getGetMethod(context, targetClass, propertyName), propertyName);
  1589. }
  1590. public static Method getSetMethod(OgnlContext context, Class targetClass, String propertyName)
  1591. throws IntrospectionException, OgnlException
  1592. {
  1593. Method result = null;
  1594. List methods = getDeclaredMethods(targetClass, propertyName, true /* find 'set' methods */);
  1595. if (methods != null)
  1596. {
  1597. for (int i = 0, icount = methods.size(); i < icount; i++)
  1598. {
  1599. Method m = (Method) methods.get(i);
  1600. Class[] mParameterTypes = findParameterTypes(targetClass, m); //getParameterTypes(m);
  1601. if (mParameterTypes.length == 1) {
  1602. result = m;
  1603. break;
  1604. }
  1605. }
  1606. }
  1607. return result;
  1608. }
  1609. public static final boolean hasSetMethod(OgnlContext context, Object target, Class targetClass, String propertyName)
  1610. throws IntrospectionException, OgnlException
  1611. {
  1612. return isMethodAccessible(context, target, getSetMethod(context, targetClass, propertyName), propertyName);
  1613. }
  1614. public static final boolean hasGetProperty(OgnlContext context, Object target, Object oname)
  1615. throws IntrospectionException, OgnlException
  1616. {
  1617. Class targetClass = (target == null) ? null : target.getClass();
  1618. String name = oname.toString();
  1619. return hasGetMethod(context, target, targetClass, name) || hasField(context, target, targetClass, name);
  1620. }
  1621. public static final boolean hasSetProperty(OgnlContext context, Object target, Object oname)
  1622. throws IntrospectionException, OgnlException
  1623. {
  1624. Class targetClass = (target == null) ? null : target.getClass();
  1625. String name = oname.toString();
  1626. return hasSetMethod(context, target, targetClass, name) || hasField(context, target, targetClass, name);
  1627. }
  1628. private static final boolean indexMethodCheck(List methods)
  1629. {
  1630. boolean result = false;
  1631. if (methods.size() > 0) {
  1632. Method fm = (Method) methods.get(0);
  1633. Class[] fmpt = getParameterTypes(fm);
  1634. int fmpc = fmpt.length;
  1635. Class lastMethodClass = fm.getDeclaringClass();
  1636. result = true;
  1637. for (int i = 1; result && (i < methods.size()); i++) {
  1638. Method m = (Method) methods.get(i);
  1639. Class c = m.getDeclaringClass();
  1640. // Check to see if more than one method implemented per class
  1641. if (lastMethodClass == c) {
  1642. result = false;
  1643. } else {
  1644. Class[] mpt = getParameterTypes(fm);
  1645. int mpc = fmpt.length;
  1646. if (fmpc != mpc) {
  1647. result = false;
  1648. }
  1649. for (int j = 0; j < fmpc; j++) {
  1650. if (fmpt[j] != mpt[j]) {
  1651. result = false;
  1652. break;
  1653. }
  1654. }
  1655. }
  1656. lastMethodClass = c;
  1657. }
  1658. }
  1659. return result;
  1660. }
  1661. static void findObjectIndexedPropertyDescriptors(Class targetClass, Map intoMap)
  1662. throws OgnlException
  1663. {
  1664. Map allMethods = getMethods(targetClass, false);
  1665. Map pairs = new HashMap(101);
  1666. for (Iterator it = allMethods.keySet().iterator(); it.hasNext();) {
  1667. String methodName = (String) it.next();
  1668. List methods = (List) allMethods.get(methodName);
  1669. /*
  1670. * Only process set/get where there is exactly one implementation of the method per
  1671. * class and those implementations are all the same
  1672. */
  1673. if (indexMethodCheck(methods)) {
  1674. boolean isGet = false, isSet = false;
  1675. Method m = (Method) methods.get(0);
  1676. if (((isSet = methodName.startsWith(SET_PREFIX)) || (isGet = methodName.startsWith(GET_PREFIX)))
  1677. && (methodName.length() > 3)) {
  1678. String propertyName = Introspector.decapitalize(methodName.substring(3));
  1679. Class[] parameterTypes = getParameterTypes(m);
  1680. int parameterCount = parameterTypes.length;
  1681. if (isGet && (parameterCount == 1) && (m.getReturnType() != Void.TYPE)) {
  1682. List pair = (List) pairs.get(propertyName);
  1683. if (pair == null) {
  1684. pairs.put(propertyName, pair = new ArrayList());
  1685. }
  1686. pair.add(m);
  1687. }
  1688. if (isSet && (parameterCount == 2) && (m.getReturnType() == Void.TYPE)) {
  1689. List pair = (List) pairs.get(propertyName);
  1690. if (pair == null) {
  1691. pairs.put(propertyName, pair = new ArrayList());
  1692. }
  1693. pair.add(m);
  1694. }
  1695. }
  1696. }
  1697. }
  1698. for (Iterator it = pairs.keySet().iterator(); it.hasNext();) {
  1699. String propertyName = (String) it.next();
  1700. List methods = (List) pairs.get(propertyName);
  1701. if (methods.size() == 2) {
  1702. Method method1 = (Method) methods.get(0), method2 = (Method) methods.get(1), setMethod = (method1
  1703. .getParameterTypes().length == 2) ? method1 : method2, getMethod = (setMethod == method1) ? method2
  1704. : method1;
  1705. Class keyType = getMethod.getParameterTypes()[0], propertyType = getMethod.getReturnType();
  1706. if (keyType == setMethod.getParameterTypes()[0]) {
  1707. if (propertyType == setMethod.getParameterTypes()[1]) {
  1708. ObjectIndexedPropertyDescriptor propertyDescriptor;
  1709. try {
  1710. propertyDescriptor = new ObjectIndexedPropertyDescriptor(propertyName, propertyType,
  1711. getMethod, setMethod);
  1712. } catch (Exception ex) {
  1713. throw new OgnlException("creating object indexed property descriptor for '" + propertyName
  1714. + "' in " + targetClass, ex);
  1715. }
  1716. intoMap.put(propertyName, propertyDescriptor);
  1717. }
  1718. }
  1719. }
  1720. }
  1721. }
  1722. /**
  1723. * This method returns the property descriptors for the given class as a Map.
  1724. *
  1725. * @param targetClass The class to get the descriptors for.
  1726. * @return Map map of property descriptors for class.
  1727. *
  1728. * @throws IntrospectionException on errors using {@link Introspector}.
  1729. * @throws OgnlException On general errors.
  1730. */
  1731. public static Map getPropertyDescriptors(Class targetClass)
  1732. throws IntrospectionException, OgnlException
  1733. {
  1734. Map result;
  1735. synchronized (_propertyDescriptorCache) {
  1736. if ((result = (Map) _propertyDescriptorCache.get(targetClass)) == null)
  1737. {
  1738. PropertyDescriptor[] pda = Introspector.getBeanInfo(targetClass).getPropertyDescriptors();
  1739. result = new HashMap(101);
  1740. for (int i = 0, icount = pda.length; i < icount; i++)
  1741. {
  1742. // workaround for Introspector bug 6528714 (bugs.sun.com)
  1743. if (pda[i].getReadMethod() != null && !isMethodCallable(pda[i].getReadMethod()))
  1744. {
  1745. pda[i].setReadMethod(findClosestMatchingMethod(targetClass, pda[i].getReadMethod(), pda[i].getName(),
  1746. pda[i].getPropertyType(), true));
  1747. }
  1748. if (pda[i].getWriteMethod() != null && !isMethodCallable(pda[i].getWriteMethod()))
  1749. {
  1750. pda[i].setWriteMethod(findClosestMatchingMethod(targetClass, pda[i].getWriteMethod(), pda[i].getName(),
  1751. pda[i].getPropertyType(), false));
  1752. }
  1753. result.put(pda[i].getName(), pda[i]);
  1754. }
  1755. findObjectIndexedPropertyDescriptors(targetClass, result);
  1756. _propertyDescriptorCache.put(targetClass, result);
  1757. }
  1758. }
  1759. return result;
  1760. }
  1761. /**
  1762. * This method returns a PropertyDescriptor for the given class and property name using a Map
  1763. * lookup (using getPropertyDescriptorsMap()).
  1764. */
  1765. public static PropertyDescriptor getPropertyDescriptor(Class targetClass, String propertyName)
  1766. throws IntrospectionException, OgnlException
  1767. {
  1768. if (targetClass == null)
  1769. return null;
  1770. return (PropertyDescriptor) getPropertyDescriptors(targetClass).get(propertyName);
  1771. }
  1772. static Method findClosestMatchingMethod(Class targetClass, Method m, String propertyName,
  1773. Class propertyType, boolean isReadMethod)
  1774. {
  1775. List methods = getDeclaredMethods(targetClass, propertyName, !isReadMethod);
  1776. for (int i=0; i < methods.size(); i++)
  1777. {
  1778. Method method = (Method) methods.get(i);
  1779. if (method.getName().equals(m.getName())
  1780. && m.getReturnType().isAssignableFrom(m.getReturnType())
  1781. && method.getReturnType() == propertyType
  1782. && method.getParameterTypes().length == m.getParameterTypes().length) {
  1783. return method;
  1784. }
  1785. }
  1786. return m;
  1787. }
  1788. public static PropertyDescriptor[] getPropertyDescriptorsArray(Class targetClass)
  1789. throws IntrospectionException
  1790. {
  1791. PropertyDescriptor[] result = null;
  1792. if (targetClass != null) {
  1793. synchronized (_propertyDescriptorCache) {
  1794. if ((result = (PropertyDescriptor[]) _propertyDescriptorCache.get(targetClass)) == null) {
  1795. _propertyDescriptorCache.put(targetClass, result = Introspector.getBeanInfo(targetClass)
  1796. .getPropertyDescriptors());
  1797. }
  1798. }
  1799. }
  1800. return result;
  1801. }
  1802. /**
  1803. * Gets the property descriptor with the given name for the target class given.
  1804. *
  1805. * @param targetClass Class for which property descriptor is desired
  1806. * @param name Name of property
  1807. * @return PropertyDescriptor of the named property or null if the class has no property with
  1808. * the given name
  1809. */
  1810. public static PropertyDescriptor getPropertyDescriptorFromArray(Class targetClass, String name)
  1811. throws IntrospectionException
  1812. {
  1813. PropertyDescriptor result = null;
  1814. PropertyDescriptor[] pda = getPropertyDescriptorsArray(targetClass);
  1815. for (int i = 0, icount = pda.length; (result == null) && (i < icount); i++) {
  1816. if (pda[i].getName().compareTo(name) == 0) {
  1817. result = pda[i];
  1818. }
  1819. }
  1820. return result;
  1821. }
  1822. public static void setMethodAccessor(Class cls, MethodAccessor accessor)
  1823. {
  1824. synchronized (_methodAccessors) {
  1825. _methodAccessors.put(cls, accessor);
  1826. }
  1827. }
  1828. public static MethodAccessor getMethodAccessor(Class cls)
  1829. throws OgnlException
  1830. {
  1831. MethodAccessor answer = (MethodAccessor) getHandler(cls, _methodAccessors);
  1832. if (answer != null)
  1833. return answer;
  1834. throw new OgnlException("No method accessor for " + cls);
  1835. }
  1836. public static void setPropertyAccessor(Class cls, PropertyAccessor accessor)
  1837. {
  1838. synchronized (_propertyAccessors) {
  1839. _propertyAccessors.put(cls, accessor);
  1840. }
  1841. }
  1842. public static PropertyAccessor getPropertyAccessor(Class cls)
  1843. throws OgnlException
  1844. {
  1845. PropertyAccessor answer = (PropertyAccessor) getHandler(cls, _propertyAccessors);
  1846. if (answer != null)
  1847. return answer;
  1848. throw new OgnlException("No property accessor for class " + cls);
  1849. }
  1850. public static ElementsAccessor getElementsAccessor(Class cls)
  1851. throws OgnlException
  1852. {
  1853. ElementsAccessor answer = (ElementsAccessor) getHandler(cls, _elementsAccessors);
  1854. if (answer != null)
  1855. return answer;
  1856. throw new OgnlException("No elements accessor for class " + cls);
  1857. }
  1858. public static void setElementsAccessor(Class cls, ElementsAccessor accessor)
  1859. {
  1860. synchronized (_elementsAccessors) {
  1861. _elementsAccessors.put(cls, accessor);
  1862. }
  1863. }
  1864. public static NullHandler getNullHandler(Class cls)
  1865. throws OgnlException
  1866. {
  1867. NullHandler answer = (NullHandler) getHandler(cls, _nullHandlers);
  1868. if (answer != null)
  1869. return answer;
  1870. throw new OgnlException("No null handler for class " + cls);
  1871. }
  1872. public static void setNullHandler(Class cls, NullHandler handler)
  1873. {
  1874. synchronized (_nullHandlers) {
  1875. _nullHandlers.put(cls, handler);
  1876. }
  1877. }
  1878. private static Object getHandler(Class forClass, ClassCache handlers)
  1879. {
  1880. Object answer = null;
  1881. synchronized (handlers) {
  1882. if ((answer = handlers.get(forClass)) == null) {
  1883. Class keyFound;
  1884. if (forClass.isArray()) {
  1885. answer = handlers.get(Object[].class);
  1886. keyFound = null;
  1887. } else {
  1888. keyFound = forClass;
  1889. outer:
  1890. for (Class c = forClass; c != null; c = c.getSuperclass()) {
  1891. answer = handlers.get(c);
  1892. if (answer == null) {
  1893. Class[] interfaces = c.getInterfaces();
  1894. for (int index = 0, count = interfaces.length; index < count; ++index) {
  1895. Class iface = interfaces[index];
  1896. answer = handlers.get(iface);
  1897. if (answer == null) {
  1898. /* Try super-interfaces */
  1899. answer = getHandler(iface, handlers);
  1900. }
  1901. if (answer != null) {
  1902. keyFound = iface;
  1903. break outer;
  1904. }
  1905. }
  1906. } else {
  1907. keyFound = c;
  1908. break;
  1909. }
  1910. }
  1911. }
  1912. if (answer != null) {
  1913. if (keyFound != forClass) {
  1914. handlers.put(forClass, answer);
  1915. }
  1916. }
  1917. }
  1918. }
  1919. return answer;
  1920. }
  1921. public static Object getProperty(OgnlContext context, Object source, Object name)
  1922. throws OgnlException
  1923. {
  1924. PropertyAccessor accessor;
  1925. if (source == null)
  1926. {
  1927. throw new OgnlException("source is null for getProperty(null, \"" + name + "\")");
  1928. }
  1929. if ((accessor = getPropertyAccessor(getTargetClass(source))) == null)
  1930. {
  1931. throw new OgnlException("No property accessor for " + getTargetClass(source).getName());
  1932. }
  1933. return accessor.getProperty(context, source, name);
  1934. }
  1935. public static void setProperty(OgnlContext context, Object target, Object name, Object value)
  1936. throws OgnlException
  1937. {
  1938. PropertyAccessor accessor;
  1939. if (target == null) {
  1940. throw new OgnlException("target is null for setProperty(null, \"" + name + "\", " + value + ")");
  1941. }
  1942. if ((accessor = getPropertyAccessor(getTargetClass(target))) == null) {
  1943. throw new OgnlException("No property accessor for " + getTargetClass(target).getName());
  1944. }
  1945. accessor.setProperty(context, target, name, value);
  1946. }
  1947. /**
  1948. * Determines the index property type, if any. Returns <code>INDEXED_PROPERTY_NONE</code> if
  1949. * the property is not index-accessible as determined by OGNL or JavaBeans. If it is indexable
  1950. * then this will return whether it is a JavaBeans indexed property, conforming to the indexed
  1951. * property patterns (returns <code>INDEXED_PROPERTY_INT</code>) or if it conforms to the
  1952. * OGNL arbitrary object indexable (returns <code>INDEXED_PROPERTY_OBJECT</code>).
  1953. */
  1954. public static int getIndexedPropertyType(OgnlContext context, Class sourceClass, String name)
  1955. throws OgnlException
  1956. {
  1957. int result = INDEXED_PROPERTY_NONE;
  1958. try {
  1959. PropertyDescriptor pd = getPropertyDescriptor(sourceClass, name);
  1960. if (pd != null) {
  1961. if (pd instanceof IndexedPropertyDescriptor) {
  1962. result = INDEXED_PROPERTY_INT;
  1963. } else {
  1964. if (pd instanceof ObjectIndexedPropertyDescriptor) {
  1965. result = INDEXED_PROPERTY_OBJECT;
  1966. }
  1967. }
  1968. }
  1969. } catch (Exception ex) {
  1970. throw new OgnlException("problem determining if '" + name + "' is an indexed property", ex);
  1971. }
  1972. return result;
  1973. }
  1974. public static Object getIndexedProperty(OgnlContext context, Object source, String name, Object index)
  1975. throws OgnlException
  1976. {
  1977. Object[] args = _objectArrayPool.create(index);
  1978. try {
  1979. PropertyDescriptor pd = getPropertyDescriptor((source == null) ? null : source.getClass(), name);
  1980. Method m;
  1981. if (pd instanceof IndexedPropertyDescriptor) {
  1982. m = ((IndexedPropertyDescriptor) pd).getIndexedReadMethod();
  1983. } else {
  1984. if (pd instanceof ObjectIndexedPropertyDescriptor) {
  1985. m = ((ObjectIndexedPropertyDescriptor) pd).getIndexedReadMethod();
  1986. } else {
  1987. throw new OgnlException("property '" + name + "' is not an indexed property");
  1988. }
  1989. }
  1990. return callMethod(context, source, m.getName(), args);
  1991. } catch (OgnlException ex) {
  1992. throw ex;
  1993. } catch (Exception ex) {
  1994. throw new OgnlException("getting indexed property descriptor for '" + name + "'", ex);
  1995. } finally {
  1996. _objectArrayPool.recycle(args);
  1997. }
  1998. }
  1999. public static void setIndexedProperty(OgnlContext context, Object source, String name, Object index,
  2000. Object value)
  2001. throws OgnlException
  2002. {
  2003. Object[] args = _objectArrayPool.create(index, value);
  2004. try {
  2005. PropertyDescriptor pd = getPropertyDescriptor((source == null) ? null : source.getClass(), name);
  2006. Method m;
  2007. if (pd instanceof IndexedPropertyDescriptor) {
  2008. m = ((IndexedPropertyDescriptor) pd).getIndexedWriteMethod();
  2009. } else {
  2010. if (pd instanceof ObjectIndexedPropertyDescriptor) {
  2011. m = ((ObjectIndexedPropertyDescriptor) pd).getIndexedWriteMethod();
  2012. } else {
  2013. throw new OgnlException("property '" + name + "' is not an indexed property");
  2014. }
  2015. }
  2016. callMethod(context, source, m.getName(), args);
  2017. } catch (OgnlException ex) {
  2018. throw ex;
  2019. } catch (Exception ex) {
  2020. throw new OgnlException("getting indexed property descriptor for '" + name + "'", ex);
  2021. } finally {
  2022. _objectArrayPool.recycle(args);
  2023. }
  2024. }
  2025. public static EvaluationPool getEvaluationPool()
  2026. {
  2027. return _evaluationPool;
  2028. }
  2029. public static ObjectArrayPool getObjectArrayPool()
  2030. {
  2031. return _objectArrayPool;
  2032. }
  2033. /**
  2034. * Registers the specified {@link ClassCacheInspector} with all class reflection based internal
  2035. * caches. This may have a significant performance impact so be careful using this in production scenarios.
  2036. *
  2037. * @param inspector
  2038. * The inspector instance that will be registered with all internal cache instances.
  2039. */
  2040. public static void setClassCacheInspector(ClassCacheInspector inspector)
  2041. {
  2042. _cacheInspector = inspector;
  2043. _propertyDescriptorCache.setClassInspector(_cacheInspector);
  2044. _constructorCache.setClassInspector(_cacheInspector);
  2045. _staticMethodCache.setClassInspector(_cacheInspector);
  2046. _instanceMethodCache.setClassInspector(_cacheInspector);
  2047. _invokePermissionCache.setClassInspector(_cacheInspector);
  2048. _fieldCache.setClassInspector(_cacheInspector);
  2049. _declaredMethods[0].setClassInspector(_cacheInspector);
  2050. _declaredMethods[1].setClassInspector(_cacheInspector);
  2051. }
  2052. public static Method getMethod(OgnlContext context, Class target, String name,
  2053. Node[] children, boolean includeStatic)
  2054. throws Exception
  2055. {
  2056. Class[] parms;
  2057. if (children != null && children.length > 0)
  2058. {
  2059. parms = new Class[children.length];
  2060. // used to reset context after loop
  2061. Class currType = context.getCurrentType();
  2062. Class currAccessor = context.getCurrentAccessor();
  2063. Object cast = context.get(ExpressionCompiler.PRE_CAST);
  2064. context.setCurrentObject(context.getRoot());
  2065. context.setCurrentType(context.getRoot() != null ? context.getRoot().getClass() : null);
  2066. context.setCurrentAccessor(null);
  2067. context.setPreviousType(null);
  2068. for (int i=0; i < children.length; i++)
  2069. {
  2070. children[i].toGetSourceString(context, context.getRoot());
  2071. parms[i] = context.getCurrentType();
  2072. }
  2073. context.put(ExpressionCompiler.PRE_CAST, cast);
  2074. context.setCurrentType(currType);
  2075. context.setCurrentAccessor(currAccessor);
  2076. context.setCurrentObject(target);
  2077. } else
  2078. {
  2079. parms = new Class[0];
  2080. }
  2081. List methods = OgnlRuntime.getMethods(target, name, includeStatic);
  2082. if (methods == null)
  2083. return null;
  2084. for (int i = 0; i < methods.size(); i++)
  2085. {
  2086. Method m = (Method) methods.get(i);
  2087. boolean varArgs = isJdk15() && m.isVarArgs();
  2088. if (parms.length != m.getParameterTypes().length && !varArgs)
  2089. continue;
  2090. Class[] mparms = m.getParameterTypes();
  2091. boolean matched = true;
  2092. for (int p = 0; p < mparms.length; p++)
  2093. {
  2094. if (varArgs && mparms[p].isArray()){
  2095. continue;
  2096. }
  2097. if (parms[p] == null)
  2098. {
  2099. matched = false;
  2100. break;
  2101. }
  2102. if (parms[p] == mparms[p])
  2103. continue;
  2104. if (mparms[p].isPrimitive()
  2105. && Character.TYPE != mparms[p] && Byte.TYPE != mparms[p]
  2106. && Number.class.isAssignableFrom(parms[p])
  2107. && OgnlRuntime.getPrimitiveWrapperClass(parms[p]) == mparms[p])
  2108. {
  2109. continue;
  2110. }
  2111. matched = false;
  2112. break;
  2113. }
  2114. if (matched)
  2115. return m;
  2116. }
  2117. return null;
  2118. }
  2119. /**
  2120. * Finds the best possible match for a method on the specified target class with a matching
  2121. * name.
  2122. *
  2123. * <p>
  2124. * The name matched will also try different combinations like <code>is + name, has + name, get + name, etc..</code>
  2125. * </p>
  2126. *
  2127. * @param target
  2128. * The class to find a matching method against.
  2129. * @param name
  2130. * The name of the method.
  2131. * @return The most likely matching {@link Method}, or null if none could be found.
  2132. */
  2133. public static Method getReadMethod(Class target, String name)
  2134. {
  2135. return getReadMethod(target, name, -1);
  2136. }
  2137. public static Method getReadMethod(Class target, String name, int numParms)
  2138. {
  2139. try {
  2140. name = name.replaceAll("\"", "").toLowerCase();
  2141. BeanInfo info = Introspector.getBeanInfo(target);
  2142. MethodDescriptor[] methods = info.getMethodDescriptors();
  2143. // exact matches first
  2144. Method m = null;
  2145. for (int i = 0; i < methods.length; i++)
  2146. {
  2147. if (!isMethodCallable(methods[i].getMethod()))
  2148. continue;
  2149. if ((methods[i].getName().equalsIgnoreCase(name)
  2150. || methods[i].getName().toLowerCase().equals(name)
  2151. || methods[i].getName().toLowerCase().equals("get" + name)
  2152. || methods[i].getName().toLowerCase().equals("has" + name)
  2153. || methods[i].getName().toLowerCase().equals("is" + name))
  2154. && !methods[i].getName().startsWith("set"))
  2155. {
  2156. if (numParms > 0 && methods[i].getMethod().getParameterTypes().length == numParms)
  2157. return methods[i].getMethod();
  2158. else if (numParms < 0)
  2159. {
  2160. if ((m != null && m.getParameterTypes().length > methods[i].getMethod().getParameterTypes().length)
  2161. || m == null)
  2162. {
  2163. m = methods[i].getMethod();
  2164. }
  2165. }
  2166. }
  2167. }
  2168. if (m != null)
  2169. return m;
  2170. for (int i = 0; i < methods.length; i++)
  2171. {
  2172. if (!isMethodCallable(methods[i].getMethod()))
  2173. continue;
  2174. if (methods[i].getName().toLowerCase().endsWith(name)
  2175. && !methods[i].getName().startsWith("set")
  2176. && methods[i].getMethod().getReturnType() != Void.TYPE) {
  2177. if (numParms > 0 && methods[i].getMethod().getParameterTypes().length == numParms)
  2178. return methods[i].getMethod();
  2179. else if (numParms < 0)
  2180. {
  2181. if ((m != null && m.getParameterTypes().length > methods[i].getMethod().getParameterTypes().length)
  2182. || m == null)
  2183. {
  2184. m = methods[i].getMethod();
  2185. }
  2186. }
  2187. }
  2188. }
  2189. if (m != null)
  2190. return m;
  2191. // try one last time adding a get to beginning
  2192. if (!name.startsWith("get"))
  2193. return OgnlRuntime.getReadMethod(target, "get" + name, numParms);
  2194. } catch (Throwable t)
  2195. {
  2196. throw OgnlOps.castToRuntime(t);
  2197. }
  2198. return null;
  2199. }
  2200. public static Method getWriteMethod(Class target, String name)
  2201. {
  2202. return getWriteMethod(target, name, -1);
  2203. }
  2204. public static Method getWriteMethod(Class target, String name, int numParms)
  2205. {
  2206. try {
  2207. name = name.replaceAll("\"", "");
  2208. BeanInfo info = Introspector.getBeanInfo(target);
  2209. MethodDescriptor[] methods = info.getMethodDescriptors();
  2210. for (int i = 0; i < methods.length; i++) {
  2211. if (!isMethodCallable(methods[i].getMethod()))
  2212. continue;
  2213. if ((methods[i].getName().equalsIgnoreCase(name)
  2214. || methods[i].getName().toLowerCase().equals(name.toLowerCase())
  2215. || methods[i].getName().toLowerCase().equals("set" + name.toLowerCase()))
  2216. && !methods[i].getName().startsWith("get")) {
  2217. if (numParms > 0 && methods[i].getMethod().getParameterTypes().length == numParms)
  2218. return methods[i].getMethod();
  2219. else if (numParms < 0)
  2220. return methods[i].getMethod();
  2221. }
  2222. }
  2223. // try again on pure class
  2224. Method[] cmethods = target.getClass().getMethods();
  2225. for (int i = 0; i < cmethods.length; i++) {
  2226. if (!isMethodCallable(cmethods[i]))
  2227. continue;
  2228. if ((cmethods[i].getName().equalsIgnoreCase(name)
  2229. || cmethods[i].getName().toLowerCase().equals(name.toLowerCase())
  2230. || cmethods[i].getName().toLowerCase().equals("set" + name.toLowerCase()))
  2231. && !cmethods[i].getName().startsWith("get")) {
  2232. if (numParms > 0 && cmethods[i].getParameterTypes().length == numParms)
  2233. return cmethods[i];
  2234. else if (numParms < 0)
  2235. return cmethods[i];
  2236. }
  2237. }
  2238. // try one last time adding a set to beginning
  2239. if (!name.startsWith("set")) {
  2240. return OgnlRuntime.getReadMethod(target, "set" + name, numParms);
  2241. }
  2242. } catch (Throwable t)
  2243. {
  2244. throw OgnlOps.castToRuntime(t);
  2245. }
  2246. return null;
  2247. }
  2248. public static PropertyDescriptor getProperty(Class target, String name)
  2249. {
  2250. try {
  2251. BeanInfo info = Introspector.getBeanInfo(target);
  2252. PropertyDescriptor[] pds = info.getPropertyDescriptors();
  2253. for (int i = 0; i < pds.length; i++) {
  2254. if (pds[i].getName().equalsIgnoreCase(name)
  2255. || pds[i].getName().toLowerCase().equals(name.toLowerCase())
  2256. || pds[i].getName().toLowerCase().endsWith(name.toLowerCase()))
  2257. return pds[i];
  2258. }
  2259. } catch (Throwable t)
  2260. {
  2261. throw OgnlOps.castToRuntime(t);
  2262. }
  2263. return null;
  2264. }
  2265. public static boolean isBoolean(String expression)
  2266. {
  2267. if (expression == null)
  2268. return false;
  2269. if ("true".equals(expression) || "false".equals(expression)
  2270. || "!true".equals(expression) || "!false".equals(expression)
  2271. || "(true)".equals(expression)
  2272. || "!(true)".equals(expression)
  2273. || "(false)".equals(expression)
  2274. || "!(false)".equals(expression)
  2275. || expression.startsWith("org.apache.commons.ognl.OgnlOps"))
  2276. return true;
  2277. return false;
  2278. }
  2279. /**
  2280. * Compares the {@link OgnlContext#getCurrentType()} and {@link OgnlContext#getPreviousType()} class types
  2281. * on the stack to determine if a numeric expression should force object conversion.
  2282. * <p/>
  2283. * <p/>
  2284. * Normally used in conjunction with the <code>forceConversion</code> parameter of
  2285. * {@link OgnlRuntime#getChildSource(OgnlContext,Object,Node,boolean)}.
  2286. * </p>
  2287. *
  2288. * @param context The current context.
  2289. * @return True, if the class types on the stack wouldn't be comparable in a pure numeric expression such as <code>o1 >= o2</code>.
  2290. */
  2291. public static boolean shouldConvertNumericTypes(OgnlContext context)
  2292. {
  2293. if (context.getCurrentType() == null || context.getPreviousType() == null)
  2294. return true;
  2295. if (context.getCurrentType() == context.getPreviousType()
  2296. && context.getCurrentType().isPrimitive() && context.getPreviousType().isPrimitive())
  2297. return false;
  2298. return context.getCurrentType() != null && !context.getCurrentType().isArray()
  2299. && context.getPreviousType() != null && !context.getPreviousType().isArray();
  2300. }
  2301. /**
  2302. * Attempts to get the java source string represented by the specific child expression
  2303. * via the {@link JavaSource#toGetSourceString(OgnlContext,Object)} interface method.
  2304. *
  2305. * @param context The ognl context to pass to the child.
  2306. * @param target The current object target to use.
  2307. * @param child The child expression.
  2308. * @return The result of calling {@link JavaSource#toGetSourceString(OgnlContext,Object)} plus additional
  2309. * enclosures of {@link OgnlOps#convertValue(Object,Class,boolean)} for conversions.
  2310. * @throws OgnlException Mandatory exception throwing catching.. (blehh)
  2311. */
  2312. public static String getChildSource(OgnlContext context, Object target, Node child)
  2313. throws OgnlException
  2314. {
  2315. return getChildSource(context, target, child, false);
  2316. }
  2317. /**
  2318. * Attempts to get the java source string represented by the specific child expression
  2319. * via the {@link JavaSource#toGetSourceString(OgnlContext,Object)} interface method.
  2320. *
  2321. * @param context The ognl context to pass to the child.
  2322. * @param target The current object target to use.
  2323. * @param child The child expression.
  2324. * @param forceConversion If true, forces {@link OgnlOps#convertValue(Object,Class)} conversions on the objects.
  2325. * @return The result of calling {@link JavaSource#toGetSourceString(OgnlContext,Object)} plus additional
  2326. * enclosures of {@link OgnlOps#convertValue(Object,Class,boolean)} for conversions.
  2327. * @throws OgnlException Mandatory exception throwing catching.. (blehh)
  2328. */
  2329. public static String getChildSource(OgnlContext context, Object target, Node child, boolean forceConversion)
  2330. throws OgnlException
  2331. {
  2332. String pre = (String) context.get("_currentChain");
  2333. if (pre == null)
  2334. pre = "";
  2335. try
  2336. {
  2337. child.getValue(context, target);
  2338. } catch (NullPointerException e)
  2339. {
  2340. // ignore
  2341. } catch (ArithmeticException e)
  2342. {
  2343. context.setCurrentType(int.class);
  2344. return "0";
  2345. } catch (Throwable t)
  2346. {
  2347. throw OgnlOps.castToRuntime(t);
  2348. }
  2349. String source = null;
  2350. try
  2351. {
  2352. source = child.toGetSourceString(context, target);
  2353. }
  2354. catch (Throwable t)
  2355. {
  2356. throw OgnlOps.castToRuntime(t);
  2357. }
  2358. // handle root / method expressions that may not have proper root java source access
  2359. if (!ASTConst.class.isInstance(child)
  2360. && (target == null || context.getRoot() != target))
  2361. {
  2362. source = pre + source;
  2363. }
  2364. if (context.getRoot() != null)
  2365. {
  2366. source = ExpressionCompiler.getRootExpression(child, context.getRoot(), context) + source;
  2367. context.setCurrentAccessor(context.getRoot().getClass());
  2368. }
  2369. if (ASTChain.class.isInstance(child))
  2370. {
  2371. String cast = (String) context.remove(ExpressionCompiler.PRE_CAST);
  2372. if (cast == null)
  2373. cast = "";
  2374. source = cast + source;
  2375. }
  2376. if (source == null || source.trim().length() < 1)
  2377. source = "null";
  2378. return source;
  2379. }
  2380. }