From 9a38355896fa062f67ebca4cad00ad09479277b2 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Wed, 7 Feb 2024 11:46:13 +0100 Subject: [PATCH] Improve tests for indexing and collection selection/projection in SpEL --- .../expression/spel/IndexingTests.java | 621 +++++++++--------- .../spel/SelectionAndProjectionTests.java | 507 +++++++------- 2 files changed, 563 insertions(+), 565 deletions(-) diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/IndexingTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/IndexingTests.java index 800d769bbd..03448e5dda 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/IndexingTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/IndexingTests.java @@ -30,7 +30,6 @@ import java.util.Map; import org.junit.jupiter.api.Test; import org.springframework.expression.EvaluationContext; -import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; @@ -38,6 +37,9 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.springframework.expression.spel.SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE; +import static org.springframework.expression.spel.SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE; @SuppressWarnings("rawtypes") class IndexingTests { @@ -50,16 +52,14 @@ class IndexingTests { this.property = property; SpelExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("property"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.HashMap"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.util.HashMap", FieldAnnotation.class.getName()); assertThat(expression.getValue(this)).isEqualTo(property); assertThat(expression.getValue(this, Map.class)).isEqualTo(property); expression = parser.parseExpression("property['foo']"); assertThat(expression.getValue(this)).isEqualTo("bar"); } - @FieldAnnotation - public Object property; - @Test @SuppressWarnings("unchecked") void indexIntoGenericPropertyContainingMapObject() { @@ -72,14 +72,323 @@ class IndexingTests { context.addPropertyAccessor(new MapAccessor()); context.setRootObject(property); Expression expression = parser.parseExpression("property"); - assertThat(expression.getValueTypeDescriptor(context).toString()).isEqualTo("java.util.HashMap"); + assertThat(expression.getValueTypeDescriptor(context)).asString() + .isEqualTo("java.util.HashMap"); assertThat(expression.getValue(context)).isEqualTo(map); assertThat(expression.getValue(context, Map.class)).isEqualTo(map); expression = parser.parseExpression("property['foo']"); assertThat(expression.getValue(context)).isEqualTo("bar"); } - public static class MapAccessor implements PropertyAccessor { + @Test + void setGenericPropertyContainingMap() { + Map property = new HashMap<>(); + property.put("foo", "bar"); + this.property = property; + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("property"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.util.HashMap", FieldAnnotation.class.getName()); + assertThat(expression.getValue(this)).isEqualTo(property); + expression = parser.parseExpression("property['foo']"); + assertThat(expression.getValue(this)).isEqualTo("bar"); + expression.setValue(this, "baz"); + assertThat(expression.getValue(this)).isEqualTo("baz"); + } + + @Test + void setPropertyContainingMap() { + Map property = new HashMap<>(); + property.put(9, 3); + this.parameterizedMap = property; + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("parameterizedMap"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("java.util.HashMap"); + assertThat(expression.getValue(this)).isEqualTo(property); + expression = parser.parseExpression("parameterizedMap['9']"); + assertThat(expression.getValue(this)).isEqualTo(3); + expression.setValue(this, "37"); + assertThat(expression.getValue(this)).isEqualTo(37); + } + + @Test + void setPropertyContainingMapAutoGrow() { + SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, false)); + Expression expression = parser.parseExpression("parameterizedMap"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("java.util.Map"); + assertThat(expression.getValue(this)).isNull(); + expression = parser.parseExpression("parameterizedMap['9']"); + assertThat(expression.getValue(this)).isNull(); + expression.setValue(this, "37"); + assertThat(expression.getValue(this)).isEqualTo(37); + } + + @Test + void indexIntoGenericPropertyContainingList() { + List property = new ArrayList<>(); + property.add("bar"); + this.property = property; + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("property"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.util.ArrayList", FieldAnnotation.class.getName()); + assertThat(expression.getValue(this)).isEqualTo(property); + expression = parser.parseExpression("property[0]"); + assertThat(expression.getValue(this)).isEqualTo("bar"); + } + + @Test + void setGenericPropertyContainingList() { + List property = new ArrayList<>(); + property.add(3); + this.property = property; + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("property"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.util.ArrayList", FieldAnnotation.class.getName()); + assertThat(expression.getValue(this)).isEqualTo(property); + expression = parser.parseExpression("property[0]"); + assertThat(expression.getValue(this)).isEqualTo(3); + expression.setValue(this, "4"); + assertThat(expression.getValue(this)).isEqualTo("4"); + } + + @Test + void setGenericPropertyContainingListAutogrow() { + List property = new ArrayList<>(); + this.property = property; + SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + Expression expression = parser.parseExpression("property"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.util.ArrayList", FieldAnnotation.class.getName()); + assertThat(expression.getValue(this)).isEqualTo(property); + + Expression indexExpression = parser.parseExpression("property[0]"); + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(() -> indexExpression.getValue(this)) + .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE)); + } + + @Test + void autoGrowListOfElementsWithoutDefaultConstructor() { + this.decimals = new ArrayList<>(); + SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + parser.parseExpression("decimals[0]").setValue(this, "123.4"); + assertThat(decimals).containsExactly(BigDecimal.valueOf(123.4)); + } + + @Test + void indexIntoPropertyContainingListContainingNullElement() { + this.decimals = new ArrayList<>(); + this.decimals.add(null); + this.decimals.add(BigDecimal.ONE); + SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + parser.parseExpression("decimals[0]").setValue(this, "9876.5"); + assertThat(decimals).containsExactly(BigDecimal.valueOf(9876.5), BigDecimal.ONE); + } + + @Test + void indexIntoPropertyContainingList() { + List property = new ArrayList<>(); + property.add(3); + this.parameterizedList = property; + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("parameterizedList"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("java.util.ArrayList"); + assertThat(expression.getValue(this)).isEqualTo(property); + expression = parser.parseExpression("parameterizedList[0]"); + assertThat(expression.getValue(this)).isEqualTo(3); + } + + @Test + void indexIntoPropertyContainingListOfList() { + List> property = new ArrayList<>(); + property.add(Arrays.asList(3)); + this.parameterizedListOfList = property; + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("parameterizedListOfList[0]"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("java.util.Arrays$ArrayList"); + assertThat(expression.getValue(this)).isEqualTo(property.get(0)); + expression = parser.parseExpression("parameterizedListOfList[0][0]"); + assertThat(expression.getValue(this)).isEqualTo(3); + } + + @Test + void setPropertyContainingList() { + List property = new ArrayList<>(); + property.add(3); + this.parameterizedList = property; + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("parameterizedList"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("java.util.ArrayList"); + assertThat(expression.getValue(this)).isEqualTo(property); + expression = parser.parseExpression("parameterizedList[0]"); + assertThat(expression.getValue(this)).isEqualTo(3); + expression.setValue(this, "4"); + assertThat(expression.getValue(this)).isEqualTo(4); + } + + @Test + void indexIntoGenericPropertyContainingNullList() { + SpelParserConfiguration configuration = new SpelParserConfiguration(true, true); + SpelExpressionParser parser = new SpelExpressionParser(configuration); + Expression expression = parser.parseExpression("property"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.lang.Object", FieldAnnotation.class.getName()); + assertThat(expression.getValue(this)).isNull(); + + Expression indexExpression = parser.parseExpression("property[0]"); + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(() -> indexExpression.getValue(this)) + .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(INDEXING_NOT_SUPPORTED_FOR_TYPE)); + } + + @Test + void indexIntoGenericPropertyContainingGrowingList() { + List property = new ArrayList<>(); + this.property = property; + SpelParserConfiguration configuration = new SpelParserConfiguration(true, true); + SpelExpressionParser parser = new SpelExpressionParser(configuration); + Expression expression = parser.parseExpression("property"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.util.ArrayList", FieldAnnotation.class.getName()); + assertThat(expression.getValue(this)).isEqualTo(property); + + Expression indexExpression = parser.parseExpression("property[0]"); + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(() -> indexExpression.getValue(this)) + .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE)); + } + + @Test + void indexIntoGenericPropertyContainingGrowingList2() { + List property2 = new ArrayList<>(); + this.property2 = property2; + SpelParserConfiguration configuration = new SpelParserConfiguration(true, true); + SpelExpressionParser parser = new SpelExpressionParser(configuration); + Expression expression = parser.parseExpression("property2"); + assertThat(expression.getValueTypeDescriptor(this)).asString().isEqualTo("java.util.ArrayList"); + assertThat(expression.getValue(this)).isEqualTo(property2); + + Expression indexExpression = parser.parseExpression("property2[0]"); + assertThatExceptionOfType(SpelEvaluationException.class) + .isThrownBy(() -> indexExpression.getValue(this)) + .satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE)); + } + + @Test + void indexIntoGenericPropertyContainingArray() { + String[] property = new String[] { "bar" }; + this.property = property; + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("property"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.lang.String[]", FieldAnnotation.class.getName()); + assertThat(expression.getValue(this)).isEqualTo(property); + expression = parser.parseExpression("property[0]"); + assertThat(expression.getValue(this)).isEqualTo("bar"); + } + + @Test + void emptyList() { + listOfScalarNotGeneric = new ArrayList(); + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("listOfScalarNotGeneric"); + assertThat(expression.getValueTypeDescriptor(this)).asString().isEqualTo("java.util.ArrayList"); + assertThat(expression.getValue(this, String.class)).isEmpty(); + } + + @Test + @SuppressWarnings("unchecked") + void resolveCollectionElementType() { + listNotGeneric = new ArrayList(2); + listNotGeneric.add(5); + listNotGeneric.add(6); + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("listNotGeneric"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.util.ArrayList", FieldAnnotation.class.getName()); + assertThat(expression.getValue(this, String.class)).isEqualTo("5,6"); + } + + @Test + void resolveCollectionElementTypeNull() { + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("listNotGeneric"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.util.List", FieldAnnotation.class.getName()); + } + + @SuppressWarnings("unchecked") + @Test + void resolveMapKeyValueTypes() { + mapNotGeneric = new HashMap(); + mapNotGeneric.put("baseAmount", 3.11); + mapNotGeneric.put("bonusAmount", 7.17); + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("mapNotGeneric"); + assertThat(expression.getValueTypeDescriptor(this)).asString() + .isEqualTo("@%s java.util.HashMap", FieldAnnotation.class.getName()); + } + + @Test + @SuppressWarnings("unchecked") + void testListOfScalar() { + listOfScalarNotGeneric = new ArrayList(1); + listOfScalarNotGeneric.add("5"); + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("listOfScalarNotGeneric[0]"); + assertThat(expression.getValue(this, Integer.class)).isEqualTo(Integer.valueOf(5)); + } + + @Test + @SuppressWarnings("unchecked") + void testListsOfMap() { + listOfMapsNotGeneric = new ArrayList(); + Map map = new HashMap(); + map.put("fruit", "apple"); + listOfMapsNotGeneric.add(map); + SpelExpressionParser parser = new SpelExpressionParser(); + Expression expression = parser.parseExpression("listOfMapsNotGeneric[0]['fruit']"); + assertThat(expression.getValue(this, String.class)).isEqualTo("apple"); + } + + + @Target({ElementType.FIELD}) + @Retention(RetentionPolicy.RUNTIME) + @interface FieldAnnotation { + } + + @FieldAnnotation + public Object property; + + public List property2; + + public Map parameterizedMap; + + public List decimals; + + public List parameterizedList; + + public List> parameterizedListOfList; + + @FieldAnnotation + public List listNotGeneric; + + @FieldAnnotation + public Map mapNotGeneric; + + public List listOfScalarNotGeneric; + + public List listOfMapsNotGeneric; + + + private static class MapAccessor implements PropertyAccessor { @Override public boolean canRead(EvaluationContext context, Object target, String name) { @@ -109,302 +418,4 @@ class IndexingTests { } - @Test - void setGenericPropertyContainingMap() { - Map property = new HashMap<>(); - property.put("foo", "bar"); - this.property = property; - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("property"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.HashMap"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("property['foo']"); - assertThat(expression.getValue(this)).isEqualTo("bar"); - expression.setValue(this, "baz"); - assertThat(expression.getValue(this)).isEqualTo("baz"); - } - - @Test - void setPropertyContainingMap() { - Map property = new HashMap<>(); - property.put(9, 3); - this.parameterizedMap = property; - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("parameterizedMap"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("java.util.HashMap"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("parameterizedMap['9']"); - assertThat(expression.getValue(this)).isEqualTo(3); - expression.setValue(this, "37"); - assertThat(expression.getValue(this)).isEqualTo(37); - } - - public Map parameterizedMap; - - @Test - void setPropertyContainingMapAutoGrow() { - SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, false)); - Expression expression = parser.parseExpression("parameterizedMap"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("java.util.Map"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("parameterizedMap['9']"); - assertThat(expression.getValue(this)).isNull(); - expression.setValue(this, "37"); - assertThat(expression.getValue(this)).isEqualTo(37); - } - - @Test - void indexIntoGenericPropertyContainingList() { - List property = new ArrayList<>(); - property.add("bar"); - this.property = property; - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("property"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("property[0]"); - assertThat(expression.getValue(this)).isEqualTo("bar"); - } - - @Test - void setGenericPropertyContainingList() { - List property = new ArrayList<>(); - property.add(3); - this.property = property; - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("property"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("property[0]"); - assertThat(expression.getValue(this)).isEqualTo(3); - expression.setValue(this, "4"); - assertThat(expression.getValue(this)).isEqualTo("4"); - } - - @Test - void setGenericPropertyContainingListAutogrow() { - List property = new ArrayList<>(); - this.property = property; - SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - Expression expression = parser.parseExpression("property"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("property[0]"); - try { - expression.setValue(this, "4"); - } - catch (EvaluationException ex) { - assertThat(ex.getMessage()).startsWith("EL1053E"); - } - } - - public List decimals; - - @Test - void autoGrowListOfElementsWithoutDefaultConstructor() { - this.decimals = new ArrayList<>(); - SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - parser.parseExpression("decimals[0]").setValue(this, "123.4"); - assertThat(decimals).containsExactly(BigDecimal.valueOf(123.4)); - } - - @Test - void indexIntoPropertyContainingListContainingNullElement() { - this.decimals = new ArrayList<>(); - this.decimals.add(null); - this.decimals.add(BigDecimal.ONE); - SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - parser.parseExpression("decimals[0]").setValue(this, "9876.5"); - assertThat(decimals).containsExactly(BigDecimal.valueOf(9876.5), BigDecimal.ONE); - } - - @Test - void indexIntoPropertyContainingList() { - List property = new ArrayList<>(); - property.add(3); - this.parameterizedList = property; - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("parameterizedList"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("java.util.ArrayList"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("parameterizedList[0]"); - assertThat(expression.getValue(this)).isEqualTo(3); - } - - public List parameterizedList; - - @Test - void indexIntoPropertyContainingListOfList() { - List> property = new ArrayList<>(); - property.add(Arrays.asList(3)); - this.parameterizedListOfList = property; - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("parameterizedListOfList[0]"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("java.util.Arrays$ArrayList"); - assertThat(expression.getValue(this)).isEqualTo(property.get(0)); - expression = parser.parseExpression("parameterizedListOfList[0][0]"); - assertThat(expression.getValue(this)).isEqualTo(3); - } - - public List> parameterizedListOfList; - - @Test - void setPropertyContainingList() { - List property = new ArrayList<>(); - property.add(3); - this.parameterizedList = property; - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("parameterizedList"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("java.util.ArrayList"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("parameterizedList[0]"); - assertThat(expression.getValue(this)).isEqualTo(3); - expression.setValue(this, "4"); - assertThat(expression.getValue(this)).isEqualTo(4); - } - - @Test - void indexIntoGenericPropertyContainingNullList() { - SpelParserConfiguration configuration = new SpelParserConfiguration(true, true); - SpelExpressionParser parser = new SpelExpressionParser(configuration); - Expression expression = parser.parseExpression("property"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.lang.Object"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("property[0]"); - try { - assertThat(expression.getValue(this)).isEqualTo("bar"); - } - catch (EvaluationException ex) { - assertThat(ex.getMessage()).startsWith("EL1027E"); - } - } - - @Test - void indexIntoGenericPropertyContainingGrowingList() { - List property = new ArrayList<>(); - this.property = property; - SpelParserConfiguration configuration = new SpelParserConfiguration(true, true); - SpelExpressionParser parser = new SpelExpressionParser(configuration); - Expression expression = parser.parseExpression("property"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("property[0]"); - try { - assertThat(expression.getValue(this)).isEqualTo("bar"); - } - catch (EvaluationException ex) { - assertThat(ex.getMessage()).startsWith("EL1053E"); - } - } - - @Test - void indexIntoGenericPropertyContainingGrowingList2() { - List property2 = new ArrayList<>(); - this.property2 = property2; - SpelParserConfiguration configuration = new SpelParserConfiguration(true, true); - SpelExpressionParser parser = new SpelExpressionParser(configuration); - Expression expression = parser.parseExpression("property2"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("java.util.ArrayList"); - assertThat(expression.getValue(this)).isEqualTo(property2); - expression = parser.parseExpression("property2[0]"); - try { - assertThat(expression.getValue(this)).isEqualTo("bar"); - } - catch (EvaluationException ex) { - assertThat(ex.getMessage()).startsWith("EL1053E"); - } - } - - public List property2; - - @Test - void indexIntoGenericPropertyContainingArray() { - String[] property = new String[] { "bar" }; - this.property = property; - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("property"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.lang.String[]"); - assertThat(expression.getValue(this)).isEqualTo(property); - expression = parser.parseExpression("property[0]"); - assertThat(expression.getValue(this)).isEqualTo("bar"); - } - - @Test - void emptyList() { - listOfScalarNotGeneric = new ArrayList(); - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("listOfScalarNotGeneric"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("java.util.ArrayList"); - assertThat(expression.getValue(this, String.class)).isEmpty(); - } - - @SuppressWarnings("unchecked") - @Test - void resolveCollectionElementType() { - listNotGeneric = new ArrayList(2); - listNotGeneric.add(5); - listNotGeneric.add(6); - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("listNotGeneric"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.ArrayList"); - assertThat(expression.getValue(this, String.class)).isEqualTo("5,6"); - } - - @Test - void resolveCollectionElementTypeNull() { - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("listNotGeneric"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.List"); - } - - @FieldAnnotation - public List listNotGeneric; - - @Target({ElementType.FIELD}) - @Retention(RetentionPolicy.RUNTIME) - public @interface FieldAnnotation { - - } - - @SuppressWarnings("unchecked") - @Test - void resolveMapKeyValueTypes() { - mapNotGeneric = new HashMap(); - mapNotGeneric.put("baseAmount", 3.11); - mapNotGeneric.put("bonusAmount", 7.17); - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("mapNotGeneric"); - assertThat(expression.getValueTypeDescriptor(this).toString()).isEqualTo("@org.springframework.expression.spel.IndexingTests$FieldAnnotation java.util.HashMap"); - } - - @FieldAnnotation - public Map mapNotGeneric; - - @SuppressWarnings("unchecked") - @Test - void testListOfScalar() { - listOfScalarNotGeneric = new ArrayList(1); - listOfScalarNotGeneric.add("5"); - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("listOfScalarNotGeneric[0]"); - assertThat(expression.getValue(this, Integer.class)).isEqualTo(Integer.valueOf(5)); - } - - public List listOfScalarNotGeneric; - - - @SuppressWarnings("unchecked") - @Test - void testListsOfMap() { - listOfMapsNotGeneric = new ArrayList(); - Map map = new HashMap(); - map.put("fruit", "apple"); - listOfMapsNotGeneric.add(map); - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expression = parser.parseExpression("listOfMapsNotGeneric[0]['fruit']"); - assertThat(expression.getValue(this, String.class)).isEqualTo("apple"); - } - - public List listOfMapsNotGeneric; - } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java index bd9e9f9ecf..80f67b6834 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java @@ -17,18 +17,22 @@ package org.springframework.expression.spel; import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; -import org.springframework.expression.TypedValue; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -36,6 +40,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.InstanceOfAssertFactories.INTEGER; import static org.assertj.core.api.InstanceOfAssertFactories.LIST; +import static org.assertj.core.api.InstanceOfAssertFactories.array; import static org.springframework.expression.spel.SpelMessage.INVALID_TYPE_FOR_SELECTION; import static org.springframework.expression.spel.SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE; import static org.springframework.expression.spel.SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN; @@ -46,241 +51,244 @@ import static org.springframework.expression.spel.SpelMessage.RESULT_OF_SELECTIO * @author Juergen Hoeller * @author Andy Clement */ -class SelectionAndProjectionTests extends AbstractExpressionTests { +class SelectionAndProjectionTests { + + @Nested + class SelectionTests extends AbstractExpressionTests { + + @Test + void selectionOnUnsupportedType() { + evaluateAndCheckError("'abc'.?[#this < 5]", INVALID_TYPE_FOR_SELECTION); + evaluateAndCheckError("null.?[#this < 5]", INVALID_TYPE_FOR_SELECTION); + } + + @Test + void selectionWithNonBooleanSelectionCriteria() { + evaluateAndCheckError("mapOfNumbersUpToTen.?['hello']", RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); + evaluateAndCheckError("mapOfNumbersUpToTen.keySet().?['hello']", RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); + } + + @Test + void selectionWithSafeNavigation() { + evaluate("null?.?[true]", null, null); + evaluate("{1,2,3}?.?[true]", List.of(1, 2, 3), ArrayList.class); + evaluate("{1,2,3,4,5,6}?.?[#this between {2, 4}]", List.of(2, 3, 4), ArrayList.class); + } + + @Test + void selectionAST() { + // select first + SpelExpression expr = (SpelExpression) parser.parseExpression("'abc'.^[true]"); + assertThat(expr.toStringAST()).isEqualTo("'abc'.^[true]"); + + // select all + expr = (SpelExpression) parser.parseExpression("'abc'.?[true]"); + assertThat(expr.toStringAST()).isEqualTo("'abc'.?[true]"); + + // select last + expr = (SpelExpression) parser.parseExpression("'abc'.$[true]"); + assertThat(expr.toStringAST()).isEqualTo("'abc'.$[true]"); + } + + @Test + @SuppressWarnings("unchecked") + void selectionWithList() { + Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new ListTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(0, 1, 2, 3, 4); + } + + @Test + void selectFirstItemInList() { + Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new ListTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); + } + + @Test + void selectLastItemInList() { + Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new ListTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); + } + + @Test + void selectionWithSetAndRegex() { + evaluate("testMap.keySet().?[#this matches '.*o.*']", "[monday]", ArrayList.class); + evaluate("testMap.keySet().?[#this matches '.*r.*'].contains('saturday')", "true", Boolean.class); + evaluate("testMap.keySet().?[#this matches '.*r.*'].size()", "3", Integer.class); + } + + @Test + @SuppressWarnings("unchecked") + void selectionWithSet() { + Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(0, 1, 2, 3, 4); + } + + @Test + void selectFirstItemInSet() { + Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); + } + + @Test + void selectLastItemInSet() { + Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); + } + + @ParameterizedTest + @ValueSource(strings = {"#root.?[#this < 3]", "?[#this < 3]"}) + void selectionWithIterable(String expressionString) { + Expression expression = new SpelExpressionParser().parseRaw(expressionString); + EvaluationContext context = new StandardEvaluationContext(new IterableTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(1, 2); + } + + @Test + void selectionWithArray() { + Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); + Object value = expression.getValue(context); + assertThat(value).asInstanceOf(array(Integer[].class)).containsExactly(0, 1, 2, 3, 4); + } + + @Test + void selectFirstItemInArray() { + Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); + } + + @Test + void selectLastItemInArray() { + Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); + } + + @Test + void selectionWithPrimitiveArray() { + Expression expression = new SpelExpressionParser().parseRaw("ints.?[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); + Object value = expression.getValue(context); + assertThat(value).asInstanceOf(array(Integer[].class)).containsExactly(0, 1, 2, 3, 4); + } + + @Test + void selectFirstItemInPrimitiveArray() { + Expression expression = new SpelExpressionParser().parseRaw("ints.^[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); + } + + @Test + void selectLastItemInPrimitiveArray() { + Expression expression = new SpelExpressionParser().parseRaw("ints.$[#this < 5]"); + EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); + } + + @Test + @SuppressWarnings("unchecked") + void selectionWithMap() { + EvaluationContext context = new StandardEvaluationContext(new MapTestBean()); + ExpressionParser parser = new SpelExpressionParser(); + + Expression exp = parser.parseExpression("colors.?[key.startsWith('b')]"); + Map colorsMap = (Map) exp.getValue(context); + assertThat(colorsMap).containsOnlyKeys("beige", "blue", "brown"); + + exp = parser.parseExpression("colors.?[key.startsWith('X')]"); + + colorsMap = (Map) exp.getValue(context); + assertThat(colorsMap).isEmpty(); + } + + @Test + @SuppressWarnings("unchecked") + void selectFirstItemInMap() { + EvaluationContext context = new StandardEvaluationContext(new MapTestBean()); + ExpressionParser parser = new SpelExpressionParser(); + + Expression exp = parser.parseExpression("colors.^[key.startsWith('b')]"); + Map colorsMap = (Map) exp.getValue(context); + assertThat(colorsMap).containsOnlyKeys("beige"); + } + + @Test + @SuppressWarnings("unchecked") + void selectLastItemInMap() { + EvaluationContext context = new StandardEvaluationContext(new MapTestBean()); + ExpressionParser parser = new SpelExpressionParser(); + + Expression exp = parser.parseExpression("colors.$[key.startsWith('b')]"); + Map colorsMap = (Map) exp.getValue(context); + assertThat(colorsMap).containsOnlyKeys("brown"); + } - @Test - void selectionOnUnsupportedType() { - evaluateAndCheckError("'abc'.?[#this<5]", INVALID_TYPE_FOR_SELECTION); - evaluateAndCheckError("null.?[#this<5]", INVALID_TYPE_FOR_SELECTION); } - @Test - void projectionOnUnsupportedType() { - evaluateAndCheckError("'abc'.![true]", PROJECTION_NOT_SUPPORTED_ON_TYPE); - evaluateAndCheckError("null.![true]", PROJECTION_NOT_SUPPORTED_ON_TYPE); - } + @Nested + class ProjectionTests extends AbstractExpressionTests { - @Test - void selectionOnNullWithSafeNavigation() { - evaluate("null?.?[#this<5]", null, null); - } + @Test + void projectionOnUnsupportedType() { + evaluateAndCheckError("'abc'.![true]", PROJECTION_NOT_SUPPORTED_ON_TYPE); + evaluateAndCheckError("null.![true]", PROJECTION_NOT_SUPPORTED_ON_TYPE); + } - @Test - void projectionOnNullWithSafeNavigation() { - evaluate("null?.![true]", null, null); - } + @Test + void projectionWithSafeNavigation() { + evaluate("null?.![#this]", null, null); + evaluate("{1,2,3}?.![#this % 2]", List.of(1, 0, 1), ArrayList.class); + } - @Test - void selectionWithNonBooleanSelectionCriteria() { - evaluateAndCheckError("mapOfNumbersUpToTen.?['hello']", RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); - evaluateAndCheckError("mapOfNumbersUpToTen.keySet().?['hello']", RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); - } + @Test + @SuppressWarnings("unchecked") + void projectionWithList() { + Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]"); + EvaluationContext context = new StandardEvaluationContext(); + context.setVariable("testList", IntegerTestBean.createList()); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(5, 6, 7); + } - @Test - void selectionAST() { - // select first - SpelExpression expr = (SpelExpression) parser.parseExpression("'abc'.^[true]"); - assertThat(expr.toStringAST()).isEqualTo("'abc'.^[true]"); - - // select all - expr = (SpelExpression) parser.parseExpression("'abc'.?[true]"); - assertThat(expr.toStringAST()).isEqualTo("'abc'.?[true]"); - - // select last - expr = (SpelExpression) parser.parseExpression("'abc'.$[true]"); - assertThat(expr.toStringAST()).isEqualTo("'abc'.$[true]"); - } - - @Test - @SuppressWarnings("unchecked") - void selectionWithList() { - Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new ListTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(0, 1, 2, 3, 4); - } - - @Test - void selectFirstItemInList() { - Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new ListTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); - } - - @Test - void selectLastItemInList() { - Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new ListTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); - } - - @Test - void selectionWithSetAndRegex() { - evaluate("testMap.keySet().?[#this matches '.*o.*']", "[monday]", ArrayList.class); - evaluate("testMap.keySet().?[#this matches '.*r.*'].contains('saturday')", "true", Boolean.class); - evaluate("testMap.keySet().?[#this matches '.*r.*'].size()", "3", Integer.class); - } - - @Test - @SuppressWarnings("unchecked") - void selectionWithSet() { - Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(0, 1, 2, 3, 4); - } - - @Test - void selectFirstItemInSet() { - Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); - } - - @Test - void selectLastItemInSet() { - Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); - } - - @Test - @SuppressWarnings("unchecked") - void selectionWithIterable() { - Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new IterableTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(0, 1, 2, 3, 4); - } - - @Test - void selectionWithArray() { - Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - Object value = expression.getValue(context); - assertThat(value.getClass().isArray()).isTrue(); - TypedValue typedValue = new TypedValue(value); - assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Integer.class); - assertThat((Integer[]) value).containsExactly(0, 1, 2, 3, 4); - } - - @Test - void selectFirstItemInArray() { - Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); - } - - @Test - void selectLastItemInArray() { - Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); - } - - @Test - void selectionWithPrimitiveArray() { - Expression expression = new SpelExpressionParser().parseRaw("ints.?[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - Object value = expression.getValue(context); - assertThat(value.getClass().isArray()).isTrue(); - TypedValue typedValue = new TypedValue(value); - assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Integer.class); - assertThat((Integer[]) value).containsExactly(0, 1, 2, 3, 4); - } - - @Test - void selectFirstItemInPrimitiveArray() { - Expression expression = new SpelExpressionParser().parseRaw("ints.^[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); - } - - @Test - void selectLastItemInPrimitiveArray() { - Expression expression = new SpelExpressionParser().parseRaw("ints.$[#this<5]"); - EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); - } - - @Test - @SuppressWarnings("unchecked") - void selectionWithMap() { - EvaluationContext context = new StandardEvaluationContext(new MapTestBean()); - ExpressionParser parser = new SpelExpressionParser(); - - Expression exp = parser.parseExpression("colors.?[key.startsWith('b')]"); - Map colorsMap = (Map) exp.getValue(context); - assertThat(colorsMap).containsOnlyKeys("beige", "blue", "brown"); - - exp = parser.parseExpression("colors.?[key.startsWith('X')]"); - - colorsMap = (Map) exp.getValue(context); - assertThat(colorsMap).isEmpty(); - } - - @Test - @SuppressWarnings("unchecked") - void selectFirstItemInMap() { - EvaluationContext context = new StandardEvaluationContext(new MapTestBean()); - ExpressionParser parser = new SpelExpressionParser(); - - Expression exp = parser.parseExpression("colors.^[key.startsWith('b')]"); - Map colorsMap = (Map) exp.getValue(context); - assertThat(colorsMap).containsOnlyKeys("beige"); - } - - @Test - @SuppressWarnings("unchecked") - void selectLastItemInMap() { - EvaluationContext context = new StandardEvaluationContext(new MapTestBean()); - ExpressionParser parser = new SpelExpressionParser(); - - Expression exp = parser.parseExpression("colors.$[key.startsWith('b')]"); - Map colorsMap = (Map) exp.getValue(context); - assertThat(colorsMap).containsOnlyKeys("brown"); - } - - @Test - @SuppressWarnings("unchecked") - void projectionWithList() { - Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]"); - EvaluationContext context = new StandardEvaluationContext(); - context.setVariable("testList", IntegerTestBean.createList()); - assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(5, 6, 7); - } - - @Test - void projectionWithMap() { - evaluate("mapOfNumbersUpToTen.![key > 5 ? value : null]", + @Test + void projectionWithMap() { + evaluate("mapOfNumbersUpToTen.![key > 5 ? value : null]", "[null, null, null, null, null, six, seven, eight, nine, ten]", ArrayList.class); - } + } - @Test - @SuppressWarnings("unchecked") - void projectionWithSet() { - Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]"); - EvaluationContext context = new StandardEvaluationContext(); - context.setVariable("testList", IntegerTestBean.createSet()); - assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(5, 6, 7); - } + @Test + @SuppressWarnings("unchecked") + void projectionWithSet() { + Expression expression = new SpelExpressionParser().parseRaw("#testSet.![wrapper.value]"); + EvaluationContext context = new StandardEvaluationContext(); + context.setVariable("testSet", IntegerTestBean.createSet()); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(5, 6, 7); + } - @Test - @SuppressWarnings("unchecked") - void projectionWithIterable() { - Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]"); - EvaluationContext context = new StandardEvaluationContext(); - context.setVariable("testList", IntegerTestBean.createIterable()); - assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(5, 6, 7); - } + @ParameterizedTest + @ValueSource(strings = {"#root.![#this * 2]", "![#this * 2]"}) + void projectionWithIterable(String expressionString) { + Expression expression = new SpelExpressionParser().parseRaw(expressionString); + EvaluationContext context = new StandardEvaluationContext(new IterableTestBean()); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(2, 4, 6, 8, 10); + } + + @Test + void projectionWithArray() { + Expression expression = new SpelExpressionParser().parseRaw("#testArray.![wrapper.value]"); + EvaluationContext context = new StandardEvaluationContext(); + context.setVariable("testArray", IntegerTestBean.createArray()); + Object value = expression.getValue(context); + assertThat(value).asInstanceOf(array(Number[].class)).containsExactly(5, 5.9f, 7); + } - @Test - void projectionWithArray() { - Expression expression = new SpelExpressionParser().parseRaw("#testArray.![wrapper.value]"); - EvaluationContext context = new StandardEvaluationContext(); - context.setVariable("testArray", IntegerTestBean.createArray()); - Object value = expression.getValue(context); - assertThat(value.getClass().isArray()).isTrue(); - TypedValue typedValue = new TypedValue(value); - assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Number.class); - assertThat((Number[]) value).containsExactly(5, 5.9f, 7); } @@ -288,7 +296,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { private final List integers = new ArrayList<>(); - ListTestBean() { + { for (int i = 0; i < 10; i++) { integers.add(i); } @@ -304,7 +312,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { private final Set integers = new LinkedHashSet<>(); - SetTestBean() { + { for (int i = 0; i < 10; i++) { integers.add(i); } @@ -316,18 +324,13 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { } - static class IterableTestBean { + static class IterableTestBean implements Iterable { - private final Set integers = new LinkedHashSet<>(); + private final Collection integers = List.of(1, 2, 3, 4, 5); - IterableTestBean() { - for (int i = 0; i < 10; i++) { - integers.add(i); - } - } - - public Iterable getIntegers() { - return integers; + @Override + public Iterator iterator() { + return integers.iterator(); } } @@ -338,7 +341,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { private final Integer[] integers = new Integer[10]; - ArrayTestBean() { + { for (int i = 0; i < 10; i++) { ints[i] = i; integers[i] = i; @@ -359,8 +362,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { private final Map colors = new TreeMap<>(); - MapTestBean() { - // colors.put("black", "schwarz"); + { colors.put("red", "rot"); colors.put("brown", "braun"); colors.put("blue", "blau"); @@ -376,13 +378,13 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { static class IntegerTestBean { - private final IntegerWrapper wrapper; + private final NumberWrapper wrapper; IntegerTestBean(Number value) { - this.wrapper = new IntegerWrapper(value); + this.wrapper = new NumberWrapper(value); } - public IntegerWrapper getWrapper() { + public NumberWrapper getWrapper() { return this.wrapper; } @@ -402,11 +404,6 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { return set; } - static Iterable createIterable() { - final Set set = createSet(); - return set; - } - static IntegerTestBean[] createArray() { IntegerTestBean[] array = new IntegerTestBean[3]; for (int i = 0; i < 3; i++) { @@ -422,17 +419,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { } - static class IntegerWrapper { - - private final Number value; - - IntegerWrapper(Number value) { - this.value = value; - } - - public Number getValue() { - return this.value; - } - } + record NumberWrapper(Number value) { + }; }