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
@@ -120,15 +120,19 @@ public final class SimpleEvaluationContext implements EvaluationContext {
120
120
121
121
private final Map <String , Object > variables = new HashMap <>();
122
122
123
+ private final boolean assignmentEnabled ;
124
+
123
125
124
126
private SimpleEvaluationContext (List <PropertyAccessor > propertyAccessors , List <IndexAccessor > indexAccessors ,
125
- List <MethodResolver > resolvers , @ Nullable TypeConverter converter , @ Nullable TypedValue rootObject ) {
127
+ List <MethodResolver > resolvers , @ Nullable TypeConverter converter , @ Nullable TypedValue rootObject ,
128
+ boolean assignmentEnabled ) {
126
129
127
130
this .propertyAccessors = propertyAccessors ;
128
131
this .indexAccessors = indexAccessors ;
129
132
this .methodResolvers = resolvers ;
130
133
this .typeConverter = (converter != null ? converter : new StandardTypeConverter ());
131
134
this .rootObject = (rootObject != null ? rootObject : TypedValue .NULL );
135
+ this .assignmentEnabled = assignmentEnabled ;
132
136
}
133
137
134
138
@@ -257,15 +261,33 @@ public Object lookupVariable(String name) {
257
261
return this .variables .get (name );
258
262
}
259
263
264
+ /**
265
+ * Determine if assignment is enabled within expressions evaluated by this evaluation
266
+ * context.
267
+ * <p>If this method returns {@code false}, the assignment ({@code =}), increment
268
+ * ({@code ++}), and decrement ({@code --}) operators are disabled.
269
+ * @return {@code true} if assignment is enabled; {@code false} otherwise
270
+ * @since 5.3.38
271
+ * @see #forPropertyAccessors(PropertyAccessor...)
272
+ * @see #forReadOnlyDataBinding()
273
+ * @see #forReadWriteDataBinding()
274
+ */
275
+ @ Override
276
+ public boolean isAssignmentEnabled () {
277
+ return this .assignmentEnabled ;
278
+ }
260
279
261
280
/**
262
281
* Create a {@code SimpleEvaluationContext} for the specified {@link PropertyAccessor}
263
282
* delegates: typically a custom {@code PropertyAccessor} specific to a use case
264
283
* (e.g. attribute resolution in a custom data structure), potentially combined with
265
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.
266
287
* @param accessors the accessor delegates to use
267
288
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
268
289
* @see DataBindingPropertyAccessor#forReadWriteAccess()
290
+ * @see #isAssignmentEnabled()
269
291
*/
270
292
public static Builder forPropertyAccessors (PropertyAccessor ... accessors ) {
271
293
for (PropertyAccessor accessor : accessors ) {
@@ -274,27 +296,33 @@ public static Builder forPropertyAccessors(PropertyAccessor... accessors) {
274
296
"ReflectivePropertyAccessor. Consider using DataBindingPropertyAccessor or a custom subclass." );
275
297
}
276
298
}
277
- return new Builder (accessors );
299
+ return new Builder (true , accessors );
278
300
}
279
301
280
302
/**
281
303
* Create a {@code SimpleEvaluationContext} for read-only access to
282
304
* public properties via {@link DataBindingPropertyAccessor}.
305
+ * <p>Assignment is disabled within expressions evaluated by the context created via
306
+ * this factory method.
283
307
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
284
308
* @see #forPropertyAccessors
309
+ * @see #isAssignmentEnabled()
285
310
*/
286
311
public static Builder forReadOnlyDataBinding () {
287
- return new Builder (DataBindingPropertyAccessor .forReadOnlyAccess ());
312
+ return new Builder (false , DataBindingPropertyAccessor .forReadOnlyAccess ());
288
313
}
289
314
290
315
/**
291
316
* Create a {@code SimpleEvaluationContext} for read-write access to
292
317
* public properties via {@link DataBindingPropertyAccessor}.
318
+ * <p>Assignment is enabled within expressions evaluated by the context created via
319
+ * this factory method.
293
320
* @see DataBindingPropertyAccessor#forReadWriteAccess()
294
321
* @see #forPropertyAccessors
322
+ * @see #isAssignmentEnabled()
295
323
*/
296
324
public static Builder forReadWriteDataBinding () {
297
- return new Builder (DataBindingPropertyAccessor .forReadWriteAccess ());
325
+ return new Builder (true , DataBindingPropertyAccessor .forReadWriteAccess ());
298
326
}
299
327
300
328
@@ -315,8 +343,11 @@ public static final class Builder {
315
343
@ Nullable
316
344
private TypedValue rootObject ;
317
345
346
+ private final boolean assignmentEnabled ;
318
347
319
- private Builder (PropertyAccessor ... accessors ) {
348
+
349
+ private Builder (boolean assignmentEnabled , PropertyAccessor ... accessors ) {
350
+ this .assignmentEnabled = assignmentEnabled ;
320
351
this .propertyAccessors = Arrays .asList (accessors );
321
352
}
322
353
@@ -410,8 +441,9 @@ public Builder withTypedRootObject(Object rootObject, TypeDescriptor typeDescrip
410
441
411
442
public SimpleEvaluationContext build () {
412
443
return new SimpleEvaluationContext (this .propertyAccessors , this .indexAccessors ,
413
- this .resolvers , this .typeConverter , this .rootObject );
444
+ this .resolvers , this .typeConverter , this .rootObject , this . assignmentEnabled );
414
445
}
446
+
415
447
}
416
448
417
449
}
0 commit comments