Skip to content

Commit 1aea016

Browse files
committed
Merge branch '6.1.x'
2 parents 5fee796 + e74406a commit 1aea016

File tree

2 files changed

+79
-17
lines changed

2 files changed

+79
-17
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/support/SimpleEvaluationContext.java

+33-17
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@
6464
* read-only access to properties via {@link DataBindingPropertyAccessor}. Similarly,
6565
* {@link SimpleEvaluationContext#forReadWriteDataBinding()} enables read and write access
6666
* to properties. Alternatively, configure custom accessors via
67-
* {@link SimpleEvaluationContext#forPropertyAccessors} and potentially activate method
68-
* resolution and/or a type converter through the builder.
67+
* {@link SimpleEvaluationContext#forPropertyAccessors}, potentially
68+
* {@linkplain Builder#withAssignmentDisabled() disable assignment}, and optionally
69+
* activate method resolution and/or a type converter through the builder.
6970
*
7071
* <p>Note that {@code SimpleEvaluationContext} is typically not configured
7172
* with a default root object. Instead it is meant to be created once and
@@ -268,9 +269,8 @@ public Object lookupVariable(String name) {
268269
* ({@code ++}), and decrement ({@code --}) operators are disabled.
269270
* @return {@code true} if assignment is enabled; {@code false} otherwise
270271
* @since 5.3.38
271-
* @see #forPropertyAccessors(PropertyAccessor...)
272272
* @see #forReadOnlyDataBinding()
273-
* @see #forReadWriteDataBinding()
273+
* @see Builder#withAssignmentDisabled()
274274
*/
275275
@Override
276276
public boolean isAssignmentEnabled() {
@@ -279,15 +279,18 @@ public boolean isAssignmentEnabled() {
279279

280280
/**
281281
* Create a {@code SimpleEvaluationContext} for the specified {@link PropertyAccessor}
282-
* delegates: typically a custom {@code PropertyAccessor} specific to a use case
283-
* (e.g. attribute resolution in a custom data structure), potentially combined with
284-
* a {@link DataBindingPropertyAccessor} if property dereferences are needed as well.
285-
* <p>Assignment is enabled within expressions evaluated by the context created via
286-
* this factory method.
282+
* delegates: typically a custom {@code PropertyAccessor} specific to a use case &mdash;
283+
* for example, for attribute resolution in a custom data structure &mdash; potentially
284+
* combined with a {@link DataBindingPropertyAccessor} if property dereferences are
285+
* needed as well.
286+
* <p>By default, assignment is enabled within expressions evaluated by the context
287+
* created via this factory method; however, assignment can be disabled via
288+
* {@link Builder#withAssignmentDisabled()}.
287289
* @param accessors the accessor delegates to use
288290
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
289291
* @see DataBindingPropertyAccessor#forReadWriteAccess()
290292
* @see #isAssignmentEnabled()
293+
* @see Builder#withAssignmentDisabled()
291294
*/
292295
public static Builder forPropertyAccessors(PropertyAccessor... accessors) {
293296
for (PropertyAccessor accessor : accessors) {
@@ -296,7 +299,7 @@ public static Builder forPropertyAccessors(PropertyAccessor... accessors) {
296299
"ReflectivePropertyAccessor. Consider using DataBindingPropertyAccessor or a custom subclass.");
297300
}
298301
}
299-
return new Builder(true, accessors);
302+
return new Builder(accessors);
300303
}
301304

302305
/**
@@ -307,22 +310,26 @@ public static Builder forPropertyAccessors(PropertyAccessor... accessors) {
307310
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
308311
* @see #forPropertyAccessors
309312
* @see #isAssignmentEnabled()
313+
* @see Builder#withAssignmentDisabled()
310314
*/
311315
public static Builder forReadOnlyDataBinding() {
312-
return new Builder(false, DataBindingPropertyAccessor.forReadOnlyAccess());
316+
return new Builder(DataBindingPropertyAccessor.forReadOnlyAccess()).withAssignmentDisabled();
313317
}
314318

315319
/**
316320
* Create a {@code SimpleEvaluationContext} for read-write access to
317321
* public properties via {@link DataBindingPropertyAccessor}.
318-
* <p>Assignment is enabled within expressions evaluated by the context created via
319-
* this factory method.
322+
* <p>By default, assignment is enabled within expressions evaluated by the context
323+
* created via this factory method. Assignment can be disabled via
324+
* {@link Builder#withAssignmentDisabled()}; however, it is preferable to use
325+
* {@link #forReadOnlyDataBinding()} if you desire read-only access.
320326
* @see DataBindingPropertyAccessor#forReadWriteAccess()
321327
* @see #forPropertyAccessors
322328
* @see #isAssignmentEnabled()
329+
* @see Builder#withAssignmentDisabled()
323330
*/
324331
public static Builder forReadWriteDataBinding() {
325-
return new Builder(true, DataBindingPropertyAccessor.forReadWriteAccess());
332+
return new Builder(DataBindingPropertyAccessor.forReadWriteAccess());
326333
}
327334

328335

@@ -343,15 +350,24 @@ public static final class Builder {
343350
@Nullable
344351
private TypedValue rootObject;
345352

346-
private final boolean assignmentEnabled;
353+
private boolean assignmentEnabled = true;
347354

348355

349-
private Builder(boolean assignmentEnabled, PropertyAccessor... accessors) {
350-
this.assignmentEnabled = assignmentEnabled;
356+
private Builder(PropertyAccessor... accessors) {
351357
this.propertyAccessors = Arrays.asList(accessors);
352358
}
353359

354360

361+
/**
362+
* Disable assignment within expressions evaluated by this evaluation context.
363+
* @since 5.3.38
364+
* @see SimpleEvaluationContext#isAssignmentEnabled()
365+
*/
366+
public Builder withAssignmentDisabled() {
367+
this.assignmentEnabled = false;
368+
return this;
369+
}
370+
355371
/**
356372
* Register the specified {@link IndexAccessor} delegates.
357373
* @param indexAccessors the index accessors to use

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

+46
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,52 @@ void forPropertyAccessorsInMixedReadOnlyMode() {
211211
assertIncrementAndDecrementWritesForIndexedStructures(context);
212212
}
213213

214+
@Test
215+
void forPropertyAccessorsWithAssignmentDisabled() {
216+
SimpleEvaluationContext context = SimpleEvaluationContext
217+
.forPropertyAccessors(new CompilableMapAccessor(false), DataBindingPropertyAccessor.forReadOnlyAccess())
218+
.withIndexAccessors(colorsIndexAccessor)
219+
.withAssignmentDisabled()
220+
.build();
221+
222+
assertCommonReadOnlyModeBehavior(context);
223+
224+
// WRITE -- via assignment operator
225+
226+
// Variable
227+
assertAssignmentDisabled(context, "#myVar = 'rejected'");
228+
229+
// Property
230+
assertAssignmentDisabled(context, "name = 'rejected'");
231+
assertAssignmentDisabled(context, "map.yellow = 'rejected'");
232+
assertIncrementDisabled(context, "count++");
233+
assertIncrementDisabled(context, "++count");
234+
assertDecrementDisabled(context, "count--");
235+
assertDecrementDisabled(context, "--count");
236+
237+
// Array Index
238+
assertAssignmentDisabled(context, "array[0] = 'rejected'");
239+
assertIncrementDisabled(context, "numbers[0]++");
240+
assertIncrementDisabled(context, "++numbers[0]");
241+
assertDecrementDisabled(context, "numbers[0]--");
242+
assertDecrementDisabled(context, "--numbers[0]");
243+
244+
// List Index
245+
assertAssignmentDisabled(context, "list[0] = 'rejected'");
246+
247+
// Map Index -- key as String
248+
assertAssignmentDisabled(context, "map['red'] = 'rejected'");
249+
250+
// Map Index -- key as pseudo property name
251+
assertAssignmentDisabled(context, "map[yellow] = 'rejected'");
252+
253+
// String Index
254+
assertAssignmentDisabled(context, "name[0] = 'rejected'");
255+
256+
// Object Index
257+
assertAssignmentDisabled(context, "['name'] = 'rejected'");
258+
}
259+
214260

215261
private void assertReadWriteMode(SimpleEvaluationContext context) {
216262
// Variables can always be set programmatically within an EvaluationContext.

0 commit comments

Comments
 (0)