org.apache.commons.jexl2.internal.Introspector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-jexl Show documentation
Show all versions of commons-jexl Show documentation
Jexl is an implementation of the JSTL Expression Language with extensions.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jexl2.internal;
import java.lang.ref.SoftReference;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import org.apache.commons.jexl2.internal.introspection.IntrospectorBase;
import org.apache.commons.jexl2.internal.introspection.MethodKey;
import org.apache.commons.logging.Log;
/**
* Default introspection services.
* Finding methods as well as property getters & setters.
* @since 1.0
*/
public class Introspector {
/** The logger to use for all warnings & errors. */
protected final Log rlog;
/** The soft reference to the introspector currently in use. */
private volatile SoftReference ref;
/**
* Creates an introspector.
* @param log the logger to use for warnings.
*/
protected Introspector(Log log) {
rlog = log;
ref = new SoftReference(null);
}
/**
* Coerce an Object to an Integer.
* @param arg the Object to coerce
* @return an Integer if it can be converted, null otherwise
*/
protected Integer toInteger(Object arg) {
if (arg == null) {
return null;
}
if (arg instanceof Number) {
return Integer.valueOf(((Number) arg).intValue());
}
try {
return Integer.valueOf(arg.toString());
} catch (NumberFormatException xnumber) {
return null;
}
}
/**
* Coerce an Object to a String.
* @param arg the Object to coerce
* @return a String if it can be converted, null otherwise
*/
protected String toString(Object arg) {
return arg == null ? null : arg.toString();
}
/**
* Gets the current introspector base.
* If the reference has been collected, this method will recreate the underlying introspector.
* @return the introspector
*/
// CSOFF: DoubleCheckedLocking
protected final IntrospectorBase base() {
IntrospectorBase intro = ref.get();
if (intro == null) {
// double checked locking is ok (fixed by Java 5 memory model).
synchronized(this) {
intro = ref.get();
if (intro == null) {
intro = new IntrospectorBase(rlog);
ref = new SoftReference(intro);
}
}
}
return intro;
}
// CSON: DoubleCheckedLocking
/**
* Sets the underlying class loader for class solving resolution.
* @param loader the loader to use
*/
public void setClassLoader(ClassLoader loader) {
base().setLoader(loader);
}
/**
* Gets a class by name through this introspector class loader.
* @param className the class name
* @return the class instance or null if it could not be found
*/
public Class> getClassByName(String className) {
return base().getClassByName(className);
}
/**
* Gets the field named by key
for the class c
.
*
* @param c Class in which the field search is taking place
* @param key Name of the field being searched for
* @return a {@link java.lang.reflect.Field} or null if it does not exist or is not accessible
* */
public final Field getField(Class> c, String key) {
return base().getField(c, key);
}
/**
* Gets the accessible field names known for a given class.
* @param c the class
* @return the class field names
*/
public final String[] getFieldNames(Class> c) {
return base().getFieldNames(c);
}
/**
* Gets the method defined by name
and
* params
for the Class c
.
*
* @param c Class in which the method search is taking place
* @param name Name of the method being searched for
* @param params An array of Objects (not Classes) that describe the
* the parameters
*
* @return a {@link java.lang.reflect.Method}
* or null if no unambiguous method could be found through introspection.
*/
public final Method getMethod(Class> c, String name, Object[] params) {
return base().getMethod(c, new MethodKey(name, params));
}
/**
* Gets the method defined by key
and for the Class c
.
*
* @param c Class in which the method search is taking place
* @param key MethodKey of the method being searched for
*
* @return a {@link java.lang.reflect.Method}
* or null if no unambiguous method could be found through introspection.
*/
public final Method getMethod(Class> c, MethodKey key) {
return base().getMethod(c, key);
}
/**
* Gets the accessible methods names known for a given class.
* @param c the class
* @return the class method names
*/
public final String[] getMethodNames(Class> c) {
return base().getMethodNames(c);
}
/**
* Gets all the methods with a given name from this map.
* @param c the class
* @param methodName the seeked methods name
* @return the array of methods
*/
public final Method[] getMethods(Class> c, final String methodName) {
return base().getMethods(c, methodName);
}
/**
* Returns a general constructor.
* @param ctorHandle the object
* @param args contructor arguments
* @return a {@link java.lang.reflect.Constructor}
* or null if no unambiguous contructor could be found through introspection.
*/
public final Constructor> getConstructor(Object ctorHandle, Object[] args) {
String className = null;
Class> clazz = null;
if (ctorHandle instanceof Class>) {
clazz = (Class>) ctorHandle;
className = clazz.getName();
} else if (ctorHandle != null) {
className = ctorHandle.toString();
} else {
return null;
}
return base().getConstructor(clazz, new MethodKey(className, args));
}
/**
* Returns a general method.
* @param obj the object
* @param name the method name
* @param args method arguments
* @return a {@link AbstractExecutor.Method}.
*/
public final AbstractExecutor.Method getMethodExecutor(Object obj, String name, Object[] args) {
AbstractExecutor.Method me = new MethodExecutor(this, obj, name, args);
return me.isAlive() ? me : null;
}
/**
* Return a property getter.
* @param obj the object to base the property from.
* @param identifier property name
* @return a {@link AbstractExecutor.Get}.
*/
public final AbstractExecutor.Get getGetExecutor(Object obj, Object identifier) {
final Class> claz = obj.getClass();
final String property = toString(identifier);
AbstractExecutor.Get executor;
// first try for a getFoo() type of property (also getfoo() )
if (property != null) {
executor = new PropertyGetExecutor(this, claz, property);
if (executor.isAlive()) {
return executor;
}
//}
// look for boolean isFoo()
//if (property != null) {
executor = new BooleanGetExecutor(this, claz, property);
if (executor.isAlive()) {
return executor;
}
}
// let's see if we are a map...
executor = new MapGetExecutor(this, claz, identifier);
if (executor.isAlive()) {
return executor;
}
// let's see if we can convert the identifier to an int,
// if obj is an array or a list, we can still do something
Integer index = toInteger(identifier);
if (index != null) {
executor = new ListGetExecutor(this, claz, index);
if (executor.isAlive()) {
return executor;
}
}
// if that didn't work, look for set("foo")
executor = new DuckGetExecutor(this, claz, identifier);
if (executor.isAlive()) {
return executor;
}
// if that didn't work, look for set("foo")
executor = new DuckGetExecutor(this, claz, property);
if (executor.isAlive()) {
return executor;
}
return null;
}
/**
* Return a property setter.
* @param obj the object to base the property from.
* @param identifier property name (or identifier)
* @param arg value to set
* @return a {@link AbstractExecutor.Set}.
*/
public final AbstractExecutor.Set getSetExecutor(final Object obj, final Object identifier, Object arg) {
final Class> claz = obj.getClass();
final String property = toString(identifier);
AbstractExecutor.Set executor;
// first try for a setFoo() type of property (also setfoo() )
if (property != null) {
executor = new PropertySetExecutor(this, claz, property, arg);
if (executor.isAlive()) {
return executor;
}
}
// let's see if we are a map...
executor = new MapSetExecutor(this, claz, identifier, arg);
if (executor.isAlive()) {
return executor;
}
// let's see if we can convert the identifier to an int,
// if obj is an array or a list, we can still do something
Integer index = toInteger(identifier);
if (index != null) {
executor = new ListSetExecutor(this, claz, index, arg);
if (executor.isAlive()) {
return executor;
}
}
// if that didn't work, look for set(foo)
executor = new DuckSetExecutor(this, claz, identifier, arg);
if (executor.isAlive()) {
return executor;
}
// if that didn't work, look for set("foo")
executor = new DuckSetExecutor(this, claz, property, arg);
if (executor.isAlive()) {
return executor;
}
return null;
}
}