51
51
* SpEL language syntax, e.g. excluding references to Java types, constructors,
52
52
* and bean references.
53
53
*
54
- * <p>When creating a {@code SimpleEvaluationContext} you need to choose the
55
- * level of support that you need for property access in SpEL expressions:
54
+ * <p>When creating a {@code SimpleEvaluationContext} you need to choose the level of
55
+ * support that you need for data binding in SpEL expressions:
56
56
* <ul>
57
- * <li>A custom {@code PropertyAccessor} (typically not reflection-based),
58
- * potentially combined with a {@link DataBindingPropertyAccessor} </li>
59
- * <li>Data binding properties for read-only access</li>
60
- * <li>Data binding properties for read and write </li>
57
+ * <li>Data binding for read-only access</li>
58
+ * <li>Data binding for read and write access </li>
59
+ * <li>A custom {@code PropertyAccessor} (typically not reflection-based), potentially
60
+ * combined with a {@link DataBindingPropertyAccessor} </li>
61
61
* </ul>
62
62
*
63
- * <p>Conveniently, {@link SimpleEvaluationContext#forReadOnlyDataBinding()}
64
- * enables read access to properties via {@link DataBindingPropertyAccessor};
65
- * same for {@link SimpleEvaluationContext#forReadWriteDataBinding()} when
66
- * write access is needed as well . Alternatively, configure custom accessors
67
- * via {@link SimpleEvaluationContext#forPropertyAccessors}, and potentially
68
- * activate method resolution and/or a type converter through the builder.
63
+ * <p>Conveniently, {@link SimpleEvaluationContext#forReadOnlyDataBinding()} enables
64
+ * read-only access to properties via {@link DataBindingPropertyAccessor}. Similarly,
65
+ * {@link SimpleEvaluationContext#forReadWriteDataBinding()} enables read and write access
66
+ * 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.
69
69
*
70
70
* <p>Note that {@code SimpleEvaluationContext} is typically not configured
71
71
* with a default root object. Instead it is meant to be created once and
72
- * used repeatedly through {@code getValue} calls on a pre-compiled
72
+ * used repeatedly through {@code getValue} calls on a predefined
73
73
* {@link org.springframework.expression.Expression} with both an
74
74
* {@code EvaluationContext} and a root object as arguments:
75
75
* {@link org.springframework.expression.Expression#getValue(EvaluationContext, Object)}.
89
89
* @author Juergen Hoeller
90
90
* @author Sam Brannen
91
91
* @since 4.3.15
92
- * @see #forPropertyAccessors
93
92
* @see #forReadOnlyDataBinding()
94
93
* @see #forReadWriteDataBinding()
94
+ * @see #forPropertyAccessors
95
95
* @see StandardEvaluationContext
96
96
* @see StandardTypeConverter
97
97
* @see DataBindingPropertyAccessor
@@ -118,14 +118,18 @@ public final class SimpleEvaluationContext implements EvaluationContext {
118
118
119
119
private final Map <String , Object > variables = new HashMap <>();
120
120
121
+ private final boolean assignmentEnabled ;
122
+
123
+
121
124
122
125
private SimpleEvaluationContext (List <PropertyAccessor > accessors , List <MethodResolver > resolvers ,
123
- @ Nullable TypeConverter converter , @ Nullable TypedValue rootObject ) {
126
+ @ Nullable TypeConverter converter , @ Nullable TypedValue rootObject , boolean assignmentEnabled ) {
124
127
125
128
this .propertyAccessors = accessors ;
126
129
this .methodResolvers = resolvers ;
127
130
this .typeConverter = (converter != null ? converter : new StandardTypeConverter ());
128
131
this .rootObject = (rootObject != null ? rootObject : TypedValue .NULL );
132
+ this .assignmentEnabled = assignmentEnabled ;
129
133
}
130
134
131
135
@@ -253,15 +257,33 @@ public Object lookupVariable(String name) {
253
257
return this .variables .get (name );
254
258
}
255
259
260
+ /**
261
+ * Determine if assignment is enabled within expressions evaluated by this evaluation
262
+ * context.
263
+ * <p>If this method returns {@code false}, the assignment ({@code =}), increment
264
+ * ({@code ++}), and decrement ({@code --}) operators are disabled.
265
+ * @return {@code true} if assignment is enabled; {@code false} otherwise
266
+ * @since 5.3.38
267
+ * @see #forPropertyAccessors(PropertyAccessor...)
268
+ * @see #forReadOnlyDataBinding()
269
+ * @see #forReadWriteDataBinding()
270
+ */
271
+ @ Override
272
+ public boolean isAssignmentEnabled () {
273
+ return this .assignmentEnabled ;
274
+ }
256
275
257
276
/**
258
277
* Create a {@code SimpleEvaluationContext} for the specified {@link PropertyAccessor}
259
278
* delegates: typically a custom {@code PropertyAccessor} specific to a use case
260
279
* (e.g. attribute resolution in a custom data structure), potentially combined with
261
280
* a {@link DataBindingPropertyAccessor} if property dereferences are needed as well.
281
+ * <p>Assignment is enabled within expressions evaluated by the context created via
282
+ * this factory method.
262
283
* @param accessors the accessor delegates to use
263
284
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
264
285
* @see DataBindingPropertyAccessor#forReadWriteAccess()
286
+ * @see #isAssignmentEnabled()
265
287
*/
266
288
public static Builder forPropertyAccessors (PropertyAccessor ... accessors ) {
267
289
for (PropertyAccessor accessor : accessors ) {
@@ -270,27 +292,33 @@ public static Builder forPropertyAccessors(PropertyAccessor... accessors) {
270
292
"ReflectivePropertyAccessor. Consider using DataBindingPropertyAccessor or a custom subclass." );
271
293
}
272
294
}
273
- return new Builder (accessors );
295
+ return new Builder (true , accessors );
274
296
}
275
297
276
298
/**
277
299
* Create a {@code SimpleEvaluationContext} for read-only access to
278
300
* public properties via {@link DataBindingPropertyAccessor}.
301
+ * <p>Assignment is disabled within expressions evaluated by the context created via
302
+ * this factory method.
279
303
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
280
304
* @see #forPropertyAccessors
305
+ * @see #isAssignmentEnabled()
281
306
*/
282
307
public static Builder forReadOnlyDataBinding () {
283
- return new Builder (DataBindingPropertyAccessor .forReadOnlyAccess ());
308
+ return new Builder (false , DataBindingPropertyAccessor .forReadOnlyAccess ());
284
309
}
285
310
286
311
/**
287
312
* Create a {@code SimpleEvaluationContext} for read-write access to
288
313
* public properties via {@link DataBindingPropertyAccessor}.
314
+ * <p>Assignment is enabled within expressions evaluated by the context created via
315
+ * this factory method.
289
316
* @see DataBindingPropertyAccessor#forReadWriteAccess()
290
317
* @see #forPropertyAccessors
318
+ * @see #isAssignmentEnabled()
291
319
*/
292
320
public static Builder forReadWriteDataBinding () {
293
- return new Builder (DataBindingPropertyAccessor .forReadWriteAccess ());
321
+ return new Builder (true , DataBindingPropertyAccessor .forReadWriteAccess ());
294
322
}
295
323
296
324
@@ -309,7 +337,10 @@ public static final class Builder {
309
337
@ Nullable
310
338
private TypedValue rootObject ;
311
339
312
- private Builder (PropertyAccessor ... accessors ) {
340
+ private final boolean assignmentEnabled ;
341
+
342
+ private Builder (boolean assignmentEnabled , PropertyAccessor ... accessors ) {
343
+ this .assignmentEnabled = assignmentEnabled ;
313
344
this .accessors = Arrays .asList (accessors );
314
345
}
315
346
@@ -391,7 +422,8 @@ public Builder withTypedRootObject(Object rootObject, TypeDescriptor typeDescrip
391
422
}
392
423
393
424
public SimpleEvaluationContext build () {
394
- return new SimpleEvaluationContext (this .accessors , this .resolvers , this .typeConverter , this .rootObject );
425
+ return new SimpleEvaluationContext (this .accessors , this .resolvers , this .typeConverter , this .rootObject ,
426
+ this .assignmentEnabled );
395
427
}
396
428
}
397
429
0 commit comments