1
1
/*
2
- * Copyright 2002-2023 the original author or authors.
2
+ * Copyright 2002-2024 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
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)}.
81
81
* @author Juergen Hoeller
82
82
* @author Sam Brannen
83
83
* @since 4.3.15
84
- * @see #forPropertyAccessors
85
84
* @see #forReadOnlyDataBinding()
86
85
* @see #forReadWriteDataBinding()
86
+ * @see #forPropertyAccessors
87
87
* @see StandardEvaluationContext
88
88
* @see StandardTypeConverter
89
89
* @see DataBindingPropertyAccessor
@@ -109,14 +109,17 @@ public final class SimpleEvaluationContext implements EvaluationContext {
109
109
110
110
private final Map <String , Object > variables = new HashMap <>();
111
111
112
+ private final boolean assignmentEnabled ;
113
+
112
114
113
115
private SimpleEvaluationContext (List <PropertyAccessor > accessors , List <MethodResolver > resolvers ,
114
- @ Nullable TypeConverter converter , @ Nullable TypedValue rootObject ) {
116
+ @ Nullable TypeConverter converter , @ Nullable TypedValue rootObject , boolean assignmentEnabled ) {
115
117
116
118
this .propertyAccessors = accessors ;
117
119
this .methodResolvers = resolvers ;
118
120
this .typeConverter = (converter != null ? converter : new StandardTypeConverter ());
119
121
this .rootObject = (rootObject != null ? rootObject : TypedValue .NULL );
122
+ this .assignmentEnabled = assignmentEnabled ;
120
123
}
121
124
122
125
@@ -224,15 +227,33 @@ public Object lookupVariable(String name) {
224
227
return this .variables .get (name );
225
228
}
226
229
230
+ /**
231
+ * Determine if assignment is enabled within expressions evaluated by this evaluation
232
+ * context.
233
+ * <p>If this method returns {@code false}, the assignment ({@code =}), increment
234
+ * ({@code ++}), and decrement ({@code --}) operators are disabled.
235
+ * @return {@code true} if assignment is enabled; {@code false} otherwise
236
+ * @since 5.3.38
237
+ * @see #forPropertyAccessors(PropertyAccessor...)
238
+ * @see #forReadOnlyDataBinding()
239
+ * @see #forReadWriteDataBinding()
240
+ */
241
+ @ Override
242
+ public boolean isAssignmentEnabled () {
243
+ return this .assignmentEnabled ;
244
+ }
227
245
228
246
/**
229
247
* Create a {@code SimpleEvaluationContext} for the specified {@link PropertyAccessor}
230
248
* delegates: typically a custom {@code PropertyAccessor} specific to a use case
231
249
* (e.g. attribute resolution in a custom data structure), potentially combined with
232
250
* a {@link DataBindingPropertyAccessor} if property dereferences are needed as well.
251
+ * <p>Assignment is enabled within expressions evaluated by the context created via
252
+ * this factory method.
233
253
* @param accessors the accessor delegates to use
234
254
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
235
255
* @see DataBindingPropertyAccessor#forReadWriteAccess()
256
+ * @see #isAssignmentEnabled()
236
257
*/
237
258
public static Builder forPropertyAccessors (PropertyAccessor ... accessors ) {
238
259
for (PropertyAccessor accessor : accessors ) {
@@ -241,34 +262,40 @@ public static Builder forPropertyAccessors(PropertyAccessor... accessors) {
241
262
"ReflectivePropertyAccessor. Consider using DataBindingPropertyAccessor or a custom subclass." );
242
263
}
243
264
}
244
- return new Builder (accessors );
265
+ return new Builder (true , accessors );
245
266
}
246
267
247
268
/**
248
269
* Create a {@code SimpleEvaluationContext} for read-only access to
249
270
* public properties via {@link DataBindingPropertyAccessor}.
271
+ * <p>Assignment is disabled within expressions evaluated by the context created via
272
+ * this factory method.
250
273
* @see DataBindingPropertyAccessor#forReadOnlyAccess()
251
274
* @see #forPropertyAccessors
275
+ * @see #isAssignmentEnabled()
252
276
*/
253
277
public static Builder forReadOnlyDataBinding () {
254
- return new Builder (DataBindingPropertyAccessor .forReadOnlyAccess ());
278
+ return new Builder (false , DataBindingPropertyAccessor .forReadOnlyAccess ());
255
279
}
256
280
257
281
/**
258
282
* Create a {@code SimpleEvaluationContext} for read-write access to
259
283
* public properties via {@link DataBindingPropertyAccessor}.
284
+ * <p>Assignment is enabled within expressions evaluated by the context created via
285
+ * this factory method.
260
286
* @see DataBindingPropertyAccessor#forReadWriteAccess()
261
287
* @see #forPropertyAccessors
288
+ * @see #isAssignmentEnabled()
262
289
*/
263
290
public static Builder forReadWriteDataBinding () {
264
- return new Builder (DataBindingPropertyAccessor .forReadWriteAccess ());
291
+ return new Builder (true , DataBindingPropertyAccessor .forReadWriteAccess ());
265
292
}
266
293
267
294
268
295
/**
269
296
* Builder for {@code SimpleEvaluationContext}.
270
297
*/
271
- public static class Builder {
298
+ public static final class Builder {
272
299
273
300
private final List <PropertyAccessor > accessors ;
274
301
@@ -280,10 +307,15 @@ public static class Builder {
280
307
@ Nullable
281
308
private TypedValue rootObject ;
282
309
283
- public Builder (PropertyAccessor ... accessors ) {
310
+ private final boolean assignmentEnabled ;
311
+
312
+
313
+ private Builder (boolean assignmentEnabled , PropertyAccessor ... accessors ) {
314
+ this .assignmentEnabled = assignmentEnabled ;
284
315
this .accessors = Arrays .asList (accessors );
285
316
}
286
317
318
+
287
319
/**
288
320
* Register the specified {@link MethodResolver} delegates for
289
321
* a combination of property access and method resolution.
@@ -315,7 +347,6 @@ public Builder withInstanceMethods() {
315
347
return this ;
316
348
}
317
349
318
-
319
350
/**
320
351
* Register a custom {@link ConversionService}.
321
352
* <p>By default a {@link StandardTypeConverter} backed by a
@@ -327,6 +358,7 @@ public Builder withConversionService(ConversionService conversionService) {
327
358
this .typeConverter = new StandardTypeConverter (conversionService );
328
359
return this ;
329
360
}
361
+
330
362
/**
331
363
* Register a custom {@link TypeConverter}.
332
364
* <p>By default a {@link StandardTypeConverter} backed by a
@@ -362,7 +394,8 @@ public Builder withTypedRootObject(Object rootObject, TypeDescriptor typeDescrip
362
394
}
363
395
364
396
public SimpleEvaluationContext build () {
365
- return new SimpleEvaluationContext (this .accessors , this .resolvers , this .typeConverter , this .rootObject );
397
+ return new SimpleEvaluationContext (this .accessors , this .resolvers , this .typeConverter , this .rootObject ,
398
+ this .assignmentEnabled );
366
399
}
367
400
}
368
401
0 commit comments