Skip to content

Commit a55207e

Browse files
committed
Add tests for read-only IndexAccessors in 6.2
1 parent b099964 commit a55207e

File tree

1 file changed

+81
-12
lines changed

1 file changed

+81
-12
lines changed

spring-expression/src/test/java/org/springframework/expression/spel/support/SimpleEvaluationContextTests.java

+81-12
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.junit.jupiter.api.Test;
2626

2727
import org.springframework.expression.Expression;
28+
import org.springframework.expression.IndexAccessor;
2829
import org.springframework.expression.spel.CompilableMapAccessor;
2930
import org.springframework.expression.spel.SpelEvaluationException;
3031
import org.springframework.expression.spel.SpelMessage;
@@ -45,21 +46,28 @@
4546
*/
4647
class SimpleEvaluationContextTests {
4748

49+
private static final IndexAccessor colorsIndexAccessor =
50+
new ReflectiveIndexAccessor(Colors.class, int.class, "get", "set");
51+
4852
private final SpelExpressionParser parser = new SpelExpressionParser();
4953

5054
private final Model model = new Model();
5155

5256

5357
@Test
5458
void forReadWriteDataBinding() {
55-
SimpleEvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
59+
SimpleEvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding()
60+
.withIndexAccessors(colorsIndexAccessor)
61+
.build();
5662

5763
assertReadWriteMode(context);
5864
}
5965

6066
@Test
6167
void forReadOnlyDataBinding() {
62-
SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
68+
SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding()
69+
.withIndexAccessors(colorsIndexAccessor)
70+
.build();
6371

6472
assertCommonReadOnlyModeBehavior(context);
6573

@@ -96,12 +104,16 @@ void forReadOnlyDataBinding() {
96104

97105
// Object Index
98106
assertAssignmentDisabled(context, "['name'] = 'rejected'");
107+
108+
// Custom Index
109+
assertAssignmentDisabled(context, "colors[4] = 'rejected'");
99110
}
100111

101112
@Test
102113
void forPropertyAccessorsInReadWriteMode() {
103114
SimpleEvaluationContext context = SimpleEvaluationContext
104115
.forPropertyAccessors(new CompilableMapAccessor(), DataBindingPropertyAccessor.forReadWriteAccess())
116+
.withIndexAccessors(colorsIndexAccessor)
105117
.build();
106118

107119
assertReadWriteMode(context);
@@ -126,12 +138,13 @@ void forPropertyAccessorsInReadWriteMode() {
126138
@Test
127139
void forPropertyAccessorsInMixedReadOnlyMode() {
128140
SimpleEvaluationContext context = SimpleEvaluationContext
129-
.forPropertyAccessors(new CompilableMapAccessor(), DataBindingPropertyAccessor.forReadOnlyAccess())
141+
.forPropertyAccessors(new CompilableMapAccessor(true), DataBindingPropertyAccessor.forReadOnlyAccess())
142+
.withIndexAccessors(colorsIndexAccessor)
130143
.build();
131144

132145
assertCommonReadOnlyModeBehavior(context);
133146

134-
// Map -- with key as property name supported by CompilableMapAccessor
147+
// Map -- with key as property name supported by CompilableMapAccessor with allowWrite = true.
135148

136149
Expression expression;
137150
expression = parser.parseExpression("map.yellow");
@@ -156,20 +169,24 @@ void forPropertyAccessorsInMixedReadOnlyMode() {
156169
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE));
157170

158171
// Array Index
159-
parser.parseExpression("array[0]").setValue(context, model, "foo");
160-
assertThat(model.array).containsExactly("foo");
172+
expression = parser.parseExpression("array[0] = 'quux'");
173+
assertThat(expression.getValue(context, model, String.class)).isEqualTo("quux");
174+
assertThat(model.array).containsExactly("quux");
161175

162176
// List Index
163-
parser.parseExpression("list[0]").setValue(context, model, "cat");
164-
assertThat(model.list).containsExactly("cat");
177+
expression = parser.parseExpression("list[0] = 'elephant'");
178+
assertThat(expression.getValue(context, model, String.class)).isEqualTo("elephant");
179+
assertThat(model.list).containsExactly("elephant");
165180

166181
// Map Index -- key as String
167-
parser.parseExpression("map['red']").setValue(context, model, "cherry");
168-
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "banana"));
182+
expression = parser.parseExpression("map['red'] = 'strawberry'");
183+
assertThat(expression.getValue(context, model, String.class)).isEqualTo("strawberry");
184+
assertThat(model.map).containsOnly(entry("red", "strawberry"), entry("yellow", "banana"));
169185

170186
// Map Index -- key as pseudo property name
171-
parser.parseExpression("map[yellow]").setValue(context, model, "lemon");
172-
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "lemon"));
187+
expression = parser.parseExpression("map[yellow] = 'star fruit'");
188+
assertThat(expression.getValue(context, model, String.class)).isEqualTo("star fruit");
189+
assertThat(model.map).containsOnly(entry("red", "strawberry"), entry("yellow", "star fruit"));
173190

174191
// String Index
175192
// The Indexer does not support writes when indexing into a String.
@@ -178,10 +195,17 @@ void forPropertyAccessorsInMixedReadOnlyMode() {
178195
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE));
179196

180197
// Object Index
198+
// Although this goes through the Indexer, the PropertyAccessorValueRef actually uses
199+
// registered PropertyAccessors to perform the write access, and that is disabled here.
181200
assertThatSpelEvaluationException()
182201
.isThrownBy(() -> parser.parseExpression("['name'] = 'rejected'").getValue(context, model))
183202
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE));
184203

204+
// Custom Index
205+
expression = parser.parseExpression("colors[5] = 'indigo'");
206+
assertThat(expression.getValue(context, model, String.class)).isEqualTo("indigo");
207+
assertThat(model.colors.get(5)).isEqualTo("indigo");
208+
185209
// WRITE -- via increment and decrement operators
186210

187211
assertIncrementAndDecrementWritesForIndexedStructures(context);
@@ -216,6 +240,10 @@ private void assertReadWriteMode(SimpleEvaluationContext context) {
216240
parser.parseExpression("map[yellow]").setValue(context, model, "lemon");
217241
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "lemon"));
218242

243+
// Custom Index
244+
parser.parseExpression("colors[4]").setValue(context, model, "purple");
245+
assertThat(model.colors.get(4)).isEqualTo("purple");
246+
219247
// READ
220248
assertReadAccess(context);
221249

@@ -270,6 +298,12 @@ private void assertReadWriteMode(SimpleEvaluationContext context) {
270298
expression = parser.parseExpression("['name']");
271299
assertThat(expression.getValue(context, model, String.class)).isEqualTo("new name");
272300

301+
// Custom Index
302+
expression = parser.parseExpression("colors[5] = 'indigo'");
303+
assertThat(expression.getValue(context, model, String.class)).isEqualTo("indigo");
304+
expression = parser.parseExpression("colors[5]");
305+
assertThat(expression.getValue(context, model, String.class)).isEqualTo("indigo");
306+
273307
// WRITE -- via increment and decrement operators
274308

275309
assertIncrementAndDecrementWritesForProperties(context);
@@ -309,6 +343,10 @@ private void assertCommonReadOnlyModeBehavior(SimpleEvaluationContext context) {
309343
parser.parseExpression("map[yellow]").setValue(context, model, "lemon");
310344
assertThat(model.map).containsOnly(entry("red", "cherry"), entry("yellow", "lemon"));
311345

346+
// Custom Index
347+
parser.parseExpression("colors[4]").setValue(context, model, "purple");
348+
assertThat(model.colors.get(4)).isEqualTo("purple");
349+
312350
// Since the setValue() attempts for "name" and "count" failed above, we have to set
313351
// them directly for assertReadAccess().
314352
model.name = "test";
@@ -354,6 +392,10 @@ private void assertReadAccess(SimpleEvaluationContext context) {
354392
// Object Index
355393
expression = parser.parseExpression("['name']");
356394
assertThat(expression.getValue(context, model, String.class)).isEqualTo("test");
395+
396+
// Custom Index
397+
expression = parser.parseExpression("colors[4]");
398+
assertThat(expression.getValue(context, model, String.class)).isEqualTo("purple");
357399
}
358400

359401
private void assertIncrementAndDecrementWritesForProperties(SimpleEvaluationContext context) {
@@ -433,6 +475,7 @@ static class Model {
433475
private final int[] numbers = {99};
434476
private final List<String> list = new ArrayList<>();
435477
private final Map<String, String> map = new HashMap<>();
478+
private final Colors colors = new Colors();
436479

437480
Model() {
438481
this.list.add("replace me");
@@ -472,6 +515,32 @@ public Map<String, String> getMap() {
472515
return this.map;
473516
}
474517

518+
public Colors getColors() {
519+
return this.colors;
520+
}
521+
}
522+
523+
static class Colors {
524+
525+
private final Map<Integer, String> map = new HashMap<>();
526+
527+
{
528+
this.map.put(1, "red");
529+
this.map.put(2, "green");
530+
this.map.put(3, "blue");
531+
}
532+
533+
public String get(int index) {
534+
if (!this.map.containsKey(index)) {
535+
throw new IndexOutOfBoundsException("No color for index " + index);
536+
}
537+
return this.map.get(index);
538+
}
539+
540+
public void set(int index, String color) {
541+
this.map.put(index, color);
542+
}
543+
475544
}
476545

477546
}

0 commit comments

Comments
 (0)