spelhelper/src/main/java/net/abhinavsarkar/spelhelper/SpelHelper.java

144 lines
5.2 KiB
Java

package net.abhinavsarkar.spelhelper;
import static java.util.Arrays.asList;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
public final class SpelHelper {
public static final String CONTEXT_LOOKUP_KEY = SpelHelper.class.getName();
private static final ExpressionParser PARSER = new SpelExpressionParser();
private static final ThreadLocal<EvaluationContext> currentContext =
new ThreadLocal<EvaluationContext>();
private volatile EvaluationContext context;
private final Set<Method> registeredFunctions = new HashSet<Method>();
private final Map<String,Method> registeredMethods =
new ConcurrentHashMap<String, Method>();
private final Map<String,Constructor<?>> registeredConstructors =
new ConcurrentHashMap<String, Constructor<?>>();
{
registerFunctionsFromClass(ExtensionFunctions.class);
registerImplicitMethodsFromClass(ImplicitMethods.class);
}
public SpelHelper registerImplicitMethodsFromClass(final Class<?> clazz) {
for (Method method : filterMethods(clazz)) {
registeredMethods.put(String.format(
"%s.%s", method.getParameterTypes()[0].getName(), method.getName()),
method);
}
return this;
}
public SpelHelper registerFunctionsFromClass(final Class<?> clazz) {
registeredFunctions.addAll(filterMethods(clazz));
context = null;
return this;
}
public SpelHelper registerImplicitConstructorsFromClass(final Class<?> clazz) {
for (Constructor<?> constructor : asList(clazz.getConstructors())) {
registeredConstructors.put(
constructor.getDeclaringClass().getSimpleName()
+ Arrays.toString(constructor.getParameterTypes()),
constructor);
}
return this;
}
public <T> T evalExpression(final String expressionString,
final Object rootElement, final Class<T> desiredType) {
EvaluationContext evaluationContext = getEvaluationContext(rootElement);
currentContext.set(evaluationContext);
T value = evalExpression(expressionString, evaluationContext, desiredType);
currentContext.set(null);
return value;
}
public <T> T evalExpression(final String expressionString,
final EvaluationContext evaluationContext, final Class<T> desiredType) {
return PARSER.parseExpression(expressionString)
.getValue(evaluationContext, desiredType);
}
public <T> T evalExpressions(final String[] expressionStrings,
final Object rootElement, final Class<T> desiredType) {
int length = expressionStrings.length;
Assert.isTrue(length > 0,
"expressionStrings should have length more than 0");
for (int i = 0; i < length - 1; i++) {
evalExpression(expressionStrings[i], rootElement, Object.class);
}
return evalExpression(expressionStrings[length - 1],
rootElement, desiredType);
}
private EvaluationContext getEvaluationContext(final Object rootObject) {
if (context == null) {
synchronized (PARSER) {
if (context == null) {
StandardEvaluationContext newContext = new StandardEvaluationContext(rootObject);
newContext.getMethodResolvers().add(new ImplicitMethodResolver());
newContext.getPropertyAccessors().add(new ImplicitPropertyAccessor());
newContext.setConstructorResolvers(
asList((ConstructorResolver) new ImplicitConstructorResolver()));
for (Method method : registeredFunctions) {
newContext.setVariable(method.getName(), method);
}
newContext.setVariable(CONTEXT_LOOKUP_KEY, this);
context = newContext;
}
}
}
return context;
}
public Method lookupImplicitMethod(final String lookup) {
Assert.notNull(lookup);
return registeredMethods.get(lookup);
}
public Constructor<?> lookupImplicitConstructor(final String lookup) {
Assert.notNull(lookup);
return registeredConstructors.get(lookup);
}
public static EvaluationContext getCurrentContext() {
return currentContext.get();
}
private static List<Method> filterMethods(final Class<?> clazz) {
List<Method> allowedMethods = new ArrayList<Method>();
for (Method method : clazz.getMethods()) {
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)
&& !method.getReturnType().equals(Void.TYPE)
&& method.getParameterTypes().length > 0) {
allowedMethods.add(method);
}
}
return allowedMethods;
}
}