DATACMNS-533 - Expose special parameters in SpEL expressions.
We now expose special parameters like Pageable and Sort to be used in SpEL expressions if they don't collide with existing named parameters. Related pull request: spring-projects/spring-data-jpa#101 Related ticket: DATAJPA-564
This commit is contained in:
committed by
Oliver Gierke
parent
d23e7baa2c
commit
31848f87eb
@@ -42,6 +42,7 @@ import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.TypeUtils;
|
||||
|
||||
/**
|
||||
@@ -109,16 +110,53 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
|
||||
// Add parameters for indexed access
|
||||
ec.setRootObject(parameterValues);
|
||||
|
||||
// Add parameters as variables if named
|
||||
for (Parameter param : parameters) {
|
||||
if (param.isNamedParameter()) {
|
||||
ec.setVariable(param.getName(), parameterValues[param.getIndex()]);
|
||||
}
|
||||
}
|
||||
Map<String, Object> variables = collectVariables(parameters, parameterValues);
|
||||
|
||||
ec.setVariables(variables);
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
protected <T extends Parameters<T, ? extends Parameter>> Map<String, Object> collectVariables(T parameters,
|
||||
Object[] parameterValues) {
|
||||
|
||||
Map<String, Object> variables = new HashMap<String, Object>();
|
||||
|
||||
registerSpecialParameterVariablesIfPresent(parameters, parameterValues, variables);
|
||||
registerNamedMethodParameterVariables(parameters, parameterValues, variables);
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
protected <T extends Parameters<T, ? extends Parameter>> void registerNamedMethodParameterVariables(T parameters,
|
||||
Object[] parameterValues, Map<String, Object> variables) {
|
||||
|
||||
for (Parameter param : parameters) {
|
||||
if (param.isNamedParameter()) {
|
||||
variables.put(param.getName(), parameterValues[param.getIndex()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected <T extends Parameters<T, ? extends Parameter>> void registerSpecialParameterVariablesIfPresent(
|
||||
T parameters, Object[] parameterValues, Map<String, Object> variables) {
|
||||
|
||||
if (parameters.hasSpecialParameter()) {
|
||||
|
||||
for (Parameter param : parameters) {
|
||||
if (param.isSpecialParameter()) {
|
||||
|
||||
Object paramValue = parameterValues[param.getIndex()];
|
||||
if (paramValue == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
variables.put(StringUtils.uncapitalize(param.getType().getSimpleName()), paramValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link EvaluationContextExtension} to be used. Either from the current configuration or the configured
|
||||
* {@link BeanFactory}.
|
||||
@@ -251,6 +289,10 @@ public class ExtensionAwareEvaluationContextProvider implements EvaluationContex
|
||||
final Method function = targetObject instanceof Map && ((Map<?, ?>) targetObject).containsKey(name) ? (Method) ((Map<?, ?>) targetObject)
|
||||
.get(name) : (Method) functions.get(name);
|
||||
|
||||
if (function == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?>[] parameterTypes = function.getParameterTypes();
|
||||
if (parameterTypes.length != argumentTypes.size()) {
|
||||
return null;
|
||||
|
||||
@@ -37,7 +37,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter> implements Iterable<T> {
|
||||
|
||||
@SuppressWarnings("unchecked") public static final List<Class<?>> TYPES = Arrays.asList(Pageable.class, Sort.class);
|
||||
public static final List<Class<?>> TYPES = Arrays.asList(Pageable.class, Sort.class);
|
||||
|
||||
private static final String PARAM_ON_SPECIAL = format("You must not user @%s on a parameter typed %s or %s",
|
||||
Param.class.getSimpleName(), Pageable.class.getSimpleName(), Sort.class.getSimpleName());
|
||||
|
||||
@@ -27,6 +27,10 @@ import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.repository.query.spi.EvaluationContextExtension;
|
||||
import org.springframework.data.repository.query.spi.EvaluationContextExtensionSupport;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
@@ -134,6 +138,40 @@ public class ExtensibleEvaluationContextProviderUnitTests {
|
||||
assertThat(evaluateExpression("_first.DUMMY_KEY", provider), is((Object) "dummy"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-533
|
||||
*/
|
||||
@Test
|
||||
public void exposesPageableParameter() throws Exception {
|
||||
|
||||
this.parameters = new DefaultParameters(SampleRepo.class.getMethod("findByFirstname", String.class, Pageable.class));
|
||||
ExtensionAwareEvaluationContextProvider provider = new ExtensionAwareEvaluationContextProvider(
|
||||
Collections.<EvaluationContextExtension> emptyList());
|
||||
|
||||
PageRequest pageable = new PageRequest(2, 3, new Sort(Direction.DESC, "lastname"));
|
||||
|
||||
assertThat(evaluateExpression("#pageable.offset", provider, new Object[] { "test", pageable }), is((Object) 6));
|
||||
assertThat(evaluateExpression("#pageable.pageSize", provider, new Object[] { "test", pageable }), is((Object) 3));
|
||||
assertThat(evaluateExpression("#pageable.sort.toString()", provider, new Object[] { "test", pageable }),
|
||||
is((Object) "lastname: DESC"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-533
|
||||
*/
|
||||
@Test
|
||||
public void exposesSortParameter() throws Exception {
|
||||
|
||||
this.parameters = new DefaultParameters(SampleRepo.class.getMethod("findByFirstname", String.class, Sort.class));
|
||||
ExtensionAwareEvaluationContextProvider provider = new ExtensionAwareEvaluationContextProvider(
|
||||
Collections.<EvaluationContextExtension> emptyList());
|
||||
|
||||
Sort sort = new Sort(Direction.DESC, "lastname");
|
||||
|
||||
assertThat(evaluateExpression("#sort.toString()", provider, new Object[] { "test", sort }),
|
||||
is((Object) "lastname: DESC"));
|
||||
}
|
||||
|
||||
public static class DummyExtension extends EvaluationContextExtensionSupport {
|
||||
|
||||
public static String DUMMY_KEY = "dummy";
|
||||
@@ -196,13 +234,21 @@ public class ExtensibleEvaluationContextProviderUnitTests {
|
||||
}
|
||||
|
||||
private Object evaluateExpression(String expression, EvaluationContextProvider provider) {
|
||||
return evaluateExpression(expression, provider, new Object[] { "parameterValue" });
|
||||
}
|
||||
|
||||
EvaluationContext evaluationContext = provider.getEvaluationContext(parameters, new Object[] { "parameterValue" });
|
||||
private Object evaluateExpression(String expression, EvaluationContextProvider provider, Object[] args) {
|
||||
|
||||
EvaluationContext evaluationContext = provider.getEvaluationContext(parameters, args);
|
||||
return new SpelExpressionParser().parseExpression(expression).getValue(evaluationContext);
|
||||
}
|
||||
|
||||
interface SampleRepo {
|
||||
|
||||
List<Object> findByFirstname(@Param("firstname") String firstname);
|
||||
|
||||
List<Object> findByFirstname(@Param("firstname") String firstname, Pageable pageable);
|
||||
|
||||
List<Object> findByFirstname(@Param("firstname") String firstname, Sort sort);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user