Skip to content

Commit c9057fd

Browse files
committed
SPR-6230: SpEL improvements
1 parent 06286b1 commit c9057fd

19 files changed

+848
-77
lines changed

org.springframework.expression/src/main/java/org/springframework/expression/EvaluationContext.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
public interface EvaluationContext {
3434

3535
/**
36-
* @return the root context object against which unqualified properties/methods/etc should be resolved
36+
* @return the default root context object against which unqualified properties/methods/etc should be resolved. This can be overridden when evaluating an expression.
3737
*/
3838
TypedValue getRootObject();
3939

org.springframework.expression/src/main/java/org/springframework/expression/Expression.java

+130-2
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,36 @@ public interface Expression {
3636
*/
3737
public Object getValue() throws EvaluationException;
3838

39+
/**
40+
* Evaluate this expression against the specified root object
41+
*
42+
* @param rootObject the root object against which properties/etc will be resolved
43+
* @return the evaluation result
44+
* @throws EvaluationException if there is a problem during evaluation
45+
*/
46+
public Object getValue(Object rootObject) throws EvaluationException;
47+
3948
/**
40-
* Evaluate the expression in the default standard context. If the result of the evaluation does not match (and
49+
* Evaluate the expression in the default context. If the result of the evaluation does not match (and
4150
* cannot be converted to) the expected result type then an exception will be returned.
4251
*
4352
* @param desiredResultType the class the caller would like the result to be
4453
* @return the evaluation result
4554
* @throws EvaluationException if there is a problem during evaluation
4655
*/
4756
public <T> T getValue(Class<T> desiredResultType) throws EvaluationException;
57+
58+
/**
59+
* Evaluate the expression in the default context against the specified root object. If the
60+
* result of the evaluation does not match (and cannot be converted to) the expected result type
61+
* then an exception will be returned.
62+
*
63+
* @param rootObject the root object against which properties/etc will be resolved
64+
* @param desiredResultType the class the caller would like the result to be
65+
* @return the evaluation result
66+
* @throws EvaluationException if there is a problem during evaluation
67+
*/
68+
public <T> T getValue(Object rootObject, Class<T> desiredResultType) throws EvaluationException;
4869

4970
/**
5071
* Evaluate this expression in the provided context and return the result of evaluation.
@@ -55,6 +76,17 @@ public interface Expression {
5576
*/
5677
public Object getValue(EvaluationContext context) throws EvaluationException;
5778

79+
/**
80+
* Evaluate this expression in the provided context and return the result of evaluation, but use
81+
* the supplied root context as an override for any default root object specified in the context.
82+
*
83+
* @param context the context in which to evaluate the expression
84+
* @param rootObject the root object against which properties/etc will be resolved
85+
* @return the evaluation result
86+
* @throws EvaluationException if there is a problem during evaluation
87+
*/
88+
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException;
89+
5890
/**
5991
* Evaluate the expression in a specified context which can resolve references to properties, methods, types, etc -
6092
* the type of the evaluation result is expected to be of a particular class and an exception will be thrown if it
@@ -67,6 +99,20 @@ public interface Expression {
6799
*/
68100
public <T> T getValue(EvaluationContext context, Class<T> desiredResultType) throws EvaluationException;
69101

102+
/**
103+
* Evaluate the expression in a specified context which can resolve references to properties, methods, types, etc -
104+
* the type of the evaluation result is expected to be of a particular class and an exception will be thrown if it
105+
* is not and cannot be converted to that type. The supplied root object overrides any default specified on the
106+
* supplied context.
107+
*
108+
* @param context the context in which to evaluate the expression
109+
* @param rootObject the root object against which properties/etc will be resolved
110+
* @param desiredResultType the class the caller would like the result to be
111+
* @return the evaluation result
112+
* @throws EvaluationException if there is a problem during evaluation
113+
*/
114+
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> desiredResultType) throws EvaluationException;
115+
70116
/**
71117
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method using
72118
* the default context.
@@ -76,6 +122,16 @@ public interface Expression {
76122
*/
77123
public Class getValueType() throws EvaluationException;
78124

125+
/**
126+
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method using
127+
* the default context.
128+
*
129+
* @param rootObject the root object against which to evaluate the expression
130+
* @return the most general type of value that can be set on this context
131+
* @throws EvaluationException if there is a problem determining the type
132+
*/
133+
public Class getValueType(Object rootObject) throws EvaluationException;
134+
79135
/**
80136
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
81137
* the given context.
@@ -85,6 +141,17 @@ public interface Expression {
85141
* @throws EvaluationException if there is a problem determining the type
86142
*/
87143
public Class getValueType(EvaluationContext context) throws EvaluationException;
144+
145+
/**
146+
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
147+
* the given context. The supplied root object overrides any specified in the context.
148+
*
149+
* @param context the context in which to evaluate the expression
150+
* @param rootObject the root object against which to evaluate the expression
151+
* @return the most general type of value that can be set on this context
152+
* @throws EvaluationException if there is a problem determining the type
153+
*/
154+
public Class getValueType(EvaluationContext context, Object rootObject) throws EvaluationException;
88155

89156
/**
90157
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method using
@@ -94,6 +161,16 @@ public interface Expression {
94161
* @throws EvaluationException if there is a problem determining the type
95162
*/
96163
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException;
164+
165+
/**
166+
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method using
167+
* the default context.
168+
*
169+
* @param rootObject the root object against which to evaluate the expression
170+
* @return a type descriptor for the most general type of value that can be set on this context
171+
* @throws EvaluationException if there is a problem determining the type
172+
*/
173+
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException;
97174

98175
/**
99176
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
@@ -105,6 +182,17 @@ public interface Expression {
105182
*/
106183
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException;
107184

185+
/**
186+
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
187+
* the given context. The supplied root object overrides any specified in the context.
188+
*
189+
* @param context the context in which to evaluate the expression
190+
* @param rootObject the root object against which to evaluate the expression
191+
* @return a type descriptor for the most general type of value that can be set on this context
192+
* @throws EvaluationException if there is a problem determining the type
193+
*/
194+
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject) throws EvaluationException;
195+
108196
/**
109197
* Determine if an expression can be written to, i.e. setValue() can be called.
110198
*
@@ -113,7 +201,27 @@ public interface Expression {
113201
* @throws EvaluationException if there is a problem determining if it is writable
114202
*/
115203
public boolean isWritable(EvaluationContext context) throws EvaluationException;
116-
204+
205+
/**
206+
* Determine if an expression can be written to, i.e. setValue() can be called.
207+
* The supplied root object overrides any specified in the context.
208+
*
209+
* @param context the context in which the expression should be checked
210+
* @param rootObject the root object against which to evaluate the expression
211+
* @return true if the expression is writable
212+
* @throws EvaluationException if there is a problem determining if it is writable
213+
*/
214+
public boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException;
215+
216+
/**
217+
* Determine if an expression can be written to, i.e. setValue() can be called.
218+
*
219+
* @param rootObject the root object against which to evaluate the expression
220+
* @return true if the expression is writable
221+
* @throws EvaluationException if there is a problem determining if it is writable
222+
*/
223+
public boolean isWritable(Object rootObject) throws EvaluationException;
224+
117225
/**
118226
* Set this expression in the provided context to the value provided.
119227
*
@@ -122,7 +230,27 @@ public interface Expression {
122230
* @throws EvaluationException if there is a problem during evaluation
123231
*/
124232
public void setValue(EvaluationContext context, Object value) throws EvaluationException;
233+
234+
/**
235+
* Set this expression in the provided context to the value provided.
236+
*
237+
* @param rootObject the root object against which to evaluate the expression
238+
* @param value the new value
239+
* @throws EvaluationException if there is a problem during evaluation
240+
*/
241+
public void setValue(Object rootObject, Object value) throws EvaluationException;
125242

243+
/**
244+
* Set this expression in the provided context to the value provided.
245+
* The supplied root object overrides any specified in the context.
246+
*
247+
* @param context the context in which to set the value of the expression
248+
* @param rootObject the root object against which to evaluate the expression
249+
* @param value the new value
250+
* @throws EvaluationException if there is a problem during evaluation
251+
*/
252+
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException;
253+
126254
/**
127255
* Returns the original string used to create this expression, unmodified.
128256
*

org.springframework.expression/src/main/java/org/springframework/expression/ExpressionException.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
* @author Andy Clement
2424
* @since 3.0
2525
*/
26-
public class ExpressionException extends Exception {
26+
public class ExpressionException extends RuntimeException {
2727

2828
protected String expressionString;
2929
protected int position; // -1 if not known - but should be known in all reasonable cases

org.springframework.expression/src/main/java/org/springframework/expression/PropertyAccessor.java

-5
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@
2525
* to determine if it can read or write them. Property resolvers are considered to be ordered and each will be called in
2626
* turn. The only rule that affects the call order is that any naming the target class directly in
2727
* getSpecifiedTargetClasses() will be called first, before the general resolvers.
28-
*
29-
* <p>If the cost of locating the property is expensive, in relation to actually retrieving its value, consider extending
30-
* CacheablePropertyAccessor rather than directly implementing PropertyAccessor. A CacheablePropertyAccessor enables the
31-
* discovery (resolution) of the property to be done once and then an object (an executor) returned and cached by the
32-
* infrastructure that can be used repeatedly to retrieve the property value.
3328
*
3429
* @author Andy Clement
3530
* @since 3.0

org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class TypedValue {
3333

3434
private final Object value;
3535

36-
private final TypeDescriptor typeDescriptor;
36+
private TypeDescriptor typeDescriptor;
3737

3838

3939
/**
@@ -43,7 +43,7 @@ public class TypedValue {
4343
*/
4444
public TypedValue(Object value) {
4545
this.value = value;
46-
this.typeDescriptor = TypeDescriptor.forObject(value);
46+
this.typeDescriptor = null;// initialized when/if requested
4747
}
4848

4949
/**
@@ -62,6 +62,9 @@ public Object getValue() {
6262
}
6363

6464
public TypeDescriptor getTypeDescriptor() {
65+
if (this.typeDescriptor==null) {
66+
this.typeDescriptor = TypeDescriptor.forObject(value);
67+
}
6568
return this.typeDescriptor;
6669
}
6770

org.springframework.expression/src/main/java/org/springframework/expression/common/CompositeStringExpression.java

+62
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ public String getValue(EvaluationContext context) throws EvaluationException {
7070
}
7171
return sb.toString();
7272
}
73+
74+
public String getValue(Object rootObject) throws EvaluationException {
75+
StringBuilder sb = new StringBuilder();
76+
for (Expression expression : this.expressions) {
77+
sb.append(ObjectUtils.getDisplayString(expression.getValue(rootObject)));
78+
}
79+
return sb.toString();
80+
}
81+
7382

7483
public Class getValueType(EvaluationContext context) {
7584
return String.class;
@@ -109,4 +118,57 @@ public Expression[] getExpressions() {
109118
return expressions;
110119
}
111120

121+
122+
public <T> T getValue(Object rootObject, Class<T> desiredResultType) throws EvaluationException {
123+
Object value = getValue(rootObject);
124+
return ExpressionUtils.convert(null, value, desiredResultType);
125+
}
126+
127+
public String getValue(EvaluationContext context, Object rootObject) throws EvaluationException {
128+
StringBuilder sb = new StringBuilder();
129+
for (Expression expression : this.expressions) {
130+
sb.append(ObjectUtils.getDisplayString(expression.getValue(context,rootObject)));
131+
}
132+
return sb.toString();
133+
}
134+
135+
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> desiredResultType)
136+
throws EvaluationException {
137+
Object value = getValue(context,rootObject);
138+
return ExpressionUtils.convert(context, value, desiredResultType);
139+
}
140+
141+
public Class getValueType(Object rootObject) throws EvaluationException {
142+
return String.class;
143+
}
144+
145+
public Class getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
146+
return String.class;
147+
}
148+
149+
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException {
150+
return TypeDescriptor.valueOf(String.class);
151+
}
152+
153+
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject)
154+
throws EvaluationException {
155+
return TypeDescriptor.valueOf(String.class);
156+
}
157+
158+
public boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException {
159+
return false;
160+
}
161+
162+
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException {
163+
throw new EvaluationException(this.expressionString, "Cannot call setValue on a composite expression");
164+
}
165+
166+
public boolean isWritable(Object rootObject) throws EvaluationException {
167+
return false;
168+
}
169+
170+
public void setValue(Object rootObject, Object value) throws EvaluationException {
171+
throw new EvaluationException(this.expressionString, "Cannot call setValue on a composite expression");
172+
}
173+
112174
}

0 commit comments

Comments
 (0)