// --------------------------------------------------------------------------
// Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// Neither the name of the Drew Davidson nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
// --------------------------------------------------------------------------
package ognl;
import ognl.enhance.
ExpressionCompiler;
import ognl.enhance.
OgnlExpressionCompiler;
import ognl.internal.
ClassCache;
import ognl.internal.
ClassCacheImpl;
import java.beans.*;
import java.lang.reflect.*;
import java.math.
BigDecimal;
import java.math.
BigInteger;
import java.security.
Permission;
import java.util.*;
/**
* Utility class used by internal OGNL API to do various things like:
*
* <ul>
* <li>Handles majority of reflection logic / caching. </li>
* <li>Utility methods for casting strings / various numeric types used by {@link OgnlExpressionCompiler}.</li.
* <li>Core runtime configuration point for setting/using global {@link TypeConverter} / {@link OgnlExpressionCompiler} /
* {@link NullHandler} instances / etc.. </li>
*</ul>
*
* @author Luke Blanshard (blanshlu@netscape.net)
* @author Drew Davidson (drew@ognl.org)
*/
public class
OgnlRuntime {
/**
* Constant expression used to indicate that a given method / property couldn't be found
* during reflection operations.
*/
public static final
Object NotFound = new
Object();
public static final
List NotFoundList = new
ArrayList();
public static final
Map NotFoundMap = new
HashMap();
public static final
Object[]
NoArguments = new
Object[]{};
public static final
Class[]
NoArgumentTypes = new
Class[]{};
/**
* Token returned by TypeConverter for no conversion possible
*/
public static final
Object NoConversionPossible = "ognl.NoConversionPossible";
/**
* Not an indexed property
*/
public static int
INDEXED_PROPERTY_NONE = 0;
/**
* JavaBeans IndexedProperty
*/
public static int
INDEXED_PROPERTY_INT = 1;
/**
* OGNL ObjectIndexedProperty
*/
public static int
INDEXED_PROPERTY_OBJECT = 2;
/**
* Constant string representation of null string.
*/
public static final
String NULL_STRING = "" + null;
/**
* Java beans standard set method prefix.
*/
private static final
String SET_PREFIX = "set";
/**
* Java beans standard get method prefix.
*/
private static final
String GET_PREFIX = "get";
/**
* Java beans standard is<Foo> boolean getter prefix.
*/
private static final
String IS_PREFIX = "is";
/**
* Prefix padding for hexadecimal numbers to HEX_LENGTH.
*/
private static final
Map HEX_PADDING = new
HashMap();
private static final int
HEX_LENGTH = 8;
/**
* Returned by <CODE>getUniqueDescriptor()</CODE> when the object is <CODE>null</CODE>.
*/
private static final
String NULL_OBJECT_STRING = "<null>";
/**
* Used to store the result of determining if current jvm is 1.5 language compatible.
*/
private static boolean
_jdk15 = false;
private static boolean
_jdkChecked = false;
static final
ClassCache _methodAccessors = new
ClassCacheImpl();
static final
ClassCache _propertyAccessors = new
ClassCacheImpl();
static final
ClassCache _elementsAccessors = new
ClassCacheImpl();
static final
ClassCache _nullHandlers = new
ClassCacheImpl();
static final
ClassCache _propertyDescriptorCache = new
ClassCacheImpl();
static final
ClassCache _constructorCache = new
ClassCacheImpl();
static final
ClassCache _staticMethodCache = new
ClassCacheImpl();
static final
ClassCache _instanceMethodCache = new
ClassCacheImpl();
static final
ClassCache _invokePermissionCache = new
ClassCacheImpl();
static final
ClassCache _fieldCache = new
ClassCacheImpl();
static final
List _superclasses = new
ArrayList(); /* Used by fieldCache lookup */
static final
ClassCache[]
_declaredMethods = new
ClassCache[]{new
ClassCacheImpl(), new
ClassCacheImpl()};
static final
Map _primitiveTypes = new
HashMap(101);
static final
ClassCache _primitiveDefaults = new
ClassCacheImpl();
static final
Map _methodParameterTypesCache = new
HashMap(101);
static final
Map _genericMethodParameterTypesCache = new
HashMap(101);
static final
Map _ctorParameterTypesCache = new
HashMap(101);
static
SecurityManager _securityManager =
System.
getSecurityManager();
static final
EvaluationPool _evaluationPool = new
EvaluationPool();
static final
ObjectArrayPool _objectArrayPool = new
ObjectArrayPool();
static final
IntHashMap _methodAccessCache = new
IntHashMap();
static final
IntHashMap _methodPermCache = new
IntHashMap();
static
ClassCacheInspector _cacheInspector;
/**
* Expression compiler used by {@link Ognl#compileExpression(OgnlContext, Object, String)} calls.
*/
private static
OgnlExpressionCompiler _compiler = new
ExpressionCompiler();
private static
IdentityHashMap PRIMITIVE_WRAPPER_CLASSES = new
IdentityHashMap();
/**
* Used to provide primitive type equivalent conversions into and out of
* native / object types.
*/
static {
PRIMITIVE_WRAPPER_CLASSES.
put(
Boolean.
TYPE,
Boolean.class);
PRIMITIVE_WRAPPER_CLASSES.
put(
Boolean.class,
Boolean.
TYPE);
PRIMITIVE_WRAPPER_CLASSES.
put(
Byte.
TYPE,
Byte.class);
PRIMITIVE_WRAPPER_CLASSES.
put(
Byte.class,
Byte.
TYPE);
PRIMITIVE_WRAPPER_CLASSES.
put(
Character.
TYPE,
Character.class);
PRIMITIVE_WRAPPER_CLASSES.
put(
Character.class,
Character.
TYPE);
PRIMITIVE_WRAPPER_CLASSES.
put(
Short.
TYPE,
Short.class);
PRIMITIVE_WRAPPER_CLASSES.
put(
Short.class,
Short.
TYPE);
PRIMITIVE_WRAPPER_CLASSES.
put(
Integer.
TYPE,
Integer.class);
PRIMITIVE_WRAPPER_CLASSES.
put(
Integer.class,
Integer.
TYPE);
PRIMITIVE_WRAPPER_CLASSES.
put(
Long.
TYPE,
Long.class);
PRIMITIVE_WRAPPER_CLASSES.
put(
Long.class,
Long.
TYPE);
PRIMITIVE_WRAPPER_CLASSES.
put(
Float.
TYPE,
Float.class);
PRIMITIVE_WRAPPER_CLASSES.
put(
Float.class,
Float.
TYPE);
PRIMITIVE_WRAPPER_CLASSES.
put(
Double.
TYPE,
Double.class);
PRIMITIVE_WRAPPER_CLASSES.
put(
Double.class,
Double.
TYPE);
}
private static final
Map NUMERIC_CASTS = new
HashMap();
/**
* Constant strings for casting different primitive types.
*/
static {
NUMERIC_CASTS.
put(
Double.class, "(double)");
NUMERIC_CASTS.
put(
Float.class, "(float)");
NUMERIC_CASTS.
put(
Integer.class, "(int)");
NUMERIC_CASTS.
put(
Long.class, "(long)");
NUMERIC_CASTS.
put(
BigDecimal.class, "(double)");
NUMERIC_CASTS.
put(
BigInteger.class, "");
}
private static final
Map NUMERIC_VALUES = new
HashMap();
/**
* Constant strings for getting the primitive value of different
* native types on the generic {@link Number} object interface. (or the less
* generic BigDecimal/BigInteger types)
*/
static {
NUMERIC_VALUES.
put(
Double.class, "doubleValue()");
NUMERIC_VALUES.
put(
Float.class, "floatValue()");
NUMERIC_VALUES.
put(
Integer.class, "intValue()");
NUMERIC_VALUES.
put(
Long.class, "longValue()");
NUMERIC_VALUES.
put(
Short.class, "shortValue()");
NUMERIC_VALUES.
put(
Byte.class, "byteValue()");
NUMERIC_VALUES.
put(
BigDecimal.class, "doubleValue()");
NUMERIC_VALUES.
put(
BigInteger.class, "doubleValue()");
NUMERIC_VALUES.
put(
Boolean.class, "booleanValue()");
}
private static final
Map NUMERIC_LITERALS = new
HashMap();
/**
* Numeric primitive literal string expressions.
*/
static {
NUMERIC_LITERALS.
put(
Integer.class, "");
NUMERIC_LITERALS.
put(
Integer.
TYPE, "");
NUMERIC_LITERALS.
put(
Long.class, "l");
NUMERIC_LITERALS.
put(
Long.
TYPE, "l");
NUMERIC_LITERALS.
put(
BigInteger.class, "d");
NUMERIC_LITERALS.
put(
Float.class, "f");
NUMERIC_LITERALS.
put(
Float.
TYPE, "f");
NUMERIC_LITERALS.
put(
Double.class, "d");
NUMERIC_LITERALS.
put(
Double.
TYPE, "d");
NUMERIC_LITERALS.
put(
BigInteger.class, "d");
NUMERIC_LITERALS.
put(
BigDecimal.class, "d");
}
private static final
Map NUMERIC_DEFAULTS = new
HashMap();
static {
NUMERIC_DEFAULTS.
put(
Boolean.class,
Boolean.
FALSE);
NUMERIC_DEFAULTS.
put(
Byte.class, new
Byte((byte) 0));
NUMERIC_DEFAULTS.
put(
Short.class, new
Short((short) 0));
NUMERIC_DEFAULTS.
put(
Character.class, new
Character((char) 0));
NUMERIC_DEFAULTS.
put(
Integer.class, new
Integer(0));
NUMERIC_DEFAULTS.
put(
Long.class, new
Long(0L));
NUMERIC_DEFAULTS.
put(
Float.class, new
Float(0.0f));
NUMERIC_DEFAULTS.
put(
Double.class, new
Double(0.0));
NUMERIC_DEFAULTS.
put(
BigInteger.class, new
BigInteger("0"));
NUMERIC_DEFAULTS.
put(
BigDecimal.class, new
BigDecimal(0.0));
}
static {
PropertyAccessor p = new
ArrayPropertyAccessor();
setPropertyAccessor(
Object.class, new
ObjectPropertyAccessor());
setPropertyAccessor(byte[].class,
p);
setPropertyAccessor(short[].class,
p);
setPropertyAccessor(char[].class,
p);
setPropertyAccessor(int[].class,
p);
setPropertyAccessor(long[].class,
p);
setPropertyAccessor(float[].class,
p);
setPropertyAccessor(double[].class,
p);
setPropertyAccessor(
Object[].class,
p);
setPropertyAccessor(
List.class, new
ListPropertyAccessor());
setPropertyAccessor(
Map.class, new
MapPropertyAccessor());
setPropertyAccessor(
Set.class, new
SetPropertyAccessor());
setPropertyAccessor(
Iterator.class, new
IteratorPropertyAccessor());
setPropertyAccessor(
Enumeration.class, new
EnumerationPropertyAccessor());
ElementsAccessor e = new
ArrayElementsAccessor();
setElementsAccessor(
Object.class, new
ObjectElementsAccessor());
setElementsAccessor(byte[].class,
e);
setElementsAccessor(short[].class,
e);
setElementsAccessor(char[].class,
e);
setElementsAccessor(int[].class,
e);
setElementsAccessor(long[].class,
e);
setElementsAccessor(float[].class,
e);
setElementsAccessor(double[].class,
e);
setElementsAccessor(
Object[].class,
e);
setElementsAccessor(
Collection.class, new
CollectionElementsAccessor());
setElementsAccessor(
Map.class, new
MapElementsAccessor());
setElementsAccessor(
Iterator.class, new
IteratorElementsAccessor());
setElementsAccessor(
Enumeration.class, new
EnumerationElementsAccessor());
setElementsAccessor(
Number.class, new
NumberElementsAccessor());
NullHandler nh = new
ObjectNullHandler();
setNullHandler(
Object.class,
nh);
setNullHandler(byte[].class,
nh);
setNullHandler(short[].class,
nh);
setNullHandler(char[].class,
nh);
setNullHandler(int[].class,
nh);
setNullHandler(long[].class,
nh);
setNullHandler(float[].class,
nh);
setNullHandler(double[].class,
nh);
setNullHandler(
Object[].class,
nh);
MethodAccessor ma = new
ObjectMethodAccessor();
setMethodAccessor(
Object.class,
ma);
setMethodAccessor(byte[].class,
ma);
setMethodAccessor(short[].class,
ma);
setMethodAccessor(char[].class,
ma);
setMethodAccessor(int[].class,
ma);
setMethodAccessor(long[].class,
ma);
setMethodAccessor(float[].class,
ma);
setMethodAccessor(double[].class,
ma);
setMethodAccessor(
Object[].class,
ma);
_primitiveTypes.
put("boolean",
Boolean.
TYPE);
_primitiveTypes.
put("byte",
Byte.
TYPE);
_primitiveTypes.
put("short",
Short.
TYPE);
_primitiveTypes.
put("char",
Character.
TYPE);
_primitiveTypes.
put("int",
Integer.
TYPE);
_primitiveTypes.
put("long",
Long.
TYPE);
_primitiveTypes.
put("float",
Float.
TYPE);
_primitiveTypes.
put("double",
Double.
TYPE);
_primitiveDefaults.
put(
Boolean.
TYPE,
Boolean.
FALSE);
_primitiveDefaults.
put(
Boolean.class,
Boolean.
FALSE);
_primitiveDefaults.
put(
Byte.
TYPE, new
Byte((byte) 0));
_primitiveDefaults.
put(
Byte.class, new
Byte((byte) 0));
_primitiveDefaults.
put(
Short.
TYPE, new
Short((short) 0));
_primitiveDefaults.
put(
Short.class, new
Short((short) 0));
_primitiveDefaults.
put(
Character.
TYPE, new
Character((char) 0));
_primitiveDefaults.
put(
Integer.
TYPE, new
Integer(0));
_primitiveDefaults.
put(
Long.
TYPE, new
Long(0L));
_primitiveDefaults.
put(
Float.
TYPE, new
Float(0.0f));
_primitiveDefaults.
put(
Double.
TYPE, new
Double(0.0));
_primitiveDefaults.
put(
BigInteger.class, new
BigInteger("0"));
_primitiveDefaults.
put(
BigDecimal.class, new
BigDecimal(0.0));
}
/**
* Clears all of the cached reflection information normally used
* to improve the speed of expressions that operate on the same classes
* or are executed multiple times.
*
* <p>
* <strong>Warning:</strong> Calling this too often can be a huge performance
* drain on your expressions - use with care.
* </p>
*/
public static void
clearCache()
{
_methodParameterTypesCache.
clear();
_ctorParameterTypesCache.
clear();
_propertyDescriptorCache.
clear();
_constructorCache.
clear();
_staticMethodCache.
clear();
_instanceMethodCache.
clear();
_invokePermissionCache.
clear();
_fieldCache.
clear();
_superclasses.
clear();
_declaredMethods[0].
clear();
_declaredMethods[1].
clear();
_methodAccessCache.
clear();
}
/**
* Checks if the current jvm is java language >= 1.5 compatible.
*
* @return True if jdk15 features are present.
*/
public static boolean
isJdk15()
{
if (
_jdkChecked)
return
_jdk15;
try
{
Class.
forName("java.lang.annotation.Annotation");
_jdk15 = true;
} catch (
Exception e) { /* ignore */ }
_jdkChecked = true;
return
_jdk15;
}
public static
String getNumericValueGetter(
Class type)
{
return (
String)
NUMERIC_VALUES.
get(
type);
}
public static
Class getPrimitiveWrapperClass(
Class primitiveClass)
{
return (
Class)
PRIMITIVE_WRAPPER_CLASSES.
get(
primitiveClass);
}
public static
String getNumericCast(
Class type)
{
return (
String)
NUMERIC_CASTS.
get(
type);
}
public static
String getNumericLiteral(
Class type)
{
return (
String)
NUMERIC_LITERALS.
get(
type);
}
public static void
setCompiler(
OgnlExpressionCompiler compiler)
{
_compiler =
compiler;
}
public static
OgnlExpressionCompiler getCompiler()
{
return
_compiler;
}
public static void
compileExpression(
OgnlContext context,
Node expression,
Object root)
throws
Exception
{
_compiler.
compileExpression(
context,
expression,
root);
}
/**
* Gets the "target" class of an object for looking up accessors that are registered on the
* target. If the object is a Class object this will return the Class itself, else it will
* return object's getClass() result.
*/
public static
Class getTargetClass(
Object o)
{
return (
o == null) ? null : ((
o instanceof
Class) ? (
Class)
o :
o.
getClass());
}
/**
* Returns the base name (the class name without the package name prepended) of the object
* given.
*/
public static
String getBaseName(
Object o)
{
return (
o == null) ? null :
getClassBaseName(
o.
getClass());
}
/**
* Returns the base name (the class name without the package name prepended) of the class given.
*/
public static
String getClassBaseName(
Class c)
{
String s =
c.
getName();
return
s.
substring(
s.
lastIndexOf('.') + 1);
}
public static
String getClassName(
Object o, boolean
fullyQualified)
{
if (!(
o instanceof
Class))
{
o =
o.
getClass();
}
return
getClassName((
Class)
o,
fullyQualified);
}
public static
String getClassName(
Class c, boolean
fullyQualified)
{
return
fullyQualified ?
c.
getName() :
getClassBaseName(
c);
}
/**
* Returns the package name of the object's class.
*/
public static
String getPackageName(
Object o)
{
return (
o == null) ? null :
getClassPackageName(
o.
getClass());
}
/**
* Returns the package name of the class given.
*/
public static
String getClassPackageName(
Class c)
{
String s =
c.
getName();
int
i =
s.
lastIndexOf('.');
return (
i < 0) ? null :
s.
substring(0,
i);
}
/**
* Returns a "pointer" string in the usual format for these things - 0x<hex digits>.
*/
public static
String getPointerString(int
num)
{
StringBuffer result = new
StringBuffer();
String hex =
Integer.
toHexString(
num),
pad;
Integer l = new
Integer(
hex.
length());
// result.append(HEX_PREFIX);
if ((
pad = (
String)
HEX_PADDING.
get(
l)) == null) {
StringBuffer pb = new
StringBuffer();
for (int
i =
hex.
length();
i <
HEX_LENGTH;
i++) {
pb.
append('0');
}
pad = new
String(
pb);
HEX_PADDING.
put(
l,
pad);
}
result.
append(
pad);
result.
append(
hex);
return new
String(
result);
}
/**
* Returns a "pointer" string in the usual format for these things - 0x<hex digits> for the
* object given. This will always return a unique value for each object.
*/
public static
String getPointerString(
Object o)
{
return
getPointerString((
o == null) ? 0 :
System.
identityHashCode(
o));
}
/**
* Returns a unique descriptor string that includes the object's class and a unique integer
* identifier. If fullyQualified is true then the class name will be fully qualified to include
* the package name, else it will be just the class' base name.
*/
public static
String getUniqueDescriptor(
Object object, boolean
fullyQualified)
{
StringBuffer result = new
StringBuffer();
if (
object != null) {
if (
object instanceof
Proxy) {
Class interfaceClass =
object.
getClass().
getInterfaces()[0];
result.
append(
getClassName(
interfaceClass,
fullyQualified));
result.
append('^');
object =
Proxy.
getInvocationHandler(
object);
}
result.
append(
getClassName(
object,
fullyQualified));
result.
append('@');
result.
append(
getPointerString(
object));
} else {
result.
append(
NULL_OBJECT_STRING);
}
return new
String(
result);
}
/**
* Returns a unique descriptor string that includes the object's class' base name and a unique
* integer identifier.
*/
public static
String getUniqueDescriptor(
Object object)
{
return
getUniqueDescriptor(
object, false);
}
/**
* Utility to convert a List into an Object[] array. If the list is zero elements this will
* return a constant array; toArray() on List always returns a new object and this is wasteful
* for our purposes.
*/
public static
Object[]
toArray(
List list)
{
Object[]
result;
int
size =
list.
size();
if (
size == 0) {
result =
NoArguments;
} else {
result =
getObjectArrayPool().
create(
list.
size());
for (int
i = 0;
i <
size;
i++) {
result[
i] =
list.
get(
i);
}
}
return
result;
}
/**
* Returns the parameter types of the given method.
*/
public static
Class[]
getParameterTypes(
Method m)
{
synchronized (
_methodParameterTypesCache)
{
Class[]
result;
if ((
result = (
Class[])
_methodParameterTypesCache.
get(
m)) == null)
{
_methodParameterTypesCache.
put(
m,
result =
m.
getParameterTypes());
}
return
result;
}
}
/**
* Finds the appropriate parameter types for the given {@link Method} and
* {@link Class} instance of the type the method is associated with. Correctly
* finds generic types if running in >= 1.5 jre as well.
*
* @param type The class type the method is being executed against.
* @param m The method to find types for.
* @return Array of parameter types for the given method.
*/
public static
Class[]
findParameterTypes(
Class type,
Method m)
{
if (
type == null)
{
return
getParameterTypes(
m);
}
if (!
isJdk15()
||
type.
getGenericSuperclass() == null
|| !
ParameterizedType.class.
isInstance(
type.
getGenericSuperclass())
||
m.
getDeclaringClass().
getTypeParameters() == null)
{
return
getParameterTypes(
m);
}
synchronized (
_genericMethodParameterTypesCache)
{
Class[]
types;
if ((
types = (
Class[])
_genericMethodParameterTypesCache.
get(
m)) != null)
{
return
types;
}
ParameterizedType param = (
ParameterizedType)
type.
getGenericSuperclass();
Type[]
genTypes =
m.
getGenericParameterTypes();
TypeVariable[]
declaredTypes =
m.
getDeclaringClass().
getTypeParameters();
types = new
Class[
genTypes.length];
typeSearch:
for (int
i=0;
i <
genTypes.length;
i++)
{
TypeVariable paramType = null;
if (
TypeVariable.class.
isInstance(
genTypes[
i]))
{
paramType = (
TypeVariable)
genTypes[
i];
} else if (
GenericArrayType.class.
isInstance(
genTypes[
i]))
{
paramType = (
TypeVariable) ((
GenericArrayType)
genTypes[
i]).
getGenericComponentType();
} else if (
Class.class.
isInstance(
genTypes[
i]))
{
types[
i] = (
Class)
genTypes[
i];
continue;
}
Class resolved =
resolveType(
param,
paramType,
declaredTypes);
if (
resolved != null)
{
if (
GenericArrayType.class.
isInstance(
genTypes[
i]))
{
resolved =
Array.
newInstance(
resolved, 0).
getClass();
}
types[
i] =
resolved;
continue;
}
types[
i] =
m.
getParameterTypes()[
i];
}
_genericMethodParameterTypesCache.
put(
m,
types);
return
types;
}
}
static
Class resolveType(
ParameterizedType param,
TypeVariable var,
TypeVariable[]
declaredTypes)
{
if (
param.
getActualTypeArguments().length < 1)
return null;
for (int
i=0;
i <
declaredTypes.length;
i++)
{
if (!
TypeVariable.class.
isInstance(
param.
getActualTypeArguments()[
i])
&&
declaredTypes[
i].
getName().
equals(
var.
getName()))
{
return (
Class)
param.
getActualTypeArguments()[
i];
}
}
/*
for (int i=0; i < var.getBounds().length; i++)
{
Type t = var.getBounds()[i];
Class resolvedType = null;
if (ParameterizedType.class.isInstance(t))
{
ParameterizedType pparam = (ParameterizedType)t;
for (int e=0; e < pparam.getActualTypeArguments().length; e++)
{
if (!TypeVariable.class.isInstance(pparam.getActualTypeArguments()[e]))
continue;
resolvedType = resolveType(pparam, (TypeVariable)pparam.getActualTypeArguments()[e], declaredTypes);
}
} else
{
resolvedType = findType(param.getActualTypeArguments(), (Class)t);
}
if (resolvedType != null)
return resolvedType;
}
*/
return null;
}
static
Class findType(
Type[]
types,
Class type)
{
for (int
i = 0;
i <
types.length;
i++)
{
if (
Class.class.
isInstance(
types[
i]) &&
type.
isAssignableFrom((
Class)
types[
i]))
return (
Class)
types[
i];
}
return null;
}
/**
* Returns the parameter types of the given method.
*/
public static
Class[]
getParameterTypes(
Constructor c)
{
synchronized (
_ctorParameterTypesCache) {
Class[]
result;
if ((
result = (
Class[])
_ctorParameterTypesCache.
get(
c)) == null) {
_ctorParameterTypesCache.
put(
c,
result =
c.
getParameterTypes());
}
return
result;
}
}
/**
* Gets the SecurityManager that OGNL uses to determine permissions for invoking methods.
*
* @return SecurityManager for OGNL
*/
public static
SecurityManager getSecurityManager()
{
return
_securityManager;
}
/**
* Sets the SecurityManager that OGNL uses to determine permissions for invoking methods.
*
* @param value SecurityManager to set
*/
public static void
setSecurityManager(
SecurityManager value)
{
_securityManager =
value;
}
/**
* Permission will be named "invoke.<declaring-class>.<method-name>".
*/
public static
Permission getPermission(
Method method)
{
Permission result = null;
Class mc =
method.
getDeclaringClass();
synchronized (
_invokePermissionCache) {
Map permissions = (
Map)
_invokePermissionCache.
get(
mc);
if (
permissions == null) {
_invokePermissionCache.
put(
mc,
permissions = new
HashMap(101));
}
if ((
result = (
Permission)
permissions.
get(
method.
getName())) == null) {
result = new
OgnlInvokePermission("invoke." +
mc.
getName() + "." +
method.
getName());
permissions.
put(
method.
getName(),
result);
}
}
return
result;
}
public static
Object invokeMethod(
Object target,
Method method,
Object[]
argsArray)
throws
InvocationTargetException,
IllegalAccessException
{
boolean
syncInvoke = false;
boolean
checkPermission = false;
int
mHash =
method.
hashCode();
// only synchronize method invocation if it actually requires it
synchronized(
method) {
if (
_methodAccessCache.
get(
mHash) == null
||
_methodAccessCache.
get(
mHash) ==
Boolean.
TRUE) {
syncInvoke = true;
}
if (
_securityManager != null &&
_methodPermCache.
get(
mHash) == null
||
_methodPermCache.
get(
mHash) ==
Boolean.
FALSE) {
checkPermission = true;
}
}
Object result;
boolean
wasAccessible = true;
if (
syncInvoke)
{
synchronized(
method)
{
if (
checkPermission)
{
try
{
_securityManager.
checkPermission(
getPermission(
method));
_methodPermCache.
put(
mHash,
Boolean.
TRUE);
} catch (
SecurityException ex) {
_methodPermCache.
put(
mHash,
Boolean.
FALSE);
throw new
IllegalAccessException("Method [" +
method + "] cannot be accessed.");
}
}
if (!
Modifier.
isPublic(
method.
getModifiers()) || !
Modifier.
isPublic(
method.
getDeclaringClass().
getModifiers()))
{
if (!(
wasAccessible = ((
AccessibleObject)
method).
isAccessible()))
{
((
AccessibleObject)
method).
setAccessible(true);
_methodAccessCache.
put(
mHash,
Boolean.
TRUE);
} else
{
_methodAccessCache.
put(
mHash,
Boolean.
FALSE);
}
} else
{
_methodAccessCache.
put(
mHash,
Boolean.
FALSE);
}
result =
method.
invoke(
target,
argsArray);
if (!
wasAccessible)
{
((
AccessibleObject)
method).
setAccessible(false);
}
}
} else
{
if (
checkPermission)
{
try
{
_securityManager.
checkPermission(
getPermission(
method));
_methodPermCache.
put(
mHash,
Boolean.
TRUE);
} catch (
SecurityException ex) {
_methodPermCache.
put(
mHash,
Boolean.
FALSE);
throw new
IllegalAccessException("Method [" +
method + "] cannot be accessed.");
}
}
result =
method.
invoke(
target,
argsArray);
}
return
result;
}
/**
* Gets the class for a method argument that is appropriate for looking up methods by
* reflection, by looking for the standard primitive wrapper classes and exchanging for them
* their underlying primitive class objects. Other classes are passed through unchanged.
*
* @param arg an object that is being passed to a method
* @return the class to use to look up the method
*/
public static final
Class getArgClass(
Object arg)
{
if (
arg == null)
return null;
Class c =
arg.
getClass();
if (
c ==
Boolean.class)
return
Boolean.
TYPE;
else if (
c.
getSuperclass() ==
Number.class) {
if (
c ==
Integer.class)
return
Integer.
TYPE;
if (
c ==
Double.class)
return
Double.
TYPE;
if (
c ==
Byte.class)
return
Byte.
TYPE;
if (
c ==
Long.class)
return
Long.
TYPE;
if (
c ==
Float.class)
return
Float.
TYPE;
if (
c ==
Short.class)
return
Short.
TYPE;
} else if (
c ==
Character.class)
return
Character.
TYPE;
return
c;
}
/**
* Tells whether the given object is compatible with the given class ---that is, whether the
* given object can be passed as an argument to a method or constructor whose parameter type is
* the given class. If object is null this will return true because null is compatible with any
* type.
*/
public static final boolean
isTypeCompatible(
Object object,
Class c)
{
boolean
result = true;
if (
object != null) {
if (
c.
isPrimitive()) {
if (
getArgClass(
object) !=
c) {
result = false;
}
} else if (!
c.
isInstance(
object)) {
result = false;
}
}
return
result;
}
/**
* Tells whether the given array of objects is compatible with the given array of classes---that
* is, whether the given array of objects can be passed as arguments to a method or constructor
* whose parameter types are the given array of classes.
*/
public static boolean
areArgsCompatible(
Object[]
args,
Class[]
classes)
{
return
areArgsCompatible(
args,
classes, null);
}
public static boolean
areArgsCompatible(
Object[]
args,
Class[]
classes,
Method m)
{
boolean
result = true;
boolean
varArgs =
m != null &&
isJdk15() &&
m.
isVarArgs();
if (
args.length !=
classes.length && !
varArgs) {
result = false;
} else if (
varArgs) {
for (int
index = 0,
count =
args.length;
result && (
index <
count); ++
index) {
if (
index >=
classes.length){
break;
}
result =
isTypeCompatible(
args[
index],
classes[
index]);
if (!
result &&
classes[
index].
isArray()) {
result =
isTypeCompatible(
args[
index],
classes[
index].
getComponentType());
}
}
} else {
for (int
index = 0,
count =
args.length;
result && (
index <
count); ++
index) {
result =
isTypeCompatible(
args[
index],
classes[
index]);
}
}
return
result;
}
/**
* Tells whether the first array of classes is more specific than the second. Assumes that the
* two arrays are of the same length.
*/
public static final boolean
isMoreSpecific(
Class[]
classes1,
Class[]
classes2)
{
for (int
index = 0,
count =
classes1.length;
index <
count; ++
index) {
Class c1 =
classes1[
index],
c2 =
classes2[
index];
if (
c1 ==
c2)
continue;
else if (
c1.
isPrimitive())
return true;
else if (
c1.
isAssignableFrom(
c2))
return false;
else if (
c2.
isAssignableFrom(
c1))
return true;
}
// They are the same! So the first is not more specific than the second.
return false;
}
public static
String getModifierString(int
modifiers)
{
String result;
if (
Modifier.
isPublic(
modifiers))
result = "public";
else if (
Modifier.
isProtected(
modifiers))
result = "protected";
else if (
Modifier.
isPrivate(
modifiers))
result = "private";
else
result = "";
if (
Modifier.
isStatic(
modifiers))
result = "static " +
result;
if (
Modifier.
isFinal(
modifiers))
result = "final " +
result;
if (
Modifier.
isNative(
modifiers))
result = "native " +
result;
if (
Modifier.
isSynchronized(
modifiers))
result = "synchronized " +
result;
if (
Modifier.
isTransient(
modifiers))
result = "transient " +
result;
return
result;
}
public static
Class classForName(
OgnlContext context,
String className)
throws
ClassNotFoundException
{
Class result = (
Class)
_primitiveTypes.
get(
className);
if (
result == null) {
ClassResolver resolver;
if ((
context == null) || ((
resolver =
context.
getClassResolver()) == null)) {
resolver =
OgnlContext.
DEFAULT_CLASS_RESOLVER;
}
result =
resolver.
classForName(
className,
context);
}
if (
result == null)
throw new
ClassNotFoundException("Unable to resolve class: " +
className);
return
result;
}
public static boolean
isInstance(
OgnlContext context,
Object value,
String className)
throws
OgnlException
{
try {
Class c =
classForName(
context,
className);
return
c.
isInstance(
value);
} catch (
ClassNotFoundException e) {
throw new
OgnlException("No such class: " +
className,
e);
}
}
public static
Object getPrimitiveDefaultValue(
Class forClass)
{
return
_primitiveDefaults.
get(
forClass);
}
public static
Object getNumericDefaultValue(
Class forClass)
{
return
NUMERIC_DEFAULTS.
get(
forClass);
}
public static
Object getConvertedType(
OgnlContext context,
Object target,
Member member,
String propertyName,
Object value,
Class type)
{
return
context.
getTypeConverter().
convertValue(
context,
target,
member,
propertyName,
value,
type);
}
public static boolean
getConvertedTypes(
OgnlContext context,
Object target,
Member member,
String propertyName,
Class[]
parameterTypes,
Object[]
args,
Object[]
newArgs)
{
boolean
result = false;
if (
parameterTypes.length ==
args.length) {
result = true;
for (int
i = 0,
ilast =
parameterTypes.length - 1;
result && (
i <=
ilast);
i++) {
Object arg =
args[
i];
Class type =
parameterTypes[
i];
if (
isTypeCompatible(
arg,
type)) {
newArgs[
i] =
arg;
} else {
Object v =
getConvertedType(
context,
target,
member,
propertyName,
arg,
type);
if (
v ==
OgnlRuntime.
NoConversionPossible) {
result = false;
} else {
newArgs[
i] =
v;
}
}
}
}
return
result;
}
public static
Method getConvertedMethodAndArgs(
OgnlContext context,
Object target,
String propertyName,
List methods,
Object[]
args,
Object[]
newArgs)
{
Method result = null;
TypeConverter converter =
context.
getTypeConverter();
if ((
converter != null) && (
methods != null))
{
for (int
i = 0,
icount =
methods.
size(); (
result == null) && (
i <
icount);
i++)
{
Method m = (
Method)
methods.
get(
i);
Class[]
parameterTypes =
findParameterTypes(
target != null ?
target.
getClass() : null,
m);//getParameterTypes(m);
if (
getConvertedTypes(
context,
target,
m,
propertyName,
parameterTypes,
args,
newArgs))
{
result =
m;
}
}
}
return
result;
}
public static
Constructor getConvertedConstructorAndArgs(
OgnlContext context,
Object target,
List constructors,
Object[]
args,
Object[]
newArgs)
{
Constructor result = null;
TypeConverter converter =
context.
getTypeConverter();
if ((
converter != null) && (
constructors != null)) {
for (int
i = 0,
icount =
constructors.
size(); (
result == null) && (
i <
icount);
i++) {
Constructor ctor = (
Constructor)
constructors.
get(
i);
Class[]
parameterTypes =
getParameterTypes(
ctor);
if (
getConvertedTypes(
context,
target,
ctor, null,
parameterTypes,
args,
newArgs)) {
result =
ctor;
}
}
}
return
result;
}
/**
* Gets the appropriate method to be called for the given target, method name and arguments. If
* successful this method will return the Method within the target that can be called and the
* converted arguments in actualArgs. If unsuccessful this method will return null and the
* actualArgs will be empty.
*
* @param context The current execution context.
* @param source Target object to run against or method name.
* @param target Instance of object to be run against.
* @param propertyName Name of property to get method of.
* @param methods List of current known methods.
* @param args Arguments originally passed in.
* @param actualArgs Converted arguments.
*
* @return Best method match or null if none could be found.
*/
public static
Method getAppropriateMethod(
OgnlContext context,
Object source,
Object target,
String propertyName,
List methods,
Object[]
args,
Object[]
actualArgs)
{
Method result = null;
Class[]
resultParameterTypes = null;
if (
methods != null)
{
for (int
i = 0,
icount =
methods.
size();
i <
icount;
i++)
{
Method m = (
Method)
methods.
get(
i);
Class typeClass =
target != null ?
target.
getClass() : null;
if (
typeClass == null &&
source != null &&
Class.class.
isInstance(
source))
{
typeClass = (
Class)
source;
}
Class[]
mParameterTypes =
findParameterTypes(
typeClass,
m);
if (
areArgsCompatible(
args,
mParameterTypes,
m)
&& ((
result == null) ||
isMoreSpecific(
mParameterTypes,
resultParameterTypes)))
{
result =
m;
resultParameterTypes =
mParameterTypes;
System.
arraycopy(
args, 0,
actualArgs, 0,
args.length);
for (int
j = 0;
j <
mParameterTypes.length;
j++)
{
Class type =
mParameterTypes[
j];
if (
type.
isPrimitive() && (
actualArgs[
j] == null))
{
actualArgs[
j] =
getConvertedType(
context,
source,
result,
propertyName, null,
type);
}
}
}
}
}
if (
result == null)
{
result =
getConvertedMethodAndArgs(
context,
target,
propertyName,
methods,
args,
actualArgs);
}
return
result;
}
public static
Object callAppropriateMethod(
OgnlContext context,
Object source,
Object target,
String methodName,
String propertyName,
List methods,
Object[]
args)
throws
MethodFailedException
{
Throwable reason = null;
Object[]
actualArgs =
_objectArrayPool.
create(
args.length);
try {
Method method =
getAppropriateMethod(
context,
source,
target,
propertyName,
methods,
args,
actualArgs);
if ((
method == null) || !
isMethodAccessible(
context,
source,
method,
propertyName))
{
StringBuffer buffer = new
StringBuffer();
String className = "";
if (
target != null)
{
className =
target.
getClass().
getName() + ".";
}
for (int
i = 0,
ilast =
args.length - 1;
i <=
ilast;
i++)
{
Object arg =
args[
i];
buffer.
append((
arg == null) ?
NULL_STRING :
arg.
getClass().
getName());
if (
i <
ilast)
{
buffer.
append(", ");
}
}
throw new
NoSuchMethodException(
className +
methodName + "(" +
buffer + ")");
}
Object[]
convertedArgs =
actualArgs;
if (
isJdk15() &&
method.
isVarArgs())
{
Class[]
parmTypes =
method.
getParameterTypes();
// split arguments in to two dimensional array for varargs reflection invocation
// where it is expected that the parameter passed in to invoke the method
// will look like "new Object[] { arrayOfNonVarArgsArguments, arrayOfVarArgsArguments }"
for (int
i=0;
i <
parmTypes.length;
i++)
{
if (
parmTypes[
i].
isArray())
{
convertedArgs = new
Object[
i + 1];
System.
arraycopy(
actualArgs, 0,
convertedArgs, 0,
convertedArgs.length);
Object[]
varArgs;
// if they passed in varargs arguments grab them and dump in to new varargs array
if (
actualArgs.length >
i)
{
ArrayList varArgsList = new
ArrayList();
for (int
j=
i;
j <
actualArgs.length;
j++)
{
if (
actualArgs[
j] != null)
{
varArgsList.
add(
actualArgs[
j]);
}
}
varArgs =
varArgsList.
toArray();
} else
{
varArgs = new
Object[0];
}
convertedArgs[
i] =
varArgs;
break;
}
}
}
return
invokeMethod(
target,
method,
convertedArgs);
} catch (
NoSuchMethodException e) {
reason =
e;
} catch (
IllegalAccessException e) {
reason =
e;
} catch (
InvocationTargetException e) {
reason =
e.
getTargetException();
} finally {
_objectArrayPool.
recycle(
actualArgs);
}
throw new
MethodFailedException(
source,
methodName,
reason);
}
public static
Object callStaticMethod(
OgnlContext context,
String className,
String methodName,
Object[]
args)
throws
OgnlException
{
try {
Class targetClass =
classForName(
context,
className);
if (
targetClass == null)
throw new
ClassNotFoundException("Unable to resolve class with name " +
className);
MethodAccessor ma =
getMethodAccessor(
targetClass);
return
ma.
callStaticMethod(
context,
targetClass,
methodName,
args);
} catch (
ClassNotFoundException ex) {
throw new
MethodFailedException(
className,
methodName,
ex);
}
}
/**
* Invokes the specified method against the target object.
*
* @param context
* The current execution context.
* @param target
* The object to invoke the method on.
* @param methodName
* Name of the method - as in "getValue" or "add", etc..
* @param propertyName
* Name of the property to call instead?
* @param args
* Optional arguments needed for method.
* @return Result of invoking method.
*
* @deprecated Use {@link #callMethod(OgnlContext, Object, String, Object[])} instead.
* @throws OgnlException For lots of different reasons.
*/
public static
Object callMethod(
OgnlContext context,
Object target,
String methodName,
String propertyName,
Object[]
args)
throws
OgnlException
{
return
callMethod(
context,
target,
methodName == null ?
propertyName :
methodName,
args);
}
/**
* Invokes the specified method against the target object.
*
* @param context
* The current execution context.
* @param target
* The object to invoke the method on.
* @param methodName
* Name of the method - as in "getValue" or "add", etc..
* @param args
* Optional arguments needed for method.
* @return Result of invoking method.
*
* @throws OgnlException For lots of different reasons.
*/
public static
Object callMethod(
OgnlContext context,
Object target,
String methodName,
Object[]
args)
throws
OgnlException
{
if (
target == null)
throw new
NullPointerException("target is null for method " +
methodName);
return
getMethodAccessor(
target.
getClass()).
callMethod(
context,
target,
methodName,
args);
}
public static
Object callConstructor(
OgnlContext context,
String className,
Object[]
args)
throws
OgnlException
{
Throwable reason = null;
Object[]
actualArgs =
args;
try {
Constructor ctor = null;
Class[]
ctorParameterTypes = null;
Class target =
classForName(
context,
className);
List constructors =
getConstructors(
target);
for (int
i = 0,
icount =
constructors.
size();
i <
icount;
i++) {
Constructor c = (
Constructor)
constructors.
get(
i);
Class[]
cParameterTypes =
getParameterTypes(
c);
if (
areArgsCompatible(
args,
cParameterTypes)
&& (
ctor == null ||
isMoreSpecific(
cParameterTypes,
ctorParameterTypes))) {
ctor =
c;
ctorParameterTypes =
cParameterTypes;
}
}
if (
ctor == null) {
actualArgs =
_objectArrayPool.
create(
args.length);
if ((
ctor =
getConvertedConstructorAndArgs(
context,
target,
constructors,
args,
actualArgs)) == null) {
throw new
NoSuchMethodException();
}
}
if (!
context.
getMemberAccess().
isAccessible(
context,
target,
ctor, null)) {
throw new
IllegalAccessException(
"access denied to " +
target.
getName() + "()");
}
return
ctor.
newInstance(
actualArgs);
} catch (
ClassNotFoundException e) {
reason =
e;
} catch (
NoSuchMethodException e) {
reason =
e;
} catch (
IllegalAccessException e) {
reason =
e;
} catch (
InvocationTargetException e) {
reason =
e.
getTargetException();
} catch (
InstantiationException e) {
reason =
e;
} finally {
if (
actualArgs !=
args) {
_objectArrayPool.
recycle(
actualArgs);
}
}
throw new
MethodFailedException(
className, "new",
reason);
}
public static final
Object getMethodValue(
OgnlContext context,
Object target,
String propertyName)
throws
OgnlException,
IllegalAccessException,
NoSuchMethodException,
IntrospectionException
{
return
getMethodValue(
context,
target,
propertyName, false);
}
/**
* If the checkAccessAndExistence flag is true this method will check to see if the method
* exists and if it is accessible according to the context's MemberAccess. If neither test
* passes this will return NotFound.
*/
public static final
Object getMethodValue(
OgnlContext context,
Object target,
String propertyName,
boolean
checkAccessAndExistence)
throws
OgnlException,
IllegalAccessException,
NoSuchMethodException,
IntrospectionException
{
Object result = null;
Method m =
getGetMethod(
context, (
target == null) ? null :
target.
getClass() ,
propertyName);
if (
m == null)
m =
getReadMethod((
target == null) ? null :
target.
getClass(),
propertyName, 0);
if (
checkAccessAndExistence)
{
if ((
m == null) || !
context.
getMemberAccess().
isAccessible(
context,
target,
m,
propertyName))
{
result =
NotFound;
}
}
if (
result == null) {
if (
m != null) {
try {
result =
invokeMethod(
target,
m,
NoArguments);
} catch (
InvocationTargetException ex) {
throw new
OgnlException(
propertyName,
ex.
getTargetException());
}
} else {
throw new
NoSuchMethodException(
propertyName);
}
}
return
result;
}
public static boolean
setMethodValue(
OgnlContext context,
Object target,
String propertyName,
Object value)
throws
OgnlException,
IllegalAccessException,
NoSuchMethodException,
IntrospectionException
{
return
setMethodValue(
context,
target,
propertyName,
value, false);
}
public static boolean
setMethodValue(
OgnlContext context,
Object target,
String propertyName,
Object value,
boolean
checkAccessAndExistence)
throws
OgnlException,
IllegalAccessException,
NoSuchMethodException,
IntrospectionException
{
boolean
result = true;
Method m =
getSetMethod(
context, (
target == null) ? null :
target.
getClass(),
propertyName);
if (
checkAccessAndExistence)
{
if ((
m == null) || !
context.
getMemberAccess().
isAccessible(
context,
target,
m,
propertyName))
{
result = false;
}
}
if (
result)
{
if (
m != null)
{
Object[]
args =
_objectArrayPool.
create(
value);
try
{
callAppropriateMethod(
context,
target,
target,
m.
getName(),
propertyName,
Collections.
nCopies(1,
m),
args);
} finally
{
_objectArrayPool.
recycle(
args);
}
} else
{
result = false;
}
}
return
result;
}
public static
List getConstructors(
Class targetClass)
{
List result;
synchronized (
_constructorCache) {
if ((
result = (
List)
_constructorCache.
get(
targetClass)) == null) {
_constructorCache.
put(
targetClass,
result =
Arrays.
asList(
targetClass.
getConstructors()));
}
}
return
result;
}
public static
Map getMethods(
Class targetClass, boolean
staticMethods)
{
ClassCache cache = (
staticMethods ?
_staticMethodCache :
_instanceMethodCache);
Map result;
synchronized (
cache)
{
if ((
result = (
Map)
cache.
get(
targetClass)) == null)
{
cache.
put(
targetClass,
result = new
HashMap(23));
for (
Class c =
targetClass;
c != null;
c =
c.
getSuperclass())
{
Method[]
ma =
c.
getDeclaredMethods();
for (int
i = 0,
icount =
ma.length;
i <
icount;
i++)
{
// skip over synthetic methods
if (!
isMethodCallable(
ma[
i]))
continue;
if (
Modifier.
isStatic(
ma[
i].
getModifiers()) ==
staticMethods)
{
List ml = (
List)
result.
get(
ma[
i].
getName());
if (
ml == null)
result.
put(
ma[
i].
getName(),
ml = new
ArrayList());
ml.
add(
ma[
i]);
}
}
}
}
}
return
result;
}
public static
List getMethods(
Class targetClass,
String name, boolean
staticMethods)
{
return (
List)
getMethods(
targetClass,
staticMethods).
get(
name);
}
public static
Map getFields(
Class targetClass)
{
Map result;
synchronized (
_fieldCache) {
if ((
result = (
Map)
_fieldCache.
get(
targetClass)) == null) {
Field fa[];
result = new
HashMap(23);
fa =
targetClass.
getDeclaredFields();
for (int
i = 0;
i <
fa.length;
i++) {
result.
put(
fa[
i].
getName(),
fa[
i]);
}
_fieldCache.
put(
targetClass,
result);
}
}
return
result;
}
public static
Field getField(
Class inClass,
String name)
{
Field result = null;
synchronized (
_fieldCache) {
Object o =
getFields(
inClass).
get(
name);
if (
o == null) {
_superclasses.
clear();
for (
Class sc =
inClass; (
sc != null);
sc =
sc.
getSuperclass()) {
if ((
o =
getFields(
sc).
get(
name)) ==
NotFound)
break;
_superclasses.
add(
sc);
if ((
result = (
Field)
o) != null)
break;
}
/*
* Bubble the found value (either cache miss or actual field) to all supeclasses
* that we saw for quicker access next time.
*/
for (int
i = 0,
icount =
_superclasses.
size();
i <
icount;
i++) {
getFields((
Class)
_superclasses.
get(
i)).
put(
name, (
result == null) ?
NotFound :
result);
}
} else {
if (
o instanceof
Field) {
result = (
Field)
o;
} else {
if (
result ==
NotFound)
result = null;
}
}
}
return
result;
}
public static
Object getFieldValue(
OgnlContext context,
Object target,
String propertyName)
throws
NoSuchFieldException
{
return
getFieldValue(
context,
target,
propertyName, false);
}
public static
Object getFieldValue(
OgnlContext context,
Object target,
String propertyName,
boolean
checkAccessAndExistence)
throws
NoSuchFieldException
{
Object result = null;
Field f =
getField((
target == null) ? null :
target.
getClass(),
propertyName);
if (
checkAccessAndExistence) {
if ((
f == null) || !
context.
getMemberAccess().
isAccessible(
context,
target,
f,
propertyName)) {
result =
NotFound;
}
}
if (
result == null) {
if (
f == null) {
throw new
NoSuchFieldException(
propertyName);
} else {
try {
Object state = null;
if (!
Modifier.
isStatic(
f.
getModifiers())) {
state =
context.
getMemberAccess().
setup(
context,
target,
f,
propertyName);
result =
f.
get(
target);
context.
getMemberAccess().
restore(
context,
target,
f,
propertyName,
state);
} else
throw new
NoSuchFieldException(
propertyName);
} catch (
IllegalAccessException ex) {
throw new
NoSuchFieldException(
propertyName);
}
}
}
return
result;
}
public static boolean
setFieldValue(
OgnlContext context,
Object target,
String propertyName,
Object value)
throws
OgnlException
{
boolean
result = false;
try {
Field f =
getField((
target == null) ? null :
target.
getClass(),
propertyName);
Object state;
if ((
f != null) && !
Modifier.
isStatic(
f.
getModifiers())) {
state =
context.
getMemberAccess().
setup(
context,
target,
f,
propertyName);
try {
if (
isTypeCompatible(
value,
f.
getType())
|| ((
value =
getConvertedType(
context,
target,
f,
propertyName,
value,
f.
getType())) != null)) {
f.
set(
target,
value);
result = true;
}
} finally {
context.
getMemberAccess().
restore(
context,
target,
f,
propertyName,
state);
}
}
} catch (
IllegalAccessException ex) {
throw new
NoSuchPropertyException(
target,
propertyName,
ex);
}
return
result;
}
public static boolean
isFieldAccessible(
OgnlContext context,
Object target,
Class inClass,
String propertyName)
{
return
isFieldAccessible(
context,
target,
getField(
inClass,
propertyName),
propertyName);
}
public static boolean
isFieldAccessible(
OgnlContext context,
Object target,
Field field,
String propertyName)
{
return
context.
getMemberAccess().
isAccessible(
context,
target,
field,
propertyName);
}
public static boolean
hasField(
OgnlContext context,
Object target,
Class inClass,
String propertyName)
{
Field f =
getField(
inClass,
propertyName);
return (
f != null) &&
isFieldAccessible(
context,
target,
f,
propertyName);
}
public static
Object getStaticField(
OgnlContext context,
String className,
String fieldName)
throws
OgnlException
{
Exception reason = null;
try {
Class c =
classForName(
context,
className);
if (
c == null)
throw new
OgnlException("Unable to find class " +
className + " when resolving field name of " +
fieldName);
/*
* Check for virtual static field "class"; this cannot interfere with normal static
* fields because it is a reserved word.
*/
if (
fieldName.
equals("class"))
{
return
c;
} else if (
OgnlRuntime.
isJdk15() &&
c.
isEnum())
{
return
Enum.
valueOf(
c,
fieldName);
} else
{
Field f =
c.
getField(
fieldName);
if (!
Modifier.
isStatic(
f.
getModifiers()))
throw new
OgnlException("Field " +
fieldName + " of class " +
className + " is not static");
return
f.
get(null);
}
} catch (
ClassNotFoundException e) {
reason =
e;
} catch (
NoSuchFieldException e) {
reason =
e;
} catch (
SecurityException e) {
reason =
e;
} catch (
IllegalAccessException e) {
reason =
e;
}
throw new
OgnlException("Could not get static field " +
fieldName + " from class " +
className,
reason);
}
public static
List getDeclaredMethods(
Class targetClass,
String propertyName, boolean
findSets)
{
List result = null;
ClassCache cache =
_declaredMethods[
findSets ? 0 : 1];
synchronized (
cache) {
Map propertyCache = (
Map)
cache.
get(
targetClass);
if ((
propertyCache == null) || ((
result = (
List)
propertyCache.
get(
propertyName)) == null)) {
String baseName =
Character.
toUpperCase(
propertyName.
charAt(0)) +
propertyName.
substring(1);
for (
Class c =
targetClass;
c != null;
c =
c.
getSuperclass()) {
Method[]
methods =
c.
getDeclaredMethods();
for (int
i = 0;
i <
methods.length;
i++) {
if (!
isMethodCallable(
methods[
i]))
continue;
String ms =
methods[
i].
getName();
if (
ms.
endsWith(
baseName)) {
boolean
isSet = false,
isIs = false;
if ((
isSet =
ms.
startsWith(
SET_PREFIX)) ||
ms.
startsWith(
GET_PREFIX)
|| (
isIs =
ms.
startsWith(
IS_PREFIX))) {
int
prefixLength = (
isIs ? 2 : 3);
if (
isSet ==
findSets) {
if (
baseName.
length() == (
ms.
length() -
prefixLength)) {
if (
result == null) {
result = new
ArrayList();
}
result.
add(
methods[
i]);
}
}
}
}
}
}
if (
propertyCache == null) {
cache.
put(
targetClass,
propertyCache = new
HashMap(101));
}
propertyCache.
put(
propertyName, (
result == null) ?
NotFoundList :
result);
}
return (
result ==
NotFoundList) ? null :
result;
}
}
/**
* Convenience used to check if a method is volatile or synthetic so as to avoid
* calling un-callable methods.
*
* @param m The method to check.
* @return True if the method should be callable, false otherwise.
*/
static boolean
isMethodCallable(
Method m)
{
if ((
isJdk15() &&
m.
isSynthetic()) ||
Modifier.
isVolatile(
m.
getModifiers()))
return false;
return true;
}
public static
Method getGetMethod(
OgnlContext context,
Class targetClass,
String propertyName)
throws
IntrospectionException,
OgnlException
{
Method result = null;
List methods =
getDeclaredMethods(
targetClass,
propertyName, false /* find 'get' methods */);
if (
methods != null)
{
for (int
i = 0,
icount =
methods.
size();
i <
icount;
i++)
{
Method m = (
Method)
methods.
get(
i);
Class[]
mParameterTypes =
findParameterTypes(
targetClass,
m); //getParameterTypes(m);
if (
mParameterTypes.length == 0)
{
result =
m;
break;
}
}
}
return
result;
}
public static boolean
isMethodAccessible(
OgnlContext context,
Object target,
Method method,
String propertyName)
{
return (
method != null) &&
context.
getMemberAccess().
isAccessible(
context,
target,
method,
propertyName);
}
public static boolean
hasGetMethod(
OgnlContext context,
Object target,
Class targetClass,
String propertyName)
throws
IntrospectionException,
OgnlException
{
return
isMethodAccessible(
context,
target,
getGetMethod(
context,
targetClass,
propertyName),
propertyName);
}
public static
Method getSetMethod(
OgnlContext context,
Class targetClass,
String propertyName)
throws
IntrospectionException,
OgnlException
{
Method result = null;
List methods =
getDeclaredMethods(
targetClass,
propertyName, true /* find 'set' methods */);
if (
methods != null)
{
for (int
i = 0,
icount =
methods.
size();
i <
icount;
i++)
{
Method m = (
Method)
methods.
get(
i);
Class[]
mParameterTypes =
findParameterTypes(
targetClass,
m); //getParameterTypes(m);
if (
mParameterTypes.length == 1) {
result =
m;
break;
}
}
}
return
result;
}
public static final boolean
hasSetMethod(
OgnlContext context,
Object target,
Class targetClass,
String propertyName)
throws
IntrospectionException,
OgnlException
{
return
isMethodAccessible(
context,
target,
getSetMethod(
context,
targetClass,
propertyName),
propertyName);
}
public static final boolean
hasGetProperty(
OgnlContext context,
Object target,
Object oname)
throws
IntrospectionException,
OgnlException
{
Class targetClass = (
target == null) ? null :
target.
getClass();
String name =
oname.
toString();
return
hasGetMethod(
context,
target,
targetClass,
name) ||
hasField(
context,
target,
targetClass,
name);
}
public static final boolean
hasSetProperty(
OgnlContext context,
Object target,
Object oname)
throws
IntrospectionException,
OgnlException
{
Class targetClass = (
target == null) ? null :
target.
getClass();
String name =
oname.
toString();
return
hasSetMethod(
context,
target,
targetClass,
name) ||
hasField(
context,
target,
targetClass,
name);
}
private static final boolean
indexMethodCheck(
List methods)
{
boolean
result = false;
if (
methods.
size() > 0) {
Method fm = (
Method)
methods.
get(0);
Class[]
fmpt =
getParameterTypes(
fm);
int
fmpc =
fmpt.length;
Class lastMethodClass =
fm.
getDeclaringClass();
result = true;
for (int
i = 1;
result && (
i <
methods.
size());
i++) {
Method m = (
Method)
methods.
get(
i);
Class c =
m.
getDeclaringClass();
// Check to see if more than one method implemented per class
if (
lastMethodClass ==
c) {
result = false;
} else {
Class[]
mpt =
getParameterTypes(
fm);
int
mpc =
fmpt.length;
if (
fmpc !=
mpc) {
result = false;
}
for (int
j = 0;
j <
fmpc;
j++) {
if (
fmpt[
j] !=
mpt[
j]) {
result = false;
break;
}
}
}
lastMethodClass =
c;
}
}
return
result;
}
static void
findObjectIndexedPropertyDescriptors(
Class targetClass,
Map intoMap)
throws
OgnlException
{
Map allMethods =
getMethods(
targetClass, false);
Map pairs = new
HashMap(101);
for (
Iterator it =
allMethods.
keySet().
iterator();
it.
hasNext();) {
String methodName = (
String)
it.
next();
List methods = (
List)
allMethods.
get(
methodName);
/*
* Only process set/get where there is exactly one implementation of the method per
* class and those implementations are all the same
*/
if (
indexMethodCheck(
methods)) {
boolean
isGet = false,
isSet = false;
Method m = (
Method)
methods.
get(0);
if (((
isSet =
methodName.
startsWith(
SET_PREFIX)) || (
isGet =
methodName.
startsWith(
GET_PREFIX)))
&& (
methodName.
length() > 3)) {
String propertyName =
Introspector.
decapitalize(
methodName.
substring(3));
Class[]
parameterTypes =
getParameterTypes(
m);
int
parameterCount =
parameterTypes.length;
if (
isGet && (
parameterCount == 1) && (
m.
getReturnType() !=
Void.
TYPE)) {
List pair = (
List)
pairs.
get(
propertyName);
if (
pair == null) {
pairs.
put(
propertyName,
pair = new
ArrayList());
}
pair.
add(
m);
}
if (
isSet && (
parameterCount == 2) && (
m.
getReturnType() ==
Void.
TYPE)) {
List pair = (
List)
pairs.
get(
propertyName);
if (
pair == null) {
pairs.
put(
propertyName,
pair = new
ArrayList());
}
pair.
add(
m);
}
}
}
}
for (
Iterator it =
pairs.
keySet().
iterator();
it.
hasNext();) {
String propertyName = (
String)
it.
next();
List methods = (
List)
pairs.
get(
propertyName);
if (
methods.
size() == 2) {
Method method1 = (
Method)
methods.
get(0),
method2 = (
Method)
methods.
get(1),
setMethod = (
method1
.
getParameterTypes().length == 2) ?
method1 :
method2,
getMethod = (
setMethod ==
method1) ?
method2
:
method1;
Class keyType =
getMethod.
getParameterTypes()[0],
propertyType =
getMethod.
getReturnType();
if (
keyType ==
setMethod.
getParameterTypes()[0]) {
if (
propertyType ==
setMethod.
getParameterTypes()[1]) {
ObjectIndexedPropertyDescriptor propertyDescriptor;
try {
propertyDescriptor = new
ObjectIndexedPropertyDescriptor(
propertyName,
propertyType,
getMethod,
setMethod);
} catch (
Exception ex) {
throw new
OgnlException("creating object indexed property descriptor for '" +
propertyName
+ "' in " +
targetClass,
ex);
}
intoMap.
put(
propertyName,
propertyDescriptor);
}
}
}
}
}
/**
* This method returns the property descriptors for the given class as a Map.
*
* @param targetClass The class to get the descriptors for.
* @return Map map of property descriptors for class.
*
* @throws IntrospectionException on errors using {@link Introspector}.
* @throws OgnlException On general errors.
*/
public static
Map getPropertyDescriptors(
Class targetClass)
throws
IntrospectionException,
OgnlException
{
Map result;
synchronized (
_propertyDescriptorCache) {
if ((
result = (
Map)
_propertyDescriptorCache.
get(
targetClass)) == null)
{
PropertyDescriptor[]
pda =
Introspector.
getBeanInfo(
targetClass).
getPropertyDescriptors();
result = new
HashMap(101);
for (int
i = 0,
icount =
pda.length;
i <
icount;
i++)
{
// workaround for Introspector bug 6528714 (bugs.sun.com)
if (
pda[
i].
getReadMethod() != null && !
isMethodCallable(
pda[
i].
getReadMethod()))
{
pda[
i].
setReadMethod(
findClosestMatchingMethod(
targetClass,
pda[
i].
getReadMethod(),
pda[
i].
getName(),
pda[
i].
getPropertyType(), true));
}
if (
pda[
i].
getWriteMethod() != null && !
isMethodCallable(
pda[
i].
getWriteMethod()))
{
pda[
i].
setWriteMethod(
findClosestMatchingMethod(
targetClass,
pda[
i].
getWriteMethod(),
pda[
i].
getName(),
pda[
i].
getPropertyType(), false));
}
result.
put(
pda[
i].
getName(),
pda[
i]);
}
findObjectIndexedPropertyDescriptors(
targetClass,
result);
_propertyDescriptorCache.
put(
targetClass,
result);
}
}
return
result;
}
/**
* This method returns a PropertyDescriptor for the given class and property name using a Map
* lookup (using getPropertyDescriptorsMap()).
*/
public static
PropertyDescriptor getPropertyDescriptor(
Class targetClass,
String propertyName)
throws
IntrospectionException,
OgnlException
{
if (
targetClass == null)
return null;
return (
PropertyDescriptor)
getPropertyDescriptors(
targetClass).
get(
propertyName);
}
static
Method findClosestMatchingMethod(
Class targetClass,
Method m,
String propertyName,
Class propertyType, boolean
isReadMethod)
{
List methods =
getDeclaredMethods(
targetClass,
propertyName, !
isReadMethod);
for (int
i=0;
i <
methods.
size();
i++)
{
Method method = (
Method)
methods.
get(
i);
if (
method.
getName().
equals(
m.
getName())
&&
m.
getReturnType().
isAssignableFrom(
m.
getReturnType())
&&
method.
getReturnType() ==
propertyType
&&
method.
getParameterTypes().length ==
m.
getParameterTypes().length) {
return
method;
}
}
return
m;
}
public static
PropertyDescriptor[]
getPropertyDescriptorsArray(
Class targetClass)
throws
IntrospectionException
{
PropertyDescriptor[]
result = null;
if (
targetClass != null) {
synchronized (
_propertyDescriptorCache) {
if ((
result = (
PropertyDescriptor[])
_propertyDescriptorCache.
get(
targetClass)) == null) {
_propertyDescriptorCache.
put(
targetClass,
result =
Introspector.
getBeanInfo(
targetClass)
.
getPropertyDescriptors());
}
}
}
return
result;
}
/**
* Gets the property descriptor with the given name for the target class given.
*
* @param targetClass Class for which property descriptor is desired
* @param name Name of property
* @return PropertyDescriptor of the named property or null if the class has no property with
* the given name
*/
public static
PropertyDescriptor getPropertyDescriptorFromArray(
Class targetClass,
String name)
throws
IntrospectionException
{
PropertyDescriptor result = null;
PropertyDescriptor[]
pda =
getPropertyDescriptorsArray(
targetClass);
for (int
i = 0,
icount =
pda.length; (
result == null) && (
i <
icount);
i++) {
if (
pda[
i].
getName().
compareTo(
name) == 0) {
result =
pda[
i];
}
}
return
result;
}
public static void
setMethodAccessor(
Class cls,
MethodAccessor accessor)
{
synchronized (
_methodAccessors) {
_methodAccessors.
put(
cls,
accessor);
}
}
public static
MethodAccessor getMethodAccessor(
Class cls)
throws
OgnlException
{
MethodAccessor answer = (
MethodAccessor)
getHandler(
cls,
_methodAccessors);
if (
answer != null)
return
answer;
throw new
OgnlException("No method accessor for " +
cls);
}
public static void
setPropertyAccessor(
Class cls,
PropertyAccessor accessor)
{
synchronized (
_propertyAccessors) {
_propertyAccessors.
put(
cls,
accessor);
}
}
public static
PropertyAccessor getPropertyAccessor(
Class cls)
throws
OgnlException
{
PropertyAccessor answer = (
PropertyAccessor)
getHandler(
cls,
_propertyAccessors);
if (
answer != null)
return
answer;
throw new
OgnlException("No property accessor for class " +
cls);
}
public static
ElementsAccessor getElementsAccessor(
Class cls)
throws
OgnlException
{
ElementsAccessor answer = (
ElementsAccessor)
getHandler(
cls,
_elementsAccessors);
if (
answer != null)
return
answer;
throw new
OgnlException("No elements accessor for class " +
cls);
}
public static void
setElementsAccessor(
Class cls,
ElementsAccessor accessor)
{
synchronized (
_elementsAccessors) {
_elementsAccessors.
put(
cls,
accessor);
}
}
public static
NullHandler getNullHandler(
Class cls)
throws
OgnlException
{
NullHandler answer = (
NullHandler)
getHandler(
cls,
_nullHandlers);
if (
answer != null)
return
answer;
throw new
OgnlException("No null handler for class " +
cls);
}
public static void
setNullHandler(
Class cls,
NullHandler handler)
{
synchronized (
_nullHandlers) {
_nullHandlers.
put(
cls,
handler);
}
}
private static
Object getHandler(
Class forClass,
ClassCache handlers)
{
Object answer = null;
synchronized (
handlers) {
if ((
answer =
handlers.
get(
forClass)) == null) {
Class keyFound;
if (
forClass.
isArray()) {
answer =
handlers.
get(
Object[].class);
keyFound = null;
} else {
keyFound =
forClass;
outer:
for (
Class c =
forClass;
c != null;
c =
c.
getSuperclass()) {
answer =
handlers.
get(
c);
if (
answer == null) {
Class[]
interfaces =
c.
getInterfaces();
for (int
index = 0,
count =
interfaces.length;
index <
count; ++
index) {
Class iface =
interfaces[
index];
answer =
handlers.
get(
iface);
if (
answer == null) {
/* Try super-interfaces */
answer =
getHandler(
iface,
handlers);
}
if (
answer != null) {
keyFound =
iface;
break
outer;
}
}
} else {
keyFound =
c;
break;
}
}
}
if (
answer != null) {
if (
keyFound !=
forClass) {
handlers.
put(
forClass,
answer);
}
}
}
}
return
answer;
}
public static
Object getProperty(
OgnlContext context,
Object source,
Object name)
throws
OgnlException
{
PropertyAccessor accessor;
if (
source == null)
{
throw new
OgnlException("source is null for getProperty(null, \"" +
name + "\")");
}
if ((
accessor =
getPropertyAccessor(
getTargetClass(
source))) == null)
{
throw new
OgnlException("No property accessor for " +
getTargetClass(
source).
getName());
}
return
accessor.
getProperty(
context,
source,
name);
}
public static void
setProperty(
OgnlContext context,
Object target,
Object name,
Object value)
throws
OgnlException
{
PropertyAccessor accessor;
if (
target == null) {
throw new
OgnlException("target is null for setProperty(null, \"" +
name + "\", " +
value + ")");
}
if ((
accessor =
getPropertyAccessor(
getTargetClass(
target))) == null) {
throw new
OgnlException("No property accessor for " +
getTargetClass(
target).
getName());
}
accessor.
setProperty(
context,
target,
name,
value);
}
/**
* Determines the index property type, if any. Returns <code>INDEXED_PROPERTY_NONE</code> if
* the property is not index-accessible as determined by OGNL or JavaBeans. If it is indexable
* then this will return whether it is a JavaBeans indexed property, conforming to the indexed
* property patterns (returns <code>INDEXED_PROPERTY_INT</code>) or if it conforms to the
* OGNL arbitrary object indexable (returns <code>INDEXED_PROPERTY_OBJECT</code>).
*/
public static int
getIndexedPropertyType(
OgnlContext context,
Class sourceClass,
String name)
throws
OgnlException
{
int
result =
INDEXED_PROPERTY_NONE;
try {
PropertyDescriptor pd =
getPropertyDescriptor(
sourceClass,
name);
if (
pd != null) {
if (
pd instanceof
IndexedPropertyDescriptor) {
result =
INDEXED_PROPERTY_INT;
} else {
if (
pd instanceof
ObjectIndexedPropertyDescriptor) {
result =
INDEXED_PROPERTY_OBJECT;
}
}
}
} catch (
Exception ex) {
throw new
OgnlException("problem determining if '" +
name + "' is an indexed property",
ex);
}
return
result;
}
public static
Object getIndexedProperty(
OgnlContext context,
Object source,
String name,
Object index)
throws
OgnlException
{
Object[]
args =
_objectArrayPool.
create(
index);
try {
PropertyDescriptor pd =
getPropertyDescriptor((
source == null) ? null :
source.
getClass(),
name);
Method m;
if (
pd instanceof
IndexedPropertyDescriptor) {
m = ((
IndexedPropertyDescriptor)
pd).
getIndexedReadMethod();
} else {
if (
pd instanceof
ObjectIndexedPropertyDescriptor) {
m = ((
ObjectIndexedPropertyDescriptor)
pd).
getIndexedReadMethod();
} else {
throw new
OgnlException("property '" +
name + "' is not an indexed property");
}
}
return
callMethod(
context,
source,
m.
getName(),
args);
} catch (
OgnlException ex) {
throw
ex;
} catch (
Exception ex) {
throw new
OgnlException("getting indexed property descriptor for '" +
name + "'",
ex);
} finally {
_objectArrayPool.
recycle(
args);
}
}
public static void
setIndexedProperty(
OgnlContext context,
Object source,
String name,
Object index,
Object value)
throws
OgnlException
{
Object[]
args =
_objectArrayPool.
create(
index,
value);
try {
PropertyDescriptor pd =
getPropertyDescriptor((
source == null) ? null :
source.
getClass(),
name);
Method m;
if (
pd instanceof
IndexedPropertyDescriptor) {
m = ((
IndexedPropertyDescriptor)
pd).
getIndexedWriteMethod();
} else {
if (
pd instanceof
ObjectIndexedPropertyDescriptor) {
m = ((
ObjectIndexedPropertyDescriptor)
pd).
getIndexedWriteMethod();
} else {
throw new
OgnlException("property '" +
name + "' is not an indexed property");
}
}
callMethod(
context,
source,
m.
getName(),
args);
} catch (
OgnlException ex) {
throw
ex;
} catch (
Exception ex) {
throw new
OgnlException("getting indexed property descriptor for '" +
name + "'",
ex);
} finally {
_objectArrayPool.
recycle(
args);
}
}
public static
EvaluationPool getEvaluationPool()
{
return
_evaluationPool;
}
public static
ObjectArrayPool getObjectArrayPool()
{
return
_objectArrayPool;
}
/**
* Registers the specified {@link ClassCacheInspector} with all class reflection based internal
* caches. This may have a significant performance impact so be careful using this in production scenarios.
*
* @param inspector
* The inspector instance that will be registered with all internal cache instances.
*/
public static void
setClassCacheInspector(
ClassCacheInspector inspector)
{
_cacheInspector =
inspector;
_propertyDescriptorCache.
setClassInspector(
_cacheInspector);
_constructorCache.
setClassInspector(
_cacheInspector);
_staticMethodCache.
setClassInspector(
_cacheInspector);
_instanceMethodCache.
setClassInspector(
_cacheInspector);
_invokePermissionCache.
setClassInspector(
_cacheInspector);
_fieldCache.
setClassInspector(
_cacheInspector);
_declaredMethods[0].
setClassInspector(
_cacheInspector);
_declaredMethods[1].
setClassInspector(
_cacheInspector);
}
public static
Method getMethod(
OgnlContext context,
Class target,
String name,
Node[]
children, boolean
includeStatic)
throws
Exception
{
Class[]
parms;
if (
children != null &&
children.length > 0)
{
parms = new
Class[
children.length];
// used to reset context after loop
Class currType =
context.
getCurrentType();
Class currAccessor =
context.
getCurrentAccessor();
Object cast =
context.
get(
ExpressionCompiler.
PRE_CAST);
context.
setCurrentObject(
context.
getRoot());
context.
setCurrentType(
context.
getRoot() != null ?
context.
getRoot().
getClass() : null);
context.
setCurrentAccessor(null);
context.
setPreviousType(null);
for (int
i=0;
i <
children.length;
i++)
{
children[
i].
toGetSourceString(
context,
context.
getRoot());
parms[
i] =
context.
getCurrentType();
}
context.
put(
ExpressionCompiler.
PRE_CAST,
cast);
context.
setCurrentType(
currType);
context.
setCurrentAccessor(
currAccessor);
context.
setCurrentObject(
target);
} else
{
parms = new
Class[0];
}
List methods =
OgnlRuntime.
getMethods(
target,
name,
includeStatic);
if (
methods == null)
return null;
for (int
i = 0;
i <
methods.
size();
i++)
{
Method m = (
Method)
methods.
get(
i);
boolean
varArgs =
isJdk15() &&
m.
isVarArgs();
if (
parms.length !=
m.
getParameterTypes().length && !
varArgs)
continue;
Class[]
mparms =
m.
getParameterTypes();
boolean
matched = true;
for (int
p = 0;
p <
mparms.length;
p++)
{
if (
varArgs &&
mparms[
p].
isArray()){
continue;
}
if (
parms[
p] == null)
{
matched = false;
break;
}
if (
parms[
p] ==
mparms[
p])
continue;
if (
mparms[
p].
isPrimitive()
&&
Character.
TYPE !=
mparms[
p] &&
Byte.
TYPE !=
mparms[
p]
&&
Number.class.
isAssignableFrom(
parms[
p])
&&
OgnlRuntime.
getPrimitiveWrapperClass(
parms[
p]) ==
mparms[
p])
{
continue;
}
matched = false;
break;
}
if (
matched)
return
m;
}
return null;
}
/**
* Finds the best possible match for a method on the specified target class with a matching
* name.
*
* <p>
* The name matched will also try different combinations like <code>is + name, has + name, get + name, etc..</code>
* </p>
*
* @param target
* The class to find a matching method against.
* @param name
* The name of the method.
* @return The most likely matching {@link Method}, or null if none could be found.
*/
public static
Method getReadMethod(
Class target,
String name)
{
return
getReadMethod(
target,
name, -1);
}
public static
Method getReadMethod(
Class target,
String name, int
numParms)
{
try {
name =
name.
replaceAll("\"", "").
toLowerCase();
BeanInfo info =
Introspector.
getBeanInfo(
target);
MethodDescriptor[]
methods =
info.
getMethodDescriptors();
// exact matches first
Method m = null;
for (int
i = 0;
i <
methods.length;
i++)
{
if (!
isMethodCallable(
methods[
i].
getMethod()))
continue;
if ((
methods[
i].
getName().
equalsIgnoreCase(
name)
||
methods[
i].
getName().
toLowerCase().
equals(
name)
||
methods[
i].
getName().
toLowerCase().
equals("get" +
name)
||
methods[
i].
getName().
toLowerCase().
equals("has" +
name)
||
methods[
i].
getName().
toLowerCase().
equals("is" +
name))
&& !
methods[
i].
getName().
startsWith("set"))
{
if (
numParms > 0 &&
methods[
i].
getMethod().
getParameterTypes().length ==
numParms)
return
methods[
i].
getMethod();
else if (
numParms < 0)
{
if ((
m != null &&
m.
getParameterTypes().length >
methods[
i].
getMethod().
getParameterTypes().length)
||
m == null)
{
m =
methods[
i].
getMethod();
}
}
}
}
if (
m != null)
return
m;
for (int
i = 0;
i <
methods.length;
i++)
{
if (!
isMethodCallable(
methods[
i].
getMethod()))
continue;
if (
methods[
i].
getName().
toLowerCase().
endsWith(
name)
&& !
methods[
i].
getName().
startsWith("set")
&&
methods[
i].
getMethod().
getReturnType() !=
Void.
TYPE) {
if (
numParms > 0 &&
methods[
i].
getMethod().
getParameterTypes().length ==
numParms)
return
methods[
i].
getMethod();
else if (
numParms < 0)
{
if ((
m != null &&
m.
getParameterTypes().length >
methods[
i].
getMethod().
getParameterTypes().length)
||
m == null)
{
m =
methods[
i].
getMethod();
}
}
}
}
if (
m != null)
return
m;
// try one last time adding a get to beginning
if (!
name.
startsWith("get"))
return
OgnlRuntime.
getReadMethod(
target, "get" +
name,
numParms);
} catch (
Throwable t)
{
throw
OgnlOps.
castToRuntime(
t);
}
return null;
}
public static
Method getWriteMethod(
Class target,
String name)
{
return
getWriteMethod(
target,
name, -1);
}
public static
Method getWriteMethod(
Class target,
String name, int
numParms)
{
try {
name =
name.
replaceAll("\"", "");
BeanInfo info =
Introspector.
getBeanInfo(
target);
MethodDescriptor[]
methods =
info.
getMethodDescriptors();
for (int
i = 0;
i <
methods.length;
i++) {
if (!
isMethodCallable(
methods[
i].
getMethod()))
continue;
if ((
methods[
i].
getName().
equalsIgnoreCase(
name)
||
methods[
i].
getName().
toLowerCase().
equals(
name.
toLowerCase())
||
methods[
i].
getName().
toLowerCase().
equals("set" +
name.
toLowerCase()))
&& !
methods[
i].
getName().
startsWith("get")) {
if (
numParms > 0 &&
methods[
i].
getMethod().
getParameterTypes().length ==
numParms)
return
methods[
i].
getMethod();
else if (
numParms < 0)
return
methods[
i].
getMethod();
}
}
// try again on pure class
Method[]
cmethods =
target.
getClass().
getMethods();
for (int
i = 0;
i <
cmethods.length;
i++) {
if (!
isMethodCallable(
cmethods[
i]))
continue;
if ((
cmethods[
i].
getName().
equalsIgnoreCase(
name)
||
cmethods[
i].
getName().
toLowerCase().
equals(
name.
toLowerCase())
||
cmethods[
i].
getName().
toLowerCase().
equals("set" +
name.
toLowerCase()))
&& !
cmethods[
i].
getName().
startsWith("get")) {
if (
numParms > 0 &&
cmethods[
i].
getParameterTypes().length ==
numParms)
return
cmethods[
i];
else if (
numParms < 0)
return
cmethods[
i];
}
}
// try one last time adding a set to beginning
if (!
name.
startsWith("set")) {
return
OgnlRuntime.
getReadMethod(
target, "set" +
name,
numParms);
}
} catch (
Throwable t)
{
throw
OgnlOps.
castToRuntime(
t);
}
return null;
}
public static
PropertyDescriptor getProperty(
Class target,
String name)
{
try {
BeanInfo info =
Introspector.
getBeanInfo(
target);
PropertyDescriptor[]
pds =
info.
getPropertyDescriptors();
for (int
i = 0;
i <
pds.length;
i++) {
if (
pds[
i].
getName().
equalsIgnoreCase(
name)
||
pds[
i].
getName().
toLowerCase().
equals(
name.
toLowerCase())
||
pds[
i].
getName().
toLowerCase().
endsWith(
name.
toLowerCase()))
return
pds[
i];
}
} catch (
Throwable t)
{
throw
OgnlOps.
castToRuntime(
t);
}
return null;
}
public static boolean
isBoolean(
String expression)
{
if (
expression == null)
return false;
if ("true".
equals(
expression) || "false".
equals(
expression)
|| "!true".
equals(
expression) || "!false".
equals(
expression)
|| "(true)".
equals(
expression)
|| "!(true)".
equals(
expression)
|| "(false)".
equals(
expression)
|| "!(false)".
equals(
expression)
||
expression.
startsWith("ognl.OgnlOps"))
return true;
return false;
}
/**
* Compares the {@link OgnlContext#getCurrentType()} and {@link OgnlContext#getPreviousType()} class types
* on the stack to determine if a numeric expression should force object conversion.
* <p/>
* <p/>
* Normally used in conjunction with the <code>forceConversion</code> parameter of
* {@link OgnlRuntime#getChildSource(OgnlContext,Object,Node,boolean)}.
* </p>
*
* @param context The current context.
* @return True, if the class types on the stack wouldn't be comparable in a pure numeric expression such as <code>o1 >= o2</code>.
*/
public static boolean
shouldConvertNumericTypes(
OgnlContext context)
{
if (
context.
getCurrentType() == null ||
context.
getPreviousType() == null)
return true;
if (
context.
getCurrentType() ==
context.
getPreviousType()
&&
context.
getCurrentType().
isPrimitive() &&
context.
getPreviousType().
isPrimitive())
return false;
return
context.
getCurrentType() != null && !
context.
getCurrentType().
isArray()
&&
context.
getPreviousType() != null && !
context.
getPreviousType().
isArray();
}
/**
* Attempts to get the java source string represented by the specific child expression
* via the {@link JavaSource#toGetSourceString(OgnlContext,Object)} interface method.
*
* @param context The ognl context to pass to the child.
* @param target The current object target to use.
* @param child The child expression.
* @return The result of calling {@link JavaSource#toGetSourceString(OgnlContext,Object)} plus additional
* enclosures of {@link OgnlOps#convertValue(Object,Class,boolean)} for conversions.
* @throws OgnlException Mandatory exception throwing catching.. (blehh)
*/
public static
String getChildSource(
OgnlContext context,
Object target,
Node child)
throws
OgnlException
{
return
getChildSource(
context,
target,
child, false);
}
/**
* Attempts to get the java source string represented by the specific child expression
* via the {@link JavaSource#toGetSourceString(OgnlContext,Object)} interface method.
*
* @param context The ognl context to pass to the child.
* @param target The current object target to use.
* @param child The child expression.
* @param forceConversion If true, forces {@link OgnlOps#convertValue(Object,Class)} conversions on the objects.
* @return The result of calling {@link JavaSource#toGetSourceString(OgnlContext,Object)} plus additional
* enclosures of {@link OgnlOps#convertValue(Object,Class,boolean)} for conversions.
* @throws OgnlException Mandatory exception throwing catching.. (blehh)
*/
public static
String getChildSource(
OgnlContext context,
Object target,
Node child, boolean
forceConversion)
throws
OgnlException
{
String pre = (
String)
context.
get("_currentChain");
if (
pre == null)
pre = "";
try
{
child.
getValue(
context,
target);
} catch (
NullPointerException e)
{
// ignore
} catch (
ArithmeticException e)
{
context.
setCurrentType(int.class);
return "0";
} catch (
Throwable t)
{
throw
OgnlOps.
castToRuntime(
t);
}
String source = null;
try
{
source =
child.
toGetSourceString(
context,
target);
}
catch (
Throwable t)
{
throw
OgnlOps.
castToRuntime(
t);
}
// handle root / method expressions that may not have proper root java source access
if (!
ASTConst.class.
isInstance(
child)
&& (
target == null ||
context.
getRoot() !=
target))
{
source =
pre +
source;
}
if (
context.
getRoot() != null)
{
source =
ExpressionCompiler.
getRootExpression(
child,
context.
getRoot(),
context) +
source;
context.
setCurrentAccessor(
context.
getRoot().
getClass());
}
if (
ASTChain.class.
isInstance(
child))
{
String cast = (
String)
context.
remove(
ExpressionCompiler.
PRE_CAST);
if (
cast == null)
cast = "";
source =
cast +
source;
}
if (
source == null ||
source.
trim().
length() < 1)
source = "null";
return
source;
}
}