diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java index df7daa65b..7daeb33bf 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProvider.java @@ -36,8 +36,6 @@ public class PersistentEntityParameterValueProvider

provider; private final Object parent; - private SpELExpressionEvaluator spELEvaluator; - /** * Creates a new {@link PersistentEntityParameterValueProvider} for the given {@link PersistentEntity} and * {@link PropertyValueProvider}. @@ -57,16 +55,6 @@ public class PersistentEntityParameterValueProvider

T getParameterValue(Parameter parameter) { - if (spELEvaluator != null && parameter.hasSpelExpression()) { - return spELEvaluator.evaluate(parameter.getSpelExpression()); - } - PreferredConstructor constructor = entity.getPersistenceConstructor(); if (constructor.isEnclosingClassParameter(parameter)) { diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELExpressionParameterValueProvider.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELExpressionParameterValueProvider.java new file mode 100644 index 000000000..a20a11366 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/SpELExpressionParameterValueProvider.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed 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.springframework.data.mapping.model; + +import org.springframework.core.convert.ConversionService; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PreferredConstructor.Parameter; +import org.springframework.util.Assert; + +/** + * {@link ParameterValueProvider} that can be used to front a {@link ParameterValueProvider} delegate to prefer a Spel + * expression evaluation over directly resolving the parameter value with the delegate. + * + * @author Oliver Gierke + */ +public class SpELExpressionParameterValueProvider

> implements ParameterValueProvider

{ + + private final SpELExpressionEvaluator evaluator; + private final ParameterValueProvider

delegate; + private final ConversionService conversionService; + + /** + * Creates a new {@link SpELExpressionParameterValueProvider} using the given {@link SpELExpressionEvaluator}, + * {@link ConversionService} and {@link ParameterValueProvider} delegate to forward calls to, that resolve parameters + * that do not have a Spel expression configured with them. + * + * @param evaluator must not be {@literal null}. + * @param conversionService must not be {@literal null}. + * @param delegate must not be {@literal null}. + */ + public SpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator, ConversionService conversionService, + ParameterValueProvider

delegate) { + + Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null!"); + Assert.notNull(conversionService, "ConversionService must not be null!"); + Assert.notNull(delegate, "ParameterValueProvider delegate must not be null!"); + + this.evaluator = evaluator; + this.conversionService = conversionService; + this.delegate = delegate; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter) + */ + public T getParameterValue(Parameter parameter) { + + if (!parameter.hasSpelExpression()) { + return delegate == null ? null : delegate.getParameterValue(parameter); + } + + Object object = evaluator.evaluate(parameter.getSpelExpression()); + return object == null ? null : potentiallyConvertSpelValue(object, parameter); + } + + /** + * Hook to allow to massage the value resulting from the Spel expression evaluation. Default implementation will + * leverage the configured {@link ConversionService} to massage the value into the parameter type. + * + * @param object the value to massage, will never be {@literal null}. + * @param parameter the {@link Parameter} we create the value for + * @return + */ + protected T potentiallyConvertSpelValue(Object object, Parameter parameter) { + return conversionService.convert(object, parameter.getRawType()); + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProviderUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProviderUnitTests.java index ee238f327..c6a720950 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProviderUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/PersistentEntityParameterValueProviderUnitTests.java @@ -20,7 +20,9 @@ import static org.junit.Assert.*; import java.util.Iterator; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -39,6 +41,9 @@ import org.springframework.data.util.ClassTypeInformation; @RunWith(MockitoJUnitRunner.class) public class PersistentEntityParameterValueProviderUnitTests

> { + @Rule + public ExpectedException exception = ExpectedException.none(); + @Mock PropertyValueProvider

propertyValueProvider; @Mock @@ -53,6 +58,7 @@ public class PersistentEntityParameterValueProviderUnitTests

entity = new BasicPersistentEntity(ClassTypeInformation.from(Inner.class)) { + @Override public P getPersistentProperty(String name) { return property; } @@ -67,6 +73,22 @@ public class PersistentEntityParameterValueProviderUnitTests

entity = new BasicPersistentEntity(ClassTypeInformation.from(Entity.class)); + ParameterValueProvider

provider = new PersistentEntityParameterValueProvider

(entity, propertyValueProvider, + property); + + PreferredConstructor constructor = entity.getPersistenceConstructor(); + + exception.expect(MappingException.class); + exception.expectMessage("bar"); + exception.expectMessage(Entity.class.getName()); + + provider.getParameterValue(constructor.getParameters().iterator().next()); + } + static class Outer { class Inner { @@ -78,4 +100,13 @@ public class PersistentEntityParameterValueProviderUnitTests

delegate; + @Mock + ConversionService conversionService; + + SpELExpressionParameterValueProvider provider; + + Parameter parameter; + + @Before + @SuppressWarnings("unchecked") + public void setUp() { + provider = new SpELExpressionParameterValueProvider(evaluator, conversionService, + delegate); + + parameter = mock(Parameter.class); + when(parameter.hasSpelExpression()).thenReturn(true); + when(parameter.getRawType()).thenReturn(Object.class); + } + + @Test + @SuppressWarnings("unchecked") + public void delegatesIfParameterDoesNotHaveASpELExpression() { + + Parameter parameter = mock(Parameter.class); + when(parameter.hasSpelExpression()).thenReturn(false); + + provider.getParameterValue(parameter); + verify(delegate, times(1)).getParameterValue(parameter); + verify(evaluator, times(0)).evaluate("expression"); + } + + @Test + public void evaluatesSpELExpression() { + + when(parameter.getSpelExpression()).thenReturn("expression"); + + provider.getParameterValue(parameter); + verify(delegate, times(0)).getParameterValue(parameter); + verify(evaluator, times(1)).evaluate("expression"); + } + + @Test + public void handsSpELValueToConversionService() { + + when(evaluator.evaluate(Mockito.any(String.class))).thenReturn("value"); + + provider.getParameterValue(parameter); + verify(delegate, times(0)).getParameterValue(parameter); + verify(conversionService, times(1)).convert("value", Object.class); + } + + @Test + public void doesNotConvertNullValue() { + + when(evaluator.evaluate(Mockito.any(String.class))).thenReturn(null); + + provider.getParameterValue(parameter); + verify(delegate, times(0)).getParameterValue(parameter); + verify(conversionService, times(0)).convert("value", Object.class); + } + + @Test + public void returnsMassagedObjectOnOverride() { + + provider = new SpELExpressionParameterValueProvider(evaluator, conversionService, + delegate) { + @Override + @SuppressWarnings("unchecked") + protected T potentiallyConvertSpelValue(Object object, Parameter parameter) { + return (T) "FOO"; + } + }; + + when(evaluator.evaluate(Mockito.anyString())).thenReturn("value"); + + Object result = provider.getParameterValue(parameter); + assertThat(result, is((Object) "FOO")); + verify(delegate, times(0)).getParameterValue(parameter); + } +}