package org.springframework.data.projection;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
class SpelEvaluatingMethodInterceptor implements MethodInterceptor {
private static final ParserContext PARSER_CONTEXT = new TemplateParserContext();
private final EvaluationContext evaluationContext;
private final MethodInterceptor delegate;
private final Map<Integer, Expression> expressions;
private final Object target;
public SpelEvaluatingMethodInterceptor(MethodInterceptor delegate, Object target, @Nullable BeanFactory beanFactory,
SpelExpressionParser parser, Class<?> targetInterface) {
Assert.notNull(delegate, "Delegate MethodInterceptor must not be null!");
Assert.notNull(target, "Target object must not be null!");
Assert.notNull(parser, "SpelExpressionParser must not be null!");
Assert.notNull(targetInterface, "Target interface must not be null!");
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
if (target instanceof Map) {
evaluationContext.addPropertyAccessor(new MapAccessor());
}
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
this.expressions = potentiallyCreateExpressionsForMethodsOnTargetInterface(parser, targetInterface);
this.evaluationContext = evaluationContext;
this.delegate = delegate;
this.target = target;
}
private static Map<Integer, Expression> potentiallyCreateExpressionsForMethodsOnTargetInterface(
SpelExpressionParser parser, Class<?> targetInterface) {
Map<Integer, Expression> expressions = new HashMap<>();
for (Method method : targetInterface.getMethods()) {
if (!method.isAnnotationPresent(Value.class)) {
continue;
}
Value value = method.getAnnotation(Value.class);
if (!StringUtils.hasText(value.value())) {
throw new IllegalStateException(String.format("@Value annotation on %s contains empty expression!", method));
}
expressions.put(method.hashCode(), parser.parseExpression(value.value(), PARSER_CONTEXT));
}
return Collections.unmodifiableMap(expressions);
}
@Nullable
@Override
public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) throws Throwable {
Expression expression = expressions.get(invocation.getMethod().hashCode());
if (expression == null) {
return delegate.invoke(invocation);
}
return expression.getValue(evaluationContext, TargetWrapper.of(target, invocation.getArguments()));
}
static final class TargetWrapper {
private final Object target;
private final Object[] args;
private TargetWrapper(Object target, Object[] args) {
this.target = target;
this.args = args;
}
public static TargetWrapper of(Object target, Object[] args) {
return new TargetWrapper(target, args);
}
public Object getTarget() {
return this.target;
}
public Object[] getArgs() {
return this.args;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof TargetWrapper)) {
return false;
}
TargetWrapper that = (TargetWrapper) o;
if (!ObjectUtils.nullSafeEquals(target, that.target)) {
return false;
}
return ObjectUtils.nullSafeEquals(args, that.args);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(target);
result = 31 * result + ObjectUtils.nullSafeHashCode(args);
return result;
}
@Override
public String toString() {
return "SpelEvaluatingMethodInterceptor.TargetWrapper(target=" + this.getTarget() + ", args="
+ java.util.Arrays.deepToString(this.getArgs()) + ")";
}
}
}