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