Coverage Report - org.apache.commons.ognl.OgnlRuntime
 
Classes in this File Line Coverage Branch Coverage Complexity
OgnlRuntime
72%
514/707
67%
387/570
4.709
 
 1  
 package org.apache.commons.ognl;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *  http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import org.apache.commons.ognl.enhance.ExpressionCompiler;
 23  
 import org.apache.commons.ognl.enhance.OgnlExpressionCompiler;
 24  
 import org.apache.commons.ognl.internal.CacheException;
 25  
 import org.apache.commons.ognl.internal.entry.DeclaredMethodCacheEntry;
 26  
 import org.apache.commons.ognl.internal.entry.GenericMethodParameterTypeCacheEntry;
 27  
 import org.apache.commons.ognl.internal.entry.MethodAccessEntryValue;
 28  
 import org.apache.commons.ognl.internal.entry.PermissionCacheEntry;
 29  
 
 30  
 import java.beans.BeanInfo;
 31  
 import java.beans.IndexedPropertyDescriptor;
 32  
 import java.beans.IntrospectionException;
 33  
 import java.beans.Introspector;
 34  
 import java.beans.MethodDescriptor;
 35  
 import java.beans.PropertyDescriptor;
 36  
 import java.lang.reflect.Constructor;
 37  
 import java.lang.reflect.Field;
 38  
 import java.lang.reflect.InvocationTargetException;
 39  
 import java.lang.reflect.Member;
 40  
 import java.lang.reflect.Method;
 41  
 import java.lang.reflect.Modifier;
 42  
 import java.lang.reflect.ParameterizedType;
 43  
 import java.lang.reflect.Proxy;
 44  
 import java.security.Permission;
 45  
 import java.util.ArrayList;
 46  
 import java.util.Collection;
 47  
 import java.util.Collections;
 48  
 import java.util.HashMap;
 49  
 import java.util.List;
 50  
 import java.util.Map;
 51  
 
 52  
 /**
 53  
  * Utility class used by internal OGNL API to do various things like:
 54  
  * <ul>
 55  
  * <li>Handles majority of reflection logic / caching.</li>
 56  
  * <li>Utility methods for casting strings / various numeric types used by {@link OgnlExpressionCompiler}.</li.
 57  
  * <li>Core runtime configuration point for setting/using global {@link TypeConverter} / {@link OgnlExpressionCompiler}
 58  
  * / {@link NullHandler} instances / etc..</li>
 59  
  * </ul>
 60  
  *
 61  
  * $Id: OgnlRuntime.java 1239219 2012-02-01 17:27:54Z mcucchiara $
 62  
  *
 63  
  * @author Luke Blanshard (blanshlu@netscape.net)
 64  
  * @author Drew Davidson (drew@ognl.org)
 65  
  */
 66  0
 public class OgnlRuntime
 67  
 {
 68  
     /**
 69  
      * Constant expression used to indicate that a given method / property couldn't be found during reflection
 70  
      * operations.
 71  
      */
 72  1
     public static final Object NotFound = new Object();
 73  
 
 74  1
     public static final Object[] NoArguments = new Object[]{ };
 75  
 
 76  
     /**
 77  
      * Token returned by TypeConverter for no conversion possible
 78  
      */
 79  1
     public static final Object NoConversionPossible = "ognl.NoConversionPossible";
 80  
 
 81  
     /**
 82  
      * Not an indexed property
 83  
      */
 84  
     public static final int INDEXED_PROPERTY_NONE = 0;
 85  
 
 86  
     /**
 87  
      * JavaBeans IndexedProperty
 88  
      */
 89  
     public static final int INDEXED_PROPERTY_INT = 1;
 90  
 
 91  
     /**
 92  
      * OGNL ObjectIndexedProperty
 93  
      */
 94  
     public static final int INDEXED_PROPERTY_OBJECT = 2;
 95  
 
 96  
     /**
 97  
      * Constant string representation of null string.
 98  
      */
 99  1
     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  1
     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  1
     static OgnlCache cache = new OgnlCache();
 129  
 
 130  1
     private static final PrimitiveTypes primitiveTypes = new PrimitiveTypes();
 131  
 
 132  1
     private static final PrimitiveDefaults primitiveDefaults = new PrimitiveDefaults();
 133  
 
 134  1
     private static SecurityManager securityManager = System.getSecurityManager();
 135  
 
 136  1
     private static final EvaluationPool evaluationPool = new EvaluationPool();
 137  
 
 138  1
     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  1
     private static final PrimitiveWrapperClasses primitiveWrapperClasses = new PrimitiveWrapperClasses();
 149  
 
 150  
     /**
 151  
      * Constant strings for casting different primitive types.
 152  
      */
 153  1
     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  1
     private static final NumericValues numericValues = new NumericValues();
 160  
 
 161  
     /**
 162  
      * Numeric primitive literal string expressions.
 163  
      */
 164  1
     private static final NumericLiterals numericLiterals = new NumericLiterals();
 165  
 
 166  1
     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  2
         cache.clear();
 179  2
     }
 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  0
         return true;
 188  
     }
 189  
 
 190  
     public static String getNumericValueGetter( Class<?> type )
 191  
     {
 192  62
         return numericValues.get( type );
 193  
     }
 194  
 
 195  
     public static Class<?> getPrimitiveWrapperClass( Class<?> primitiveClass )
 196  
     {
 197  397
         return primitiveWrapperClasses.get( primitiveClass );
 198  
     }
 199  
 
 200  
     public static String getNumericCast( Class<? extends Number> type )
 201  
     {
 202  0
         return numericCasts.get( type );
 203  
     }
 204  
 
 205  
     public static String getNumericLiteral( Class<? extends Number> type )
 206  
     {
 207  16
         return numericLiterals.get( type );
 208  
     }
 209  
 
 210  
     public static void setCompiler( OgnlExpressionCompiler compiler )
 211  
     {
 212  0
         OgnlRuntime.compiler = compiler;
 213  0
     }
 214  
 
 215  
     /**
 216  
      * @deprecated use getCompiler(OgnlContext) instead
 217  
      */
 218  
     public static OgnlExpressionCompiler getCompiler()
 219  
     {
 220  0
         return getCompiler( null );
 221  
     }
 222  
 
 223  
     public static OgnlExpressionCompiler getCompiler( OgnlContext ognlContext )
 224  
     {
 225  333922
         if ( compiler == null )
 226  
         {
 227  
             try
 228  
             {
 229  50
                 OgnlRuntime.classForName( ognlContext, "javassist.ClassPool" );
 230  50
                 compiler = new ExpressionCompiler();
 231  
             }
 232  0
             catch ( ClassNotFoundException e )
 233  
             {
 234  0
                 throw new IllegalArgumentException(
 235  
                     "Javassist library is missing in classpath! Please add missed dependency!", e );
 236  50
             }
 237  
         }
 238  333922
         return compiler;
 239  
     }
 240  
 
 241  
     public static void compileExpression( OgnlContext context, Node expression, Object root )
 242  
         throws Exception
 243  
     {
 244  562
         getCompiler( context ).compileExpression( context, expression, root );
 245  561
     }
 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  1645
         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  0
         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  0
         String className = clazz.getName();
 270  0
         return className.substring( className.lastIndexOf( '.' ) + 1 );
 271  
     }
 272  
 
 273  
     public static String getClassName( Object object, boolean fullyQualified )
 274  
     {
 275  0
         if ( !( object instanceof Class ) )
 276  
         {
 277  0
             object = object.getClass();
 278  
         }
 279  
 
 280  0
         return getClassName( (Class<?>) object, fullyQualified );
 281  
     }
 282  
 
 283  
     public static String getClassName( Class<?> clazz, boolean fullyQualified )
 284  
     {
 285  0
         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  0
         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  0
         String className = clazz.getName();
 302  0
         int index = className.lastIndexOf( '.' );
 303  
 
 304  0
         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  0
         String hex = Integer.toHexString( num ), pad;
 313  0
         Integer l = hex.length();
 314  
 
 315  
         // stringBuilder.append(HEX_PREFIX);
 316  0
         if ( ( pad = HEX_PADDING.get( l ) ) == null )
 317  
         {
 318  0
             StringBuilder paddingStringBuilder = new StringBuilder();
 319  
 
 320  0
             for ( int i = hex.length(); i < HEX_LENGTH; i++ )
 321  
             {
 322  0
                 paddingStringBuilder.append( '0' );
 323  
             }
 324  0
             pad = paddingStringBuilder.toString();
 325  0
             HEX_PADDING.put( l, pad );
 326  
         }
 327  0
         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  0
         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  0
         StringBuilder stringBuilder = new StringBuilder();
 347  
 
 348  0
         if ( object != null )
 349  
         {
 350  0
             if ( object instanceof Proxy )
 351  
             {
 352  0
                 Class<?> interfaceClass = object.getClass().getInterfaces()[0];
 353  
 
 354  0
                 String className = getClassName( interfaceClass, fullyQualified );
 355  0
                 stringBuilder.append( className ).append( '^' );
 356  0
                 object = Proxy.getInvocationHandler( object );
 357  
             }
 358  0
             String className = getClassName( object, fullyQualified );
 359  0
             String pointerString = getPointerString( object );
 360  0
             stringBuilder.append( className ).append( '@' ).append( pointerString );
 361  0
         }
 362  
         else
 363  
         {
 364  0
             stringBuilder.append( NULL_OBJECT_STRING );
 365  
         }
 366  0
         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  0
         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  0
         int size = list.size();
 385  
 
 386  0
         if ( size == 0 )
 387  
         {
 388  0
             array = NoArguments;
 389  
         }
 390  
         else
 391  
         {
 392  0
             array = getObjectArrayPool().create( size );
 393  0
             for ( int i = 0; i < size; i++ )
 394  
             {
 395  0
                 array[i] = list.get( i );
 396  
             }
 397  
         }
 398  0
         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  47415116
         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  4311690
         if ( type == null || type.getGenericSuperclass() == null || !ParameterizedType.class.isInstance(
 423  
             type.getGenericSuperclass() ) || method.getDeclaringClass().getTypeParameters() == null )
 424  
         {
 425  4311631
             return getParameterTypes( method );
 426  
         }
 427  
 
 428  59
         GenericMethodParameterTypeCacheEntry key = new GenericMethodParameterTypeCacheEntry( method, type );
 429  59
         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  2950169
         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  0
         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  0
         OgnlRuntime.securityManager = securityManager;
 462  0
         cache.setSecurityManager(securityManager);
 463  0
     }
 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  43100001
         PermissionCacheEntry key = new PermissionCacheEntry( method );
 475  43100001
         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  1440
         if ( securityManager != null )
 484  
         {
 485  0
             if ( !cache.getMethodPerm( method ) )
 486  
             {
 487  0
                 throw new IllegalAccessException( "Method [" + method + "] cannot be accessed." );
 488  
             }
 489  
         }
 490  
 
 491  1440
         MethodAccessEntryValue entry = cache.getMethodAccess( method );
 492  1440
         if ( !entry.isAccessible() )
 493  
         {
 494  
             // only synchronize method invocation if it actually requires it
 495  20
             synchronized ( method )
 496  
             {
 497  
 
 498  20
                 if ( entry.isNotPublic() && !entry.isAccessible() )
 499  
                 {
 500  20
                     method.setAccessible( true );
 501  
                 }
 502  
 
 503  20
                 result = method.invoke( target, argsArray );
 504  
 
 505  20
                 if ( !entry.isAccessible() )
 506  
                 {
 507  20
                     method.setAccessible( false );
 508  
                 }
 509  20
             }
 510  
         }
 511  
         else
 512  
         {
 513  1420
             result = method.invoke( target, argsArray );
 514  
         }
 515  
 
 516  1439
         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  87
         if ( arg == null )
 530  
         {
 531  0
             return null;
 532  
         }
 533  87
         Class<?> clazz = arg.getClass();
 534  87
         if ( clazz == Boolean.class )
 535  
         {
 536  3
             return Boolean.TYPE;
 537  
         }
 538  84
         else if ( clazz.getSuperclass() == Number.class )
 539  
         {
 540  71
             if ( clazz == Integer.class )
 541  
             {
 542  57
                 return Integer.TYPE;
 543  
             }
 544  14
             if ( clazz == Double.class )
 545  
             {
 546  8
                 return Double.TYPE;
 547  
             }
 548  6
             if ( clazz == Byte.class )
 549  
             {
 550  0
                 return Byte.TYPE;
 551  
             }
 552  6
             if ( clazz == Long.class )
 553  
             {
 554  2
                 return Long.TYPE;
 555  
             }
 556  4
             if ( clazz == Float.class )
 557  
             {
 558  4
                 return Float.TYPE;
 559  
             }
 560  0
             if ( clazz == Short.class )
 561  
             {
 562  0
                 return Short.TYPE;
 563  
             }
 564  
         }
 565  13
         else if ( clazz == Character.class )
 566  
         {
 567  0
             return Character.TYPE;
 568  
         }
 569  13
         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  459
         boolean result = true;
 580  
 
 581  459
         if ( object != null )
 582  
         {
 583  441
             if ( clazz.isPrimitive() )
 584  
             {
 585  87
                 if ( getArgClass( object ) != clazz )
 586  
                 {
 587  34
                     result = false;
 588  
                 }
 589  
             }
 590  354
             else if ( !clazz.isInstance( object ) )
 591  
             {
 592  73
                 result = false;
 593  
             }
 594  
         }
 595  459
         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  223
         return areArgsCompatible( args, classes, null );
 606  
     }
 607  
 
 608  
     public static boolean areArgsCompatible( Object[] args, Class<?>[] classes, Method method )
 609  
     {
 610  653
         boolean result = true;
 611  653
         boolean varArgs = method != null && method.isVarArgs();
 612  
 
 613  653
         if ( args.length != classes.length && !varArgs )
 614  
         {
 615  154
             result = false;
 616  
         }
 617  499
         else if ( varArgs )
 618  
         {
 619  12
             for ( int index = 0; result && ( index < args.length ); ++index )
 620  
             {
 621  8
                 if ( index >= classes.length )
 622  
                 {
 623  1
                     break;
 624  
                 }
 625  
 
 626  7
                 result = isTypeCompatible( args[index], classes[index] );
 627  
 
 628  7
                 if ( !result && classes[index].isArray() )
 629  
                 {
 630  1
                     result = isTypeCompatible( args[index], classes[index].getComponentType() );
 631  
                 }
 632  
             }
 633  
         }
 634  
         else
 635  
         {
 636  920
             for ( int index = 0; result && ( index < args.length ); ++index )
 637  
             {
 638  426
                 result = isTypeCompatible( args[index], classes[index] );
 639  
             }
 640  
         }
 641  653
         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  59
         for ( int index = 0; index < classes1.length; ++index )
 651  
         {
 652  16
             Class<?> class1 = classes1[index], class2 = classes2[index];
 653  16
             if ( class1 != class2 )
 654  
             {
 655  5
                 if ( class1.isPrimitive() )
 656  
                 {
 657  0
                     return true;
 658  
                 }
 659  5
                 else if ( class1.isAssignableFrom( class2 ) )
 660  
                 {
 661  3
                     return false;
 662  
                 }
 663  2
                 else if ( class2.isAssignableFrom( class1 ) )
 664  
                 {
 665  0
                     return true;
 666  
                 }
 667  
             }
 668  
         }
 669  
 
 670  
         // They are the same! So the first is not more specific than the second.
 671  43
         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  0
         if ( Modifier.isPublic( modifiers ) )
 684  
         {
 685  0
             modifierString = "public";
 686  
         }
 687  0
         else if ( Modifier.isProtected( modifiers ) )
 688  
         {
 689  0
             modifierString = "protected";
 690  
         }
 691  0
         else if ( Modifier.isPrivate( modifiers ) )
 692  
         {
 693  0
             modifierString = "private";
 694  
         }
 695  
         else
 696  
         {
 697  0
             modifierString = "";
 698  
         }
 699  0
         if ( Modifier.isStatic( modifiers ) )
 700  
         {
 701  0
             modifierString = "static " + modifierString;
 702  
         }
 703  0
         if ( Modifier.isFinal( modifiers ) )
 704  
         {
 705  0
             modifierString = "final " + modifierString;
 706  
         }
 707  0
         if ( Modifier.isNative( modifiers ) )
 708  
         {
 709  0
             modifierString = "native " + modifierString;
 710  
         }
 711  0
         if ( Modifier.isSynchronized( modifiers ) )
 712  
         {
 713  0
             modifierString = "synchronized " + modifierString;
 714  
         }
 715  0
         if ( Modifier.isTransient( modifiers ) )
 716  
         {
 717  0
             modifierString = "transient " + modifierString;
 718  
         }
 719  0
         return modifierString;
 720  
     }
 721  
 
 722  
     public static Class<?> classForName( OgnlContext context, String className )
 723  
         throws ClassNotFoundException
 724  
     {
 725  376
         Class<?> result = primitiveTypes.get( className );
 726  
 
 727  376
         if ( result == null )
 728  
         {
 729  
             ClassResolver resolver;
 730  
 
 731  348
             if ( ( context == null ) || ( ( resolver = context.getClassResolver() ) == null ) )
 732  
             {
 733  50
                 resolver = OgnlContext.DEFAULT_CLASS_RESOLVER;
 734  
             }
 735  348
             result = resolver.classForName( className, context );
 736  
         }
 737  
 
 738  376
         if ( result == null )
 739  
         {
 740  1
             throw new ClassNotFoundException( "Unable to resolve class: " + className );
 741  
         }
 742  
 
 743  375
         return result;
 744  
     }
 745  
 
 746  
     public static boolean isInstance( OgnlContext context, Object value, String className )
 747  
         throws OgnlException
 748  
     {
 749  
         try
 750  
         {
 751  5
             Class<?> clazz = classForName( context, className );
 752  5
             return clazz.isInstance( value );
 753  
         }
 754  0
         catch ( ClassNotFoundException e )
 755  
         {
 756  0
             throw new OgnlException( "No such class: " + className, e );
 757  
         }
 758  
     }
 759  
 
 760  
     public static Object getPrimitiveDefaultValue( Class<?> forClass )
 761  
     {
 762  330000000
         return primitiveDefaults.get( forClass );
 763  
     }
 764  
 
 765  
     public static Object getNumericDefaultValue( Class<?> forClass )
 766  
     {
 767  0
         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  22
         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  14
         boolean result = false;
 781  
 
 782  14
         if ( parameterTypes.length == args.length )
 783  
         {
 784  14
             result = true;
 785  34
             for ( int i = 0; result && ( i <= parameterTypes.length - 1 ); i++ )
 786  
             {
 787  20
                 Object arg = args[i];
 788  20
                 Class<?> type = parameterTypes[i];
 789  
 
 790  20
                 if ( isTypeCompatible( arg, type ) )
 791  
                 {
 792  2
                     newArgs[i] = arg;
 793  
                 }
 794  
                 else
 795  
                 {
 796  18
                     Object convertedType = getConvertedType( context, target, member, propertyName, arg, type );
 797  
 
 798  18
                     if ( convertedType == OgnlRuntime.NoConversionPossible )
 799  
                     {
 800  0
                         result = false;
 801  
                     }
 802  
                     else
 803  
                     {
 804  18
                         newArgs[i] = convertedType;
 805  
                     }
 806  
                 }
 807  
             }
 808  
         }
 809  14
         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  19
         Method convertedMethod = null;
 817  19
         TypeConverter typeConverter = context.getTypeConverter();
 818  
 
 819  19
         if ( ( typeConverter != null ) && ( methods != null ) )
 820  
         {
 821  14
             int methodsSize = methods.size();
 822  28
             for ( int i = 0; ( convertedMethod == null ) && ( i < methodsSize ); i++ )
 823  
             {
 824  14
                 Method method = methods.get( i );
 825  14
                 Class<?>[] parameterTypes =
 826  
                     findParameterTypes( target != null ? target.getClass() : null, method );// getParameterTypes(method);
 827  
 
 828  14
                 if ( getConvertedTypes( context, target, method, propertyName, parameterTypes, args, newArgs ) )
 829  
                 {
 830  14
                     convertedMethod = method;
 831  
                 }
 832  
             }
 833  
         }
 834  19
         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  0
         Constructor<?> constructor = null;
 843  0
         TypeConverter typeConverter = context.getTypeConverter();
 844  
 
 845  0
         if ( ( typeConverter != null ) && ( constructors != null ) )
 846  
         {
 847  0
             for ( int i = 0; ( constructor == null ) && ( i < constructors.size() ); i++ )
 848  
             {
 849  0
                 Constructor<?> ctor = constructors.get( i );
 850  0
                 Class<?>[] parameterTypes = getParameterTypes( ctor );
 851  
 
 852  0
                 if ( getConvertedTypes( context, target, ctor, null, parameterTypes, args, newArgs ) )
 853  
                 {
 854  0
                     constructor = ctor;
 855  
                 }
 856  
             }
 857  
         }
 858  0
         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  289
         Method appropriateMethod = null;
 880  289
         Class<?>[] resultParameterTypes = null;
 881  
 
 882  289
         if ( methods != null )
 883  
         {
 884  284
             for ( Method method : methods )
 885  
             {
 886  430
                 Class<?> typeClass = target != null ? target.getClass() : null;
 887  430
                 if ( typeClass == null && source != null && Class.class.isInstance( source ) )
 888  
                 {
 889  57
                     typeClass = (Class<?>) source;
 890  
                 }
 891  
 
 892  430
                 Class<?>[] mParameterTypes = findParameterTypes( typeClass, method );
 893  
 
 894  430
                 if ( areArgsCompatible( args, mParameterTypes, method ) &&
 895  
                     ( ( appropriateMethod == null ) || isMoreSpecific( mParameterTypes, resultParameterTypes ) ) )
 896  
                 {
 897  270
                     appropriateMethod = method;
 898  270
                     resultParameterTypes = mParameterTypes;
 899  270
                     System.arraycopy( args, 0, actualArgs, 0, args.length );
 900  
 
 901  449
                     for ( int i = 0; i < mParameterTypes.length; i++ )
 902  
                     {
 903  179
                         Class<?> type = mParameterTypes[i];
 904  
 
 905  179
                         if ( type.isPrimitive() && ( actualArgs[i] == null ) )
 906  
                         {
 907  0
                             actualArgs[i] = getConvertedType( context, source, appropriateMethod, propertyName, null, type );
 908  
                         }
 909  
                     }
 910  
                 }
 911  430
             }
 912  
         }
 913  
 
 914  289
         if ( appropriateMethod == null )
 915  
         {
 916  19
             appropriateMethod = getConvertedMethodAndArgs( context, target, propertyName, methods, args, actualArgs );
 917  
         }
 918  
 
 919  289
         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  289
         Throwable cause = null;
 927  289
         Object[] actualArgs = objectArrayPool.create( args.length );
 928  
 
 929  
         try
 930  
         {
 931  289
             Method method = getAppropriateMethod( context, source, target, propertyName, methods, args, actualArgs );
 932  
 
 933  289
             if ( ( method == null ) || !isMethodAccessible( context, source, method, propertyName ) )
 934  
             {
 935  8
                 StringBuilder buffer = new StringBuilder();
 936  8
                 String className = "";
 937  
 
 938  8
                 if ( target != null )
 939  
                 {
 940  7
                     className = target.getClass().getName() + ".";
 941  
                 }
 942  
 
 943  11
                 for ( int i = 0, ilast = args.length - 1; i <= ilast; i++ )
 944  
                 {
 945  3
                     Object arg = args[i];
 946  
 
 947  3
                     buffer.append( ( arg == null ) ? NULL_STRING : arg.getClass().getName() );
 948  3
                     if ( i < ilast )
 949  
                     {
 950  0
                         buffer.append( ", " );
 951  
                     }
 952  
                 }
 953  
 
 954  8
                 throw new NoSuchMethodException( className + methodName + "(" + buffer + ")" );
 955  
             }
 956  
 
 957  281
             Object[] convertedArgs = actualArgs;
 958  
 
 959  281
             if ( method.isVarArgs() )
 960  
             {
 961  3
                 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  5
                 for ( int i = 0; i < parmTypes.length; i++ )
 968  
                 {
 969  5
                     if ( parmTypes[i].isArray() )
 970  
                     {
 971  3
                         convertedArgs = new Object[i + 1];
 972  3
                         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  3
                         if ( actualArgs.length > i )
 979  
                         {
 980  3
                             List<Object> varArgsList = new ArrayList<Object>();
 981  7
                             for ( int j = i; j < actualArgs.length; j++ )
 982  
                             {
 983  4
                                 if ( actualArgs[j] != null )
 984  
                                 {
 985  2
                                     varArgsList.add( actualArgs[j] );
 986  
                                 }
 987  
                             }
 988  
 
 989  3
                             varArgs = varArgsList.toArray();
 990  3
                         }
 991  
                         else
 992  
                         {
 993  0
                             varArgs = new Object[0];
 994  
                         }
 995  
 
 996  3
                         convertedArgs[i] = varArgs;
 997  3
                         break;
 998  
                     }
 999  
                 }
 1000  
             }
 1001  
 
 1002  281
             return invokeMethod( target, method, convertedArgs );
 1003  
 
 1004  
         }
 1005  8
         catch ( NoSuchMethodException e )
 1006  
         {
 1007  8
             cause = e;
 1008  
         }
 1009  0
         catch ( IllegalAccessException e )
 1010  
         {
 1011  0
             cause = e;
 1012  
         }
 1013  1
         catch ( InvocationTargetException e )
 1014  
         {
 1015  1
             cause = e.getTargetException();
 1016  
         }
 1017  
         finally
 1018  
         {
 1019  289
             objectArrayPool.recycle( actualArgs );
 1020  9
         }
 1021  
 
 1022  9
         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  28
             Class<?> targetClass = classForName( context, className );
 1031  27
             if ( targetClass == null )
 1032  
             {
 1033  0
                 throw new ClassNotFoundException( "Unable to resolve class with name " + className );
 1034  
             }
 1035  
 
 1036  27
             MethodAccessor methodAccessor = getMethodAccessor( targetClass );
 1037  
 
 1038  27
             return methodAccessor.callStaticMethod( context, targetClass, methodName, args );
 1039  
         }
 1040  1
         catch ( ClassNotFoundException ex )
 1041  
         {
 1042  1
             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  254
         if ( target == null )
 1060  
         {
 1061  3
             throw new NullPointerException( "target is null for method " + methodName );
 1062  
         }
 1063  
 
 1064  251
         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  78
         Throwable cause = null;
 1071  78
         Object[] actualArgs = args;
 1072  
 
 1073  
         try
 1074  
         {
 1075  78
             Constructor<?> ctor = null;
 1076  78
             Class<?>[] ctorParameterTypes = null;
 1077  78
             Class<?> target = classForName( context, className );
 1078  78
             List<Constructor<?>> constructors = getConstructors( target );
 1079  
 
 1080  78
             for ( Constructor<?> constructor : constructors )
 1081  
             {
 1082  169
                 Class<?>[] cParameterTypes = getParameterTypes( constructor );
 1083  
 
 1084  169
                 if ( areArgsCompatible( args, cParameterTypes ) && ( ctor == null || isMoreSpecific( cParameterTypes,
 1085  
                                                                                                      ctorParameterTypes ) ) )
 1086  
                 {
 1087  78
                     ctor = constructor;
 1088  78
                     ctorParameterTypes = cParameterTypes;
 1089  
                 }
 1090  169
             }
 1091  78
             if ( ctor == null )
 1092  
             {
 1093  0
                 actualArgs = objectArrayPool.create( args.length );
 1094  0
                 if ( ( ctor = getConvertedConstructorAndArgs( context, target, constructors, args, actualArgs ) )
 1095  
                     == null )
 1096  
                 {
 1097  0
                     throw new NoSuchMethodException();
 1098  
                 }
 1099  
             }
 1100  78
             if ( !context.getMemberAccess().isAccessible( context, target, ctor, null ) )
 1101  
             {
 1102  0
                 throw new IllegalAccessException( "access denied to " + target.getName() + "()" );
 1103  
             }
 1104  78
             return ctor.newInstance( actualArgs );
 1105  
         }
 1106  0
         catch ( ClassNotFoundException e )
 1107  
         {
 1108  0
             cause = e;
 1109  
         }
 1110  0
         catch ( NoSuchMethodException e )
 1111  
         {
 1112  0
             cause = e;
 1113  
         }
 1114  0
         catch ( IllegalAccessException e )
 1115  
         {
 1116  0
             cause = e;
 1117  
         }
 1118  0
         catch ( InvocationTargetException e )
 1119  
         {
 1120  0
             cause = e.getTargetException();
 1121  
         }
 1122  0
         catch ( InstantiationException e )
 1123  
         {
 1124  0
             cause = e;
 1125  
         }
 1126  
         finally
 1127  
         {
 1128  78
             if ( actualArgs != args )
 1129  
             {
 1130  0
                 objectArrayPool.recycle( actualArgs );
 1131  
             }
 1132  
         }
 1133  
 
 1134  0
         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  0
         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  1273
         Object methodValue = null;
 1152  1273
         Class<?> targetClass = target == null ? null : target.getClass();
 1153  1273
         Method method = getGetMethod( context, targetClass, propertyName );
 1154  1273
         if ( method == null )
 1155  
         {
 1156  104
             method = getReadMethod( targetClass, propertyName, 0 );
 1157  
         }
 1158  
 
 1159  1273
         if ( checkAccessAndExistence )
 1160  
         {
 1161  1273
             if ( ( method == null ) || !context.getMemberAccess().isAccessible( context, target, method, propertyName ) )
 1162  
             {
 1163  114
                 methodValue = NotFound;
 1164  
             }
 1165  
         }
 1166  1273
         if ( methodValue == null )
 1167  
         {
 1168  1159
             if ( method != null )
 1169  
             {
 1170  
                 try
 1171  
                 {
 1172  1159
                     methodValue = invokeMethod( target, method, NoArguments );
 1173  
                 }
 1174  0
                 catch ( InvocationTargetException ex )
 1175  
                 {
 1176  0
                     throw new OgnlException( propertyName, ex.getTargetException() );
 1177  1159
                 }
 1178  
             }
 1179  
             else
 1180  
             {
 1181  0
                 throw new NoSuchMethodException( propertyName );
 1182  
             }
 1183  
         }
 1184  1273
         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  0
         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  16
         boolean result = true;
 1198  16
         Method method = getSetMethod( context, ( target == null ) ? null : target.getClass(), propertyName );
 1199  
 
 1200  16
         if ( checkAccessAndExistence )
 1201  
         {
 1202  16
             if ( ( method == null ) || !context.getMemberAccess().isAccessible( context, target, method, propertyName ) )
 1203  
             {
 1204  5
                 result = false;
 1205  
             }
 1206  
         }
 1207  
 
 1208  16
         if ( result )
 1209  
         {
 1210  11
             if ( method != null )
 1211  
             {
 1212  11
                 Object[] args = objectArrayPool.create( value );
 1213  
 
 1214  
                 try
 1215  
                 {
 1216  11
                     callAppropriateMethod( context, target, target, method.getName(), propertyName,
 1217  
                                            Collections.nCopies( 1, method ), args );
 1218  
                 }
 1219  
                 finally
 1220  
                 {
 1221  11
                     objectArrayPool.recycle( args );
 1222  11
                 }
 1223  11
             }
 1224  
             else
 1225  
             {
 1226  0
                 result = false;
 1227  
             }
 1228  
         }
 1229  
 
 1230  16
         return result;
 1231  
     }
 1232  
 
 1233  
     public static List<Constructor<?>> getConstructors( Class<?> targetClass )
 1234  
     {
 1235  165000078
         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  132000495
         DeclaredMethodCacheEntry.MethodType type = staticMethods ?
 1246  
             DeclaredMethodCacheEntry.MethodType.STATIC :
 1247  
             DeclaredMethodCacheEntry.MethodType.NON_STATIC;
 1248  132000495
         DeclaredMethodCacheEntry key = new DeclaredMethodCacheEntry( targetClass, type );
 1249  132000495
         return cache.getMethod( key );
 1250  
     }
 1251  
 
 1252  
     public static List<Method> getMethods( Class<?> targetClass, String name, boolean staticMethods )
 1253  
     {
 1254  412
         return getMethods( targetClass, staticMethods ).get( name );
 1255  
     }
 1256  
 
 1257  
     public static Map<String, Field> getFields( Class<?> targetClass )
 1258  
     {
 1259  165000254
         return cache.getField( targetClass );
 1260  
     }
 1261  
 
 1262  
     public static Field getField( Class<?> inClass, String name )
 1263  
     {
 1264  120
         Field field = getFields( inClass ).get( name );
 1265  
 
 1266  120
         if ( field == null )
 1267  
         {
 1268  
             // if field is null, it should search along the superclasses
 1269  92
             Class<?> superClass = inClass.getSuperclass();
 1270  225
             while ( superClass != null )
 1271  
             {
 1272  134
                 field = getFields( superClass ).get( name );
 1273  134
                 if ( field != null )
 1274  
                 {
 1275  1
                     return field;
 1276  
                 }
 1277  133
                 superClass = superClass.getSuperclass();
 1278  
             }
 1279  
         }
 1280  119
         return field;
 1281  
     }
 1282  
 
 1283  
     public static Object getFieldValue( OgnlContext context, Object target, String propertyName )
 1284  
         throws NoSuchFieldException
 1285  
     {
 1286  0
         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  114
         Object result = null;
 1294  114
         Class<?> targetClass = target == null ? null : target.getClass();
 1295  114
         Field field = getField( targetClass, propertyName );
 1296  
 
 1297  114
         if ( checkAccessAndExistence )
 1298  
         {
 1299  114
             if ( ( field == null ) || !context.getMemberAccess().isAccessible( context, target, field, propertyName ) )
 1300  
             {
 1301  101
                 result = NotFound;
 1302  
             }
 1303  
         }
 1304  114
         if ( result == null )
 1305  
         {
 1306  13
             if ( field == null )
 1307  
             {
 1308  0
                 throw new NoSuchFieldException( propertyName );
 1309  
             }
 1310  
             try
 1311  
             {
 1312  
                 Object state;
 1313  
 
 1314  13
                 if ( !Modifier.isStatic( field.getModifiers() ) )
 1315  
                 {
 1316  13
                     state = context.getMemberAccess().setup( context, target, field, propertyName );
 1317  13
                     result = field.get( target );
 1318  13
                     context.getMemberAccess().restore( context, target, field, propertyName, state );
 1319  
                 }
 1320  
                 else
 1321  
                 {
 1322  0
                     throw new NoSuchFieldException( propertyName );
 1323  
                 }
 1324  
 
 1325  
             }
 1326  0
             catch ( IllegalAccessException ex )
 1327  
             {
 1328  0
                 throw new NoSuchFieldException( propertyName );
 1329  13
             }
 1330  
         }
 1331  114
         return result;
 1332  
     }
 1333  
 
 1334  
     public static boolean setFieldValue( OgnlContext context, Object target, String propertyName, Object value )
 1335  
         throws OgnlException
 1336  
     {
 1337  5
         boolean result = false;
 1338  
 
 1339  
         try
 1340  
         {
 1341  5
             Class<?> targetClass = target == null ? null : target.getClass();
 1342  5
             Field field = getField( targetClass, propertyName );
 1343  
             Object state;
 1344  
 
 1345  5
             if ( ( field != null ) && !Modifier.isStatic( field.getModifiers() ) )
 1346  
             {
 1347  5
                 state = context.getMemberAccess().setup( context, target, field, propertyName );
 1348  
                 try
 1349  
                 {
 1350  5
                     if ( isTypeCompatible( value, field.getType() ) || (
 1351  
                         ( value = getConvertedType( context, target, field, propertyName, value, field.getType() ) ) != null ) )
 1352  
                     {
 1353  5
                         field.set( target, value );
 1354  4
                         result = true;
 1355  
                     }
 1356  
                 }
 1357  
                 finally
 1358  
                 {
 1359  5
                     context.getMemberAccess().restore( context, target, field, propertyName, state );
 1360  4
                 }
 1361  
             }
 1362  
         }
 1363  1
         catch ( IllegalAccessException ex )
 1364  
         {
 1365  1
             throw new NoSuchPropertyException( target, propertyName, ex );
 1366  4
         }
 1367  4
         return result;
 1368  
     }
 1369  
 
 1370  
     public static boolean isFieldAccessible( OgnlContext context, Object target, Class<?> inClass, String propertyName )
 1371  
     {
 1372  0
         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  0
         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  0
         Field field = getField( inClass, propertyName );
 1383  
 
 1384  0
         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  50
             Class<?> clazz = classForName( context, className );
 1394  
 
 1395  50
             if ( clazz == null )
 1396  
             {
 1397  0
                 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  50
             if ( "class".equals( fieldName ) )
 1406  
             {
 1407  12
                 return clazz;
 1408  
             }
 1409  38
             else if ( clazz.isEnum() )
 1410  
             {
 1411  7
                 return Enum.valueOf( (Class<? extends Enum>) clazz, fieldName );
 1412  
             }
 1413  
             else
 1414  
             {
 1415  31
                 Field field = clazz.getField( fieldName );
 1416  31
                 if ( !Modifier.isStatic(field.getModifiers()) )
 1417  
                 {
 1418  0
                     throw new OgnlException( "Field " + fieldName + " of class " + className + " is not static" );
 1419  
                 }
 1420  
 
 1421  31
                 return field.get( null );
 1422  
             }
 1423  
         }
 1424  0
         catch ( ClassNotFoundException e )
 1425  
         {
 1426  0
             cause = e;
 1427  
         }
 1428  0
         catch ( NoSuchFieldException e )
 1429  
         {
 1430  0
             cause = e;
 1431  
         }
 1432  0
         catch ( SecurityException e )
 1433  
         {
 1434  0
             cause = e;
 1435  
         }
 1436  0
         catch ( IllegalAccessException e )
 1437  
         {
 1438  0
             cause = e;
 1439  0
         }
 1440  
 
 1441  0
         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  6601299
         String baseName = Character.toUpperCase( propertyName.charAt( 0 ) ) + propertyName.substring( 1 );
 1454  6601299
         List<Method> methods = new ArrayList<Method>();
 1455  6601299
         List<String> methodNames = new ArrayList<String>( 2 );
 1456  6601299
         if ( findSets )
 1457  
         {
 1458  3300021
             methodNames.add( SET_PREFIX + baseName );
 1459  
         }
 1460  
         else
 1461  
         {
 1462  3301278
             methodNames.add( IS_PREFIX + baseName );
 1463  3301278
             methodNames.add( GET_PREFIX + baseName );
 1464  
         }
 1465  6601299
         for ( String methodName : methodNames )
 1466  
         {
 1467  9902577
             DeclaredMethodCacheEntry key = new DeclaredMethodCacheEntry( targetClass );
 1468  9902577
             List<Method> methodList = cache.getMethod( key ).get( methodName );
 1469  9902577
             if ( methodList != null )
 1470  
             {
 1471  2701243
                 methods.addAll( methodList );
 1472  
             }
 1473  9902577
         }
 1474  
 
 1475  6601299
         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  66645
         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  1275
         Method result = null;
 1495  
 
 1496  1275
         List<Method> methods = getDeclaredMethods( targetClass, propertyName, false /* find 'get' methods */ );
 1497  
 
 1498  1275
         if ( methods != null )
 1499  
         {
 1500  1275
             for ( Method method : methods )
 1501  
             {
 1502  1228
                 Class<?>[] mParameterTypes = findParameterTypes( targetClass, method ); // getParameterTypes(method);
 1503  
 
 1504  1228
                 if ( mParameterTypes.length == 0 )
 1505  
                 {
 1506  1171
                     result = method;
 1507  1171
                     break;
 1508  
                 }
 1509  57
             }
 1510  
         }
 1511  
 
 1512  1275
         return result;
 1513  
     }
 1514  
 
 1515  
     public static boolean isMethodAccessible( OgnlContext context, Object target, Method method, String propertyName )
 1516  
     {
 1517  284
         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  0
         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  20
         Method setMethod = null;
 1530  
 
 1531  20
         List<Method> methods = getDeclaredMethods( targetClass, propertyName, true /* find 'set' methods */ );
 1532  
 
 1533  20
         if ( methods != null )
 1534  
         {
 1535  20
             for ( Method method : methods )
 1536  
             {
 1537  14
                 Class<?>[] mParameterTypes = findParameterTypes( targetClass, method ); // getParameterTypes(method);
 1538  
 
 1539  14
                 if ( mParameterTypes.length == 1 )
 1540  
                 {
 1541  14
                     setMethod = method;
 1542  14
                     break;
 1543  
                 }
 1544  0
             }
 1545  
         }
 1546  
 
 1547  20
         return setMethod;
 1548  
     }
 1549  
 
 1550  
     public static boolean hasSetMethod( OgnlContext context, Object target, Class<?> targetClass, String propertyName )
 1551  
         throws IntrospectionException, OgnlException
 1552  
     {
 1553  0
         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  0
         Class<?> targetClass = ( target == null ) ? null : target.getClass();
 1560  0
         String name = oname.toString();
 1561  
 
 1562  0
         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  0
         Class<?> targetClass = ( target == null ) ? null : target.getClass();
 1569  0
         String name = oname.toString();
 1570  
 
 1571  0
         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  1791
         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  1791
         if ( targetClass == null )
 1601  
         {
 1602  1
             return null;
 1603  
         }
 1604  
 
 1605  1790
         return getPropertyDescriptors( targetClass ).get( propertyName );
 1606  
     }
 1607  
 
 1608  
     public static PropertyDescriptor[] getPropertyDescriptorsArray( Class<?> targetClass )
 1609  
         throws IntrospectionException, OgnlException
 1610  
     {
 1611  1
         Collection<PropertyDescriptor> propertyDescriptors = getPropertyDescriptors( targetClass ).values();
 1612  1
         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  1
         PropertyDescriptor result = null;
 1628  1
         PropertyDescriptor[] propertyDescriptors = getPropertyDescriptorsArray( targetClass );
 1629  
 
 1630  52
         for ( PropertyDescriptor propertyDescriptor : propertyDescriptors )
 1631  
         {
 1632  51
             if ( result != null )
 1633  
             {
 1634  0
                 break;
 1635  
             }
 1636  51
             if ( propertyDescriptor.getName().compareTo( name ) == 0 )
 1637  
             {
 1638  1
                 result = propertyDescriptor;
 1639  
             }
 1640  
         }
 1641  1
         return result;
 1642  
     }
 1643  
 
 1644  
     public static void setMethodAccessor( Class<?> clazz, MethodAccessor accessor )
 1645  
     {
 1646  0
         cache.setMethodAccessor( clazz, accessor );
 1647  0
     }
 1648  
 
 1649  
     public static MethodAccessor getMethodAccessor( Class<?> clazz )
 1650  
         throws OgnlException
 1651  
     {
 1652  278
         return cache.getMethodAccessor( clazz );
 1653  
     }
 1654  
 
 1655  
     public static void setPropertyAccessor( Class<?> clazz, PropertyAccessor accessor )
 1656  
     {
 1657  23
         cache.setPropertyAccessor( clazz, accessor );
 1658  23
     }
 1659  
 
 1660  
     public static PropertyAccessor getPropertyAccessor( Class<?> clazz )
 1661  
         throws OgnlException
 1662  
     {
 1663  2406
         return cache.getPropertyAccessor( clazz );
 1664  
     }
 1665  
 
 1666  
     public static ElementsAccessor getElementsAccessor( Class<?> clazz )
 1667  
         throws OgnlException
 1668  
     {
 1669  14
         return cache.getElementsAccessor( clazz );
 1670  
     }
 1671  
 
 1672  
     public static void setElementsAccessor( Class<?> clazz, ElementsAccessor accessor )
 1673  
     {
 1674  0
         cache.setElementsAccessor( clazz, accessor );
 1675  0
     }
 1676  
 
 1677  
     public static NullHandler getNullHandler( Class<?> clazz )
 1678  
         throws OgnlException
 1679  
     {
 1680  100
         return cache.getNullHandler( clazz );
 1681  
     }
 1682  
 
 1683  
     public static void setNullHandler( Class<?> clazz, NullHandler handler )
 1684  
     {
 1685  4
         cache.setNullHandler( clazz, handler );
 1686  4
     }
 1687  
 
 1688  
     public static Object getProperty( OgnlContext context, Object source, Object name )
 1689  
         throws OgnlException
 1690  
     {
 1691  
         PropertyAccessor accessor;
 1692  
 
 1693  1506
         if ( source == null )
 1694  
         {
 1695  2
             throw new OgnlException( "source is null for getProperty(null, \"" + name + "\")" );
 1696  
         }
 1697  1504
         if ( ( accessor = getPropertyAccessor( getTargetClass( source ) ) ) == null )
 1698  
         {
 1699  0
             throw new OgnlException( "No property accessor for " + getTargetClass( source ).getName() );
 1700  
         }
 1701  
 
 1702  1504
         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  27
         if ( target == null )
 1711  
         {
 1712  0
             throw new OgnlException( "target is null for setProperty(null, \"" + name + "\", " + value + ")" );
 1713  
         }
 1714  27
         if ( ( accessor = getPropertyAccessor( getTargetClass( target ) ) ) == null )
 1715  
         {
 1716  0
             throw new OgnlException( "No property accessor for " + getTargetClass( target ).getName() );
 1717  
         }
 1718  
 
 1719  27
         accessor.setProperty( context, target, name, value );
 1720  26
     }
 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  961
         int result = INDEXED_PROPERTY_NONE;
 1733  
 
 1734  
         try
 1735  
         {
 1736  961
             PropertyDescriptor propertyDescriptor = getPropertyDescriptor( sourceClass, name );
 1737  961
             if ( propertyDescriptor != null )
 1738  
             {
 1739  845
                 if ( propertyDescriptor instanceof IndexedPropertyDescriptor )
 1740  
                 {
 1741  16
                     result = INDEXED_PROPERTY_INT;
 1742  
                 }
 1743  
                 else
 1744  
                 {
 1745  829
                     if ( propertyDescriptor instanceof ObjectIndexedPropertyDescriptor )
 1746  
                     {
 1747  34
                         result = INDEXED_PROPERTY_OBJECT;
 1748  
                     }
 1749  
                 }
 1750  
             }
 1751  
         }
 1752  0
         catch ( Exception ex )
 1753  
         {
 1754  0
             throw new OgnlException( "problem determining if '" + name + "' is an indexed property", ex );
 1755  961
         }
 1756  961
         return result;
 1757  
     }
 1758  
 
 1759  
     public static Object getIndexedProperty( OgnlContext context, Object source, String name, Object index )
 1760  
         throws OgnlException
 1761  
     {
 1762  17
         Object[] args = objectArrayPool.create( index );
 1763  
 
 1764  
         try
 1765  
         {
 1766  17
             PropertyDescriptor propertyDescriptor = getPropertyDescriptor( ( source == null ) ? null : source.getClass(), name );
 1767  
             Method method;
 1768  
 
 1769  17
             if ( propertyDescriptor instanceof IndexedPropertyDescriptor )
 1770  
             {
 1771  7
                 method = ( (IndexedPropertyDescriptor) propertyDescriptor ).getIndexedReadMethod();
 1772  
             }
 1773  
             else
 1774  
             {
 1775  10
                 if ( propertyDescriptor instanceof ObjectIndexedPropertyDescriptor )
 1776  
                 {
 1777  7
                     method = ( (ObjectIndexedPropertyDescriptor) propertyDescriptor ).getIndexedReadMethod();
 1778  
                 }
 1779  
                 else
 1780  
                 {
 1781  3
                     throw new OgnlException( "property '" + name + "' is not an indexed property" );
 1782  
                 }
 1783  
             }
 1784  
 
 1785  14
             return callMethod( context, source, method.getName(), args );
 1786  
 
 1787  
         }
 1788  3
         catch ( OgnlException ex )
 1789  
         {
 1790  3
             throw ex;
 1791  
         }
 1792  0
         catch ( Exception ex )
 1793  
         {
 1794  0
             throw new OgnlException( "getting indexed property descriptor for '" + name + "'", ex );
 1795  
         }
 1796  
         finally
 1797  
         {
 1798  17
             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  1
         Object[] args = objectArrayPool.create( index, value );
 1806  
 
 1807  
         try
 1808  
         {
 1809  1
             PropertyDescriptor propertyDescriptor = getPropertyDescriptor( ( source == null ) ? null : source.getClass(), name );
 1810  
             Method method;
 1811  
 
 1812  1
             if ( propertyDescriptor instanceof IndexedPropertyDescriptor )
 1813  
             {
 1814  1
                 method = ( (IndexedPropertyDescriptor) propertyDescriptor ).getIndexedWriteMethod();
 1815  
             }
 1816  
             else
 1817  
             {
 1818  0
                 if ( propertyDescriptor instanceof ObjectIndexedPropertyDescriptor )
 1819  
                 {
 1820  0
                     method = ( (ObjectIndexedPropertyDescriptor) propertyDescriptor ).getIndexedWriteMethod();
 1821  
                 }
 1822  
                 else
 1823  
                 {
 1824  0
                     throw new OgnlException( "property '" + name + "' is not an indexed property" );
 1825  
                 }
 1826  
             }
 1827  
 
 1828  1
             callMethod( context, source, method.getName(), args );
 1829  
 
 1830  
         }
 1831  0
         catch ( OgnlException ex )
 1832  
         {
 1833  0
             throw ex;
 1834  
         }
 1835  0
         catch ( Exception ex )
 1836  
         {
 1837  0
             throw new OgnlException( "getting indexed property descriptor for '" + name + "'", ex );
 1838  
         }
 1839  
         finally
 1840  
         {
 1841  1
             objectArrayPool.recycle( args );
 1842  1
         }
 1843  1
     }
 1844  
 
 1845  
     public static EvaluationPool getEvaluationPool()
 1846  
     {
 1847  0
         return evaluationPool;
 1848  
     }
 1849  
 
 1850  
     public static ObjectArrayPool getObjectArrayPool()
 1851  
     {
 1852  688
         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  1
         cache.setClassCacheInspector( inspector );
 1864  1
     }
 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  128
         if ( children != null && children.length > 0 )
 1872  
         {
 1873  69
             parms = new Class[children.length];
 1874  
 
 1875  
             // used to reset context after loop
 1876  69
             Class<?> currType = context.getCurrentType();
 1877  69
             Class<?> currAccessor = context.getCurrentAccessor();
 1878  69
             Object cast = context.get( ExpressionCompiler.PRE_CAST );
 1879  
 
 1880  69
             context.setCurrentObject( context.getRoot() );
 1881  69
             context.setCurrentType( context.getRoot() != null ? context.getRoot().getClass() : null );
 1882  69
             context.setCurrentAccessor( null );
 1883  69
             context.setPreviousType( null );
 1884  
 
 1885  158
             for ( int i = 0; i < children.length; i++ )
 1886  
             {
 1887  89
                 children[i].toGetSourceString( context, context.getRoot() );
 1888  89
                 parms[i] = context.getCurrentType();
 1889  
             }
 1890  
 
 1891  69
             context.put( ExpressionCompiler.PRE_CAST, cast );
 1892  
 
 1893  69
             context.setCurrentType( currType );
 1894  69
             context.setCurrentAccessor( currAccessor );
 1895  69
             context.setCurrentObject( target );
 1896  69
         }
 1897  
         else
 1898  
         {
 1899  59
             parms = new Class[0];
 1900  
         }
 1901  
 
 1902  128
         List<Method> methods = OgnlRuntime.getMethods( target, name, includeStatic );
 1903  128
         if ( methods == null )
 1904  
         {
 1905  17
             return null;
 1906  
         }
 1907  
 
 1908  111
         for ( Method method : methods )
 1909  
         {
 1910  140
             boolean varArgs = method.isVarArgs();
 1911  
 
 1912  140
             if ( parms.length != method.getParameterTypes().length && !varArgs )
 1913  
             {
 1914  14
                 continue;
 1915  
             }
 1916  
 
 1917  126
             Class<?>[] methodParameterTypes = method.getParameterTypes();
 1918  126
             boolean matched = true;
 1919  184
             for ( int i = 0; i < methodParameterTypes.length; i++ )
 1920  
             {
 1921  93
                 Class<?> methodParameterType = methodParameterTypes[i];
 1922  93
                 if ( varArgs && methodParameterType.isArray() )
 1923  
                 {
 1924  1
                     continue;
 1925  
                 }
 1926  
 
 1927  92
                 Class<?> parm = parms[i];
 1928  92
                 if ( parm == null )
 1929  
                 {
 1930  2
                     matched = false;
 1931  2
                     break;
 1932  
                 }
 1933  
 
 1934  90
                 if ( parm == methodParameterType || methodParameterType.isPrimitive() && Character.TYPE != methodParameterType && Byte.TYPE != methodParameterType
 1935  
                     && Number.class.isAssignableFrom(parm)
 1936  
                     && OgnlRuntime.getPrimitiveWrapperClass(parm) == methodParameterType)
 1937  
                 {
 1938  0
                     continue;
 1939  
                 }
 1940  
 
 1941  33
                 matched = false;
 1942  33
                 break;
 1943  
             }
 1944  
 
 1945  126
             if ( matched )
 1946  
             {
 1947  91
                 return method;
 1948  
             }
 1949  35
         }
 1950  
 
 1951  20
         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  130
         return getReadMethod( target, name, -1 );
 1967  
     }
 1968  
 
 1969  
     public static Method getReadMethod( Class<?> target, String name, int numParms )
 1970  
     {
 1971  
         try
 1972  
         {
 1973  560
             name = name.replaceAll( "\"", "" ).toLowerCase();
 1974  
 
 1975  560
             BeanInfo info = Introspector.getBeanInfo( target );
 1976  560
             MethodDescriptor[] methodDescriptors = info.getMethodDescriptors();
 1977  
 
 1978  
             // exact matches first
 1979  
 
 1980  560
             Method method = null;
 1981  
 
 1982  26984
             for ( MethodDescriptor methodDescriptor : methodDescriptors )
 1983  
             {
 1984  26449
                 if ( !isMethodCallable( methodDescriptor.getMethod() ) )
 1985  
                 {
 1986  34
                     continue;
 1987  
                 }
 1988  
 
 1989  26415
                 String methodName = methodDescriptor.getName();
 1990  26415
                 String lowerMethodName = methodName.toLowerCase();
 1991  26415
                 int methodParamLen = methodDescriptor.getMethod().getParameterTypes().length;
 1992  
 
 1993  26415
                 if ( ( methodName.equalsIgnoreCase( name ) || lowerMethodName.equals( "get" + name )
 1994  
                     || lowerMethodName.equals( "has" + name ) || lowerMethodName.equals( "is" + name ) )
 1995  
                     && !methodName.startsWith( "set" ) )
 1996  
                 {
 1997  244
                     if ( numParms > 0 && methodParamLen == numParms )
 1998  
                     {
 1999  20
                         return methodDescriptor.getMethod();
 2000  
                     }
 2001  224
                     else if ( numParms < 0 )
 2002  
                     {
 2003  131
                         if ( methodName.equals( name ) )
 2004  
                         {
 2005  5
                             return methodDescriptor.getMethod();
 2006  
                         }
 2007  126
                         else if ( method == null || ( method.getParameterTypes().length > methodParamLen ) )
 2008  
                         {
 2009  124
                             method = methodDescriptor.getMethod();
 2010  
                         }
 2011  
                     }
 2012  
                 }
 2013  
             }
 2014  
 
 2015  535
             if ( method != null )
 2016  
             {
 2017  123
                 return method;
 2018  
             }
 2019  
 
 2020  19678
             for ( MethodDescriptor methodDescriptor : methodDescriptors )
 2021  
             {
 2022  19266
                 if ( !isMethodCallable( methodDescriptor.getMethod() ) )
 2023  
                 {
 2024  32
                     continue;
 2025  
                 }
 2026  
 
 2027  19234
                 if ( methodDescriptor.getName().toLowerCase().endsWith( name ) && !methodDescriptor.getName().startsWith( "set" )
 2028  
                     && methodDescriptor.getMethod().getReturnType() != Void.TYPE )
 2029  
                 {
 2030  
 
 2031  145
                     if ( numParms > 0 && methodDescriptor.getMethod().getParameterTypes().length == numParms )
 2032  
                     {
 2033  0
                         return methodDescriptor.getMethod();
 2034  
                     }
 2035  145
                     else if ( numParms < 0 )
 2036  
                     {
 2037  0
                         if ( ( method != null
 2038  
                             && method.getParameterTypes().length > methodDescriptor.getMethod().getParameterTypes().length )
 2039  
                             || method == null )
 2040  
                         {
 2041  0
                             method = methodDescriptor.getMethod();
 2042  
                         }
 2043  
                     }
 2044  
                 }
 2045  
             }
 2046  
 
 2047  412
             if ( method != null )
 2048  
             {
 2049  0
                 return method;
 2050  
             }
 2051  
 
 2052  
             // try one last time adding a get to beginning
 2053  
 
 2054  412
             if ( !name.startsWith( "get" ) )
 2055  
             {
 2056  204
                 return OgnlRuntime.getReadMethod( target, "get" + name, numParms );
 2057  
             }
 2058  
 
 2059  
         }
 2060  0
         catch ( Throwable t )
 2061  
         {
 2062  0
             throw OgnlOps.castToRuntime( t );
 2063  208
         }
 2064  
 
 2065  208
         return null;
 2066  
     }
 2067  
 
 2068  
     public static Method getWriteMethod( Class<?> target, String name )
 2069  
     {
 2070  118
         return getWriteMethod( target, name, -1 );
 2071  
     }
 2072  
 
 2073  
     public static Method getWriteMethod( Class<?> target, String name, int numParms )
 2074  
     {
 2075  
         try
 2076  
         {
 2077  172
             name = name.replaceAll( "\"", "" );
 2078  
 
 2079  172
             BeanInfo info = Introspector.getBeanInfo( target );
 2080  172
             MethodDescriptor[] methods = info.getMethodDescriptors();
 2081  
 
 2082  5947
             for ( MethodDescriptor method : methods )
 2083  
             {
 2084  5858
                 if ( !isMethodCallable( method.getMethod() ) )
 2085  
                 {
 2086  0
                     continue;
 2087  
                 }
 2088  
 
 2089  5858
                 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  86
                     if ( numParms > 0 && method.getMethod().getParameterTypes().length == numParms )
 2095  
                     {
 2096  18
                         return method.getMethod();
 2097  
                     }
 2098  68
                     else if ( numParms < 0 )
 2099  
                     {
 2100  65
                         return method.getMethod();
 2101  
                     }
 2102  
                 }
 2103  
             }
 2104  
 
 2105  
             // try again on pure class
 2106  
 
 2107  89
             Method[] cmethods = target.getClass().getMethods();
 2108  5874
             for ( Method cmethod : cmethods )
 2109  
             {
 2110  5785
                 if ( !isMethodCallable( cmethod ) )
 2111  
                 {
 2112  0
                     continue;
 2113  
                 }
 2114  
 
 2115  5785
                 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  0
                     if ( numParms > 0 && cmethod.getParameterTypes().length == numParms )
 2121  
                     {
 2122  0
                         return cmethod;
 2123  
                     }
 2124  0
                     else if ( numParms < 0 )
 2125  
                     {
 2126  0
                         return cmethod;
 2127  
                     }
 2128  
                 }
 2129  
             }
 2130  
 
 2131  
             // try one last time adding a set to beginning
 2132  
 
 2133  89
             if ( !name.startsWith( "set" ) )
 2134  
             {
 2135  87
                 return OgnlRuntime.getReadMethod( target, "set" + name, numParms );
 2136  
             }
 2137  
 
 2138  
         }
 2139  0
         catch ( Throwable t )
 2140  
         {
 2141  0
             throw OgnlOps.castToRuntime( t );
 2142  2
         }
 2143  
 
 2144  2
         return null;
 2145  
     }
 2146  
 
 2147  
     public static PropertyDescriptor getProperty( Class<?> target, String name )
 2148  
     {
 2149  
         try
 2150  
         {
 2151  7
             BeanInfo info = Introspector.getBeanInfo( target );
 2152  
 
 2153  7
             PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors();
 2154  
 
 2155  14
             for ( PropertyDescriptor propertyDescriptor : propertyDescriptors )
 2156  
             {
 2157  
 
 2158  7
                 String propertyDescriptorName = propertyDescriptor.getName();
 2159  7
                 if ( propertyDescriptorName.equalsIgnoreCase( name ) || propertyDescriptorName.toLowerCase().equals( name.toLowerCase() )
 2160  
                     || propertyDescriptorName.toLowerCase().endsWith( name.toLowerCase() ) )
 2161  
                 {
 2162  0
                     return propertyDescriptor;
 2163  
                 }
 2164  
             }
 2165  
 
 2166  
         }
 2167  0
         catch ( Throwable t )
 2168  
         {
 2169  0
             throw OgnlOps.castToRuntime( t );
 2170  7
         }
 2171  
 
 2172  7
         return null;
 2173  
     }
 2174  
 
 2175  
     public static boolean isBoolean( String expression )
 2176  
     {
 2177  118
         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  28
         Class<?> currentType = context.getCurrentType();
 2199  28
         Class<?> previousType = context.getPreviousType();
 2200  28
         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  350
         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  406
         String pre = (String) context.get( "_currentChain" );
 2238  406
         if ( pre == null )
 2239  
         {
 2240  388
             pre = "";
 2241  
         }
 2242  
 
 2243  
         try
 2244  
         {
 2245  406
             child.getValue( context, target );
 2246  
         }
 2247  1
         catch ( NullPointerException e )
 2248  
         {
 2249  
             // ignore
 2250  
         }
 2251  0
         catch ( ArithmeticException e )
 2252  
         {
 2253  0
             context.setCurrentType( int.class );
 2254  0
             return "0";
 2255  
         }
 2256  1
         catch ( Throwable t )
 2257  
         {
 2258  1
             throw OgnlOps.castToRuntime( t );
 2259  405
         }
 2260  
 
 2261  
         String source;
 2262  
 
 2263  
         try
 2264  
         {
 2265  405
             source = child.toGetSourceString( context, target );
 2266  
         }
 2267  9
         catch ( Throwable t )
 2268  
         {
 2269  9
             throw OgnlOps.castToRuntime( t );
 2270  396
         }
 2271  
 
 2272  
         // handle root / method expressions that may not have proper root java source access
 2273  
 
 2274  396
         if ( !ASTConst.class.isInstance( child ) && ( target == null || context.getRoot() != target ) )
 2275  
         {
 2276  30
             source = pre + source;
 2277  
         }
 2278  
 
 2279  396
         if ( context.getRoot() != null )
 2280  
         {
 2281  257
             source = ExpressionCompiler.getRootExpression( child, context.getRoot(), context ) + source;
 2282  257
             context.setCurrentAccessor( context.getRoot().getClass() );
 2283  
         }
 2284  
 
 2285  396
         if ( ASTChain.class.isInstance( child ) )
 2286  
         {
 2287  33
             String cast = (String) context.remove( ExpressionCompiler.PRE_CAST );
 2288  33
             if ( cast == null )
 2289  
             {
 2290  15
                 cast = "";
 2291  
             }
 2292  
 
 2293  33
             source = cast + source;
 2294  
         }
 2295  
 
 2296  396
         if ( source == null || source.trim().length() < 1 )
 2297  
         {
 2298  3
             source = "null";
 2299  
         }
 2300  
 
 2301  396
         return source;
 2302  
     }
 2303  
 }