|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2020 the original author or authors. |
|
|
|
* Copyright 2002-2022 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -52,22 +52,22 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
|
|
|
public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testSimpleAccess01() { |
|
|
|
void simpleAccess01() { |
|
|
|
evaluate("name", "Nikola Tesla", String.class); |
|
|
|
evaluate("name", "Nikola Tesla", String.class); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testSimpleAccess02() { |
|
|
|
void simpleAccess02() { |
|
|
|
evaluate("placeOfBirth.city", "SmilJan", String.class); |
|
|
|
evaluate("placeOfBirth.city", "SmilJan", String.class); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testSimpleAccess03() { |
|
|
|
void simpleAccess03() { |
|
|
|
evaluate("stringArrayOfThreeItems.length", "3", Integer.class); |
|
|
|
evaluate("stringArrayOfThreeItems.length", "3", Integer.class); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testNonExistentPropertiesAndMethods() { |
|
|
|
void nonExistentPropertiesAndMethods() { |
|
|
|
// madeup does not exist as a property
|
|
|
|
// madeup does not exist as a property
|
|
|
|
evaluateAndCheckError("madeup", SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, 0); |
|
|
|
evaluateAndCheckError("madeup", SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, 0); |
|
|
|
|
|
|
|
|
|
|
|
@ -80,21 +80,21 @@ public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
* supplied resolver might be able to - so null shouldn't crash the reflection resolver. |
|
|
|
* supplied resolver might be able to - so null shouldn't crash the reflection resolver. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testAccessingOnNullObject() { |
|
|
|
void accessingOnNullObject() { |
|
|
|
SpelExpression expr = (SpelExpression)parser.parseExpression("madeup"); |
|
|
|
SpelExpression expr = (SpelExpression) parser.parseExpression("madeup"); |
|
|
|
EvaluationContext context = new StandardEvaluationContext(null); |
|
|
|
EvaluationContext context = new StandardEvaluationContext(null); |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class) |
|
|
|
expr.getValue(context)) |
|
|
|
.isThrownBy(() -> expr.getValue(context)) |
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL)); |
|
|
|
.extracting(SpelEvaluationException::getMessageCode).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL); |
|
|
|
assertThat(expr.isWritable(context)).isFalse(); |
|
|
|
assertThat(expr.isWritable(context)).isFalse(); |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class) |
|
|
|
expr.setValue(context, "abc")) |
|
|
|
.isThrownBy(() -> expr.setValue(context, "abc")) |
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL)); |
|
|
|
.extracting(SpelEvaluationException::getMessageCode).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
// Adding a new property accessor just for a particular type
|
|
|
|
// Adding a new property accessor just for a particular type
|
|
|
|
public void testAddingSpecificPropertyAccessor() { |
|
|
|
void addingSpecificPropertyAccessor() { |
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
StandardEvaluationContext ctx = new StandardEvaluationContext(); |
|
|
|
StandardEvaluationContext ctx = new StandardEvaluationContext(); |
|
|
|
|
|
|
|
|
|
|
|
@ -125,35 +125,34 @@ public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testAddingRemovingAccessors() { |
|
|
|
void addingAndRemovingAccessors() { |
|
|
|
StandardEvaluationContext ctx = new StandardEvaluationContext(); |
|
|
|
StandardEvaluationContext ctx = new StandardEvaluationContext(); |
|
|
|
|
|
|
|
|
|
|
|
// reflective property accessor is the only one by default
|
|
|
|
// reflective property accessor is the only one by default
|
|
|
|
List<PropertyAccessor> propertyAccessors = ctx.getPropertyAccessors(); |
|
|
|
assertThat(ctx.getPropertyAccessors()).hasSize(1); |
|
|
|
assertThat(propertyAccessors.size()).isEqualTo(1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StringyPropertyAccessor spa = new StringyPropertyAccessor(); |
|
|
|
StringyPropertyAccessor spa = new StringyPropertyAccessor(); |
|
|
|
ctx.addPropertyAccessor(spa); |
|
|
|
ctx.addPropertyAccessor(spa); |
|
|
|
assertThat(ctx.getPropertyAccessors().size()).isEqualTo(2); |
|
|
|
assertThat(ctx.getPropertyAccessors()).hasSize(2); |
|
|
|
|
|
|
|
|
|
|
|
List<PropertyAccessor> copy = new ArrayList<>(ctx.getPropertyAccessors()); |
|
|
|
List<PropertyAccessor> copy = new ArrayList<>(ctx.getPropertyAccessors()); |
|
|
|
assertThat(ctx.removePropertyAccessor(spa)).isTrue(); |
|
|
|
assertThat(ctx.removePropertyAccessor(spa)).isTrue(); |
|
|
|
assertThat(ctx.removePropertyAccessor(spa)).isFalse(); |
|
|
|
assertThat(ctx.removePropertyAccessor(spa)).isFalse(); |
|
|
|
assertThat(ctx.getPropertyAccessors().size()).isEqualTo(1); |
|
|
|
assertThat(ctx.getPropertyAccessors()).hasSize(1); |
|
|
|
|
|
|
|
|
|
|
|
ctx.setPropertyAccessors(copy); |
|
|
|
ctx.setPropertyAccessors(copy); |
|
|
|
assertThat(ctx.getPropertyAccessors().size()).isEqualTo(2); |
|
|
|
assertThat(ctx.getPropertyAccessors()).hasSize(2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void testAccessingPropertyOfClass() { |
|
|
|
void accessingPropertyOfClass() { |
|
|
|
Expression expression = parser.parseExpression("name"); |
|
|
|
Expression expression = parser.parseExpression("name"); |
|
|
|
Object value = expression.getValue(new StandardEvaluationContext(String.class)); |
|
|
|
Object value = expression.getValue(new StandardEvaluationContext(String.class)); |
|
|
|
assertThat(value).isEqualTo("java.lang.String"); |
|
|
|
assertThat(value).isEqualTo("java.lang.String"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void shouldAlwaysUsePropertyAccessorFromEvaluationContext() { |
|
|
|
void shouldAlwaysUsePropertyAccessorFromEvaluationContext() { |
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
SpelExpressionParser parser = new SpelExpressionParser(); |
|
|
|
Expression expression = parser.parseExpression("name"); |
|
|
|
Expression expression = parser.parseExpression("name"); |
|
|
|
|
|
|
|
|
|
|
|
@ -167,19 +166,19 @@ public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void standardGetClassAccess() { |
|
|
|
void standardGetClassAccess() { |
|
|
|
assertThat(parser.parseExpression("'a'.class.name").getValue()).isEqualTo(String.class.getName()); |
|
|
|
assertThat(parser.parseExpression("'a'.class.name").getValue()).isEqualTo(String.class.getName()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void noGetClassAccess() { |
|
|
|
void noGetClassAccess() { |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
parser.parseExpression("'a'.class.name").getValue(context)); |
|
|
|
parser.parseExpression("'a'.class.name").getValue(context)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void propertyReadOnly() { |
|
|
|
void propertyReadOnly() { |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
|
|
|
|
|
|
|
|
Expression expr = parser.parseExpression("name"); |
|
|
|
Expression expr = parser.parseExpression("name"); |
|
|
|
@ -193,7 +192,7 @@ public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void propertyReadOnlyWithRecordStyle() { |
|
|
|
void propertyReadOnlyWithRecordStyle() { |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
|
|
|
|
|
|
|
|
Expression expr = parser.parseExpression("name"); |
|
|
|
Expression expr = parser.parseExpression("name"); |
|
|
|
@ -207,7 +206,7 @@ public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void propertyReadWrite() { |
|
|
|
void propertyReadWrite() { |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); |
|
|
|
|
|
|
|
|
|
|
|
Expression expr = parser.parseExpression("name"); |
|
|
|
Expression expr = parser.parseExpression("name"); |
|
|
|
@ -226,27 +225,27 @@ public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void propertyReadWriteWithRootObject() { |
|
|
|
void propertyReadWriteWithRootObject() { |
|
|
|
Person target = new Person("p1"); |
|
|
|
Person rootObject = new Person("p1"); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().withRootObject(target).build(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().withRootObject(rootObject).build(); |
|
|
|
assertThat(context.getRootObject().getValue()).isSameAs(target); |
|
|
|
assertThat(context.getRootObject().getValue()).isSameAs(rootObject); |
|
|
|
|
|
|
|
|
|
|
|
Expression expr = parser.parseExpression("name"); |
|
|
|
Expression expr = parser.parseExpression("name"); |
|
|
|
assertThat(expr.getValue(context, target)).isEqualTo("p1"); |
|
|
|
assertThat(expr.getValue(context)).isEqualTo("p1"); |
|
|
|
target.setName("p2"); |
|
|
|
rootObject.setName("p2"); |
|
|
|
assertThat(expr.getValue(context, target)).isEqualTo("p2"); |
|
|
|
assertThat(expr.getValue(context)).isEqualTo("p2"); |
|
|
|
|
|
|
|
|
|
|
|
parser.parseExpression("name='p3'").getValue(context, target); |
|
|
|
parser.parseExpression("name='p3'").getValue(context, rootObject); |
|
|
|
assertThat(target.getName()).isEqualTo("p3"); |
|
|
|
assertThat(rootObject.getName()).isEqualTo("p3"); |
|
|
|
assertThat(expr.getValue(context, target)).isEqualTo("p3"); |
|
|
|
assertThat(expr.getValue(context)).isEqualTo("p3"); |
|
|
|
|
|
|
|
|
|
|
|
expr.setValue(context, target, "p4"); |
|
|
|
expr.setValue(context, "p4"); |
|
|
|
assertThat(target.getName()).isEqualTo("p4"); |
|
|
|
assertThat(rootObject.getName()).isEqualTo("p4"); |
|
|
|
assertThat(expr.getValue(context, target)).isEqualTo("p4"); |
|
|
|
assertThat(expr.getValue(context)).isEqualTo("p4"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void propertyAccessWithoutMethodResolver() { |
|
|
|
void propertyAccessWithoutMethodResolver() { |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
Person target = new Person("p1"); |
|
|
|
Person target = new Person("p1"); |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> |
|
|
|
@ -254,20 +253,20 @@ public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void propertyAccessWithInstanceMethodResolver() { |
|
|
|
void propertyAccessWithInstanceMethodResolver() { |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build(); |
|
|
|
Person target = new Person("p1"); |
|
|
|
Person target = new Person("p1"); |
|
|
|
assertThat(parser.parseExpression("name.substring(1)").getValue(context, target)).isEqualTo("1"); |
|
|
|
assertThat(parser.parseExpression("name.substring(1)").getValue(context, target)).isEqualTo("1"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void propertyAccessWithInstanceMethodResolverAndTypedRootObject() { |
|
|
|
void propertyAccessWithInstanceMethodResolverAndTypedRootObject() { |
|
|
|
Person target = new Person("p1"); |
|
|
|
Person rootObject = new Person("p1"); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding(). |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding(). |
|
|
|
withInstanceMethods().withTypedRootObject(target, TypeDescriptor.valueOf(Object.class)).build(); |
|
|
|
withInstanceMethods().withTypedRootObject(rootObject, TypeDescriptor.valueOf(Object.class)).build(); |
|
|
|
|
|
|
|
|
|
|
|
assertThat(parser.parseExpression("name.substring(1)").getValue(context, target)).isEqualTo("1"); |
|
|
|
assertThat(parser.parseExpression("name.substring(1)").getValue(context)).isEqualTo("1"); |
|
|
|
assertThat(context.getRootObject().getValue()).isSameAs(target); |
|
|
|
assertThat(context.getRootObject().getValue()).isSameAs(rootObject); |
|
|
|
assertThat(context.getRootObject().getTypeDescriptor().getType()).isSameAs(Object.class); |
|
|
|
assertThat(context.getRootObject().getTypeDescriptor().getType()).isSameAs(Object.class); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -277,7 +276,7 @@ public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
Expression expression = parser.parseExpression("stringArrayOfThreeItems[3]"); |
|
|
|
Expression expression = parser.parseExpression("stringArrayOfThreeItems[3]"); |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class) |
|
|
|
assertThatExceptionOfType(SpelEvaluationException.class) |
|
|
|
.isThrownBy(() -> expression.getValue(context, new Inventor())) |
|
|
|
.isThrownBy(() -> expression.getValue(context, new Inventor())) |
|
|
|
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.ARRAY_INDEX_OUT_OF_BOUNDS)); |
|
|
|
.extracting(SpelEvaluationException::getMessageCode).isEqualTo(SpelMessage.ARRAY_INDEX_OUT_OF_BOUNDS); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -335,7 +334,7 @@ public class PropertyAccessTests extends AbstractExpressionTests { |
|
|
|
|
|
|
|
|
|
|
|
private final Map<String, Object> values; |
|
|
|
private final Map<String, Object> values; |
|
|
|
|
|
|
|
|
|
|
|
public ConfigurablePropertyAccessor(Map<String, Object> values) { |
|
|
|
ConfigurablePropertyAccessor(Map<String, Object> values) { |
|
|
|
this.values = values; |
|
|
|
this.values = values; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|