|
24 | 24 | import java.util.Map;
|
25 | 25 | import java.util.Set;
|
26 | 26 | import java.util.TreeMap;
|
| 27 | +import java.util.stream.IntStream; |
27 | 28 |
|
28 | 29 | import org.junit.jupiter.api.Nested;
|
29 | 30 | import org.junit.jupiter.api.Test;
|
|
38 | 39 | import org.springframework.expression.spel.support.StandardEvaluationContext;
|
39 | 40 |
|
40 | 41 | import static org.assertj.core.api.Assertions.assertThat;
|
41 |
| -import static org.assertj.core.api.InstanceOfAssertFactories.LIST; |
42 | 42 | import static org.assertj.core.api.InstanceOfAssertFactories.array;
|
43 | 43 | import static org.springframework.expression.spel.SpelMessage.INVALID_TYPE_FOR_SELECTION;
|
44 | 44 | import static org.springframework.expression.spel.SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE;
|
@@ -141,11 +141,56 @@ void selectLastItemInSet() {
|
141 | 141 | }
|
142 | 142 |
|
143 | 143 | @ParameterizedTest
|
144 |
| - @ValueSource(strings = {"#root.?[#this < 3]", "?[#this < 3]"}) |
| 144 | + @ValueSource(strings = {"#root.?[#this <= 3]", "?[#this <= 3]"}) |
| 145 | + @SuppressWarnings("unchecked") |
145 | 146 | void selectionWithIterable(String expressionString) {
|
146 | 147 | Expression expression = new SpelExpressionParser().parseRaw(expressionString);
|
147 |
| - EvaluationContext context = new StandardEvaluationContext(new IterableTestBean()); |
148 |
| - assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(1, 2); |
| 148 | + EvaluationContext context = new StandardEvaluationContext(new Counter(5)); |
| 149 | + assertThat(expression.getValue(context, List.class)).containsExactly(1, 2, 3); |
| 150 | + } |
| 151 | + |
| 152 | + @ParameterizedTest |
| 153 | + @ValueSource(strings = {"#root.^[#this <= 3]", "^[#this <= 3]"}) |
| 154 | + void selectFirstItemInIterable(String expressionString) { |
| 155 | + Expression expression = new SpelExpressionParser().parseRaw(expressionString); |
| 156 | + EvaluationContext context = new StandardEvaluationContext(new Counter(5)); |
| 157 | + assertThat(expression.getValue(context, Integer.class)).isEqualTo(1); |
| 158 | + } |
| 159 | + |
| 160 | + @ParameterizedTest |
| 161 | + @ValueSource(strings = {"#root.$[#this <= 3]", "$[#this <= 3]"}) |
| 162 | + void selectLastItemInIterable(String expressionString) { |
| 163 | + Expression expression = new SpelExpressionParser().parseRaw(expressionString); |
| 164 | + EvaluationContext context = new StandardEvaluationContext(new Counter(5)); |
| 165 | + assertThat(expression.getValue(context, Integer.class)).isEqualTo(3); |
| 166 | + } |
| 167 | + |
| 168 | + @Test |
| 169 | + void indexIntoIterableSelection() { |
| 170 | + EvaluationContext context = new StandardEvaluationContext(); |
| 171 | + context.setVariable("counter", new Counter(5)); |
| 172 | + ExpressionParser parser = new SpelExpressionParser(); |
| 173 | + |
| 174 | + // Select first (selection expression is hard-coded to true) |
| 175 | + Expression expression = parser.parseExpression("#counter.^[true]"); |
| 176 | + assertThat(expression.getValue(context, Integer.class)).isEqualTo(1); |
| 177 | + |
| 178 | + // Select last (selection expression is hard-coded to true) |
| 179 | + expression = parser.parseExpression("#counter.$[true]"); |
| 180 | + assertThat(expression.getValue(context, Integer.class)).isEqualTo(5); |
| 181 | + |
| 182 | + // Select index (selection expression is hard-coded to true) |
| 183 | + |
| 184 | + // The following will not work, since we cannot index into an Iterable. |
| 185 | + // expression = parser.parseExpression("#counter[1]"); |
| 186 | + |
| 187 | + // The following works, since project selection with a selection expression |
| 188 | + // that always evaluates to true results in a List containing all elements |
| 189 | + // of the Iterable, and we can index into that List. |
| 190 | + expression = parser.parseExpression("#counter.?[true][1]"); |
| 191 | + assertThat(expression.getValue(context, Integer.class)).isEqualTo(2); |
| 192 | + expression = parser.parseExpression("#counter.?[true][2]"); |
| 193 | + assertThat(expression.getValue(context, Integer.class)).isEqualTo(3); |
149 | 194 | }
|
150 | 195 |
|
151 | 196 | @Test
|
@@ -273,10 +318,11 @@ void projectionWithSet() {
|
273 | 318 |
|
274 | 319 | @ParameterizedTest
|
275 | 320 | @ValueSource(strings = {"#root.![#this * 2]", "![#this * 2]"})
|
| 321 | + @SuppressWarnings("unchecked") |
276 | 322 | void projectionWithIterable(String expressionString) {
|
277 | 323 | Expression expression = new SpelExpressionParser().parseRaw(expressionString);
|
278 |
| - EvaluationContext context = new StandardEvaluationContext(new IterableTestBean()); |
279 |
| - assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(2, 4, 6, 8, 10); |
| 324 | + EvaluationContext context = new StandardEvaluationContext(new Counter(5)); |
| 325 | + assertThat(expression.getValue(context, List.class)).containsExactly(2, 4, 6, 8, 10); |
280 | 326 | }
|
281 | 327 |
|
282 | 328 | @Test
|
@@ -323,13 +369,20 @@ public Set<Integer> getIntegers() {
|
323 | 369 | }
|
324 | 370 |
|
325 | 371 |
|
326 |
| - static class IterableTestBean implements Iterable<Integer> { |
| 372 | + /** |
| 373 | + * Simulates a custom {@link Iterable} which is itself not a {@link Collection}. |
| 374 | + */ |
| 375 | + class Counter implements Iterable<Integer> { |
327 | 376 |
|
328 |
| - private final Collection<Integer> integers = List.of(1, 2, 3, 4, 5); |
| 377 | + private final List<Integer> list = new ArrayList<>(); |
| 378 | + |
| 379 | + Counter(int size) { |
| 380 | + IntStream.rangeClosed(1, size).forEach(this.list::add); |
| 381 | + } |
329 | 382 |
|
330 | 383 | @Override
|
331 | 384 | public Iterator<Integer> iterator() {
|
332 |
| - return integers.iterator(); |
| 385 | + return this.list.iterator(); |
333 | 386 | }
|
334 | 387 | }
|
335 | 388 |
|
|
0 commit comments