Skip to content

Commit e3ba957

Browse files
sbrannenbclozel
authored andcommitted
Polish SpEL internals
1 parent caec8f4 commit e3ba957

File tree

5 files changed

+135
-172
lines changed

5 files changed

+135
-172
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/ast/AstUtils.java renamed to spring-expression/src/main/java/org/springframework/expression/spel/ast/AccessorUtils.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@
2525
import org.springframework.util.ObjectUtils;
2626

2727
/**
28-
* Utility methods for use in the AST classes.
28+
* Utility methods for use with property and index accessors.
2929
*
3030
* @author Andy Clement
3131
* @author Sam Brannen
3232
* @since 3.0.2
3333
*/
34-
abstract class AstUtils {
34+
abstract class AccessorUtils {
3535

3636
/**
3737
* Determine the set of accessors that should be used to try to access an

spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ private ValueRef getValueRef(ExpressionState state, AccessMode accessMode) throw
253253
// Check for a custom IndexAccessor.
254254
EvaluationContext evalContext = state.getEvaluationContext();
255255
List<IndexAccessor> accessorsToTry =
256-
AstUtils.getAccessorsToTry(target, evalContext.getIndexAccessors());
256+
AccessorUtils.getAccessorsToTry(target, evalContext.getIndexAccessors());
257257
if (accessMode.supportsReads) {
258258
try {
259259
for (IndexAccessor indexAccessor : accessorsToTry) {
@@ -754,7 +754,7 @@ public TypedValue getValue() {
754754
Indexer.this.cachedPropertyReadState = null;
755755
}
756756
List<PropertyAccessor> accessorsToTry =
757-
AstUtils.getAccessorsToTry(targetType, this.evaluationContext.getPropertyAccessors());
757+
AccessorUtils.getAccessorsToTry(targetType, this.evaluationContext.getPropertyAccessors());
758758
for (PropertyAccessor accessor : accessorsToTry) {
759759
if (accessor.canRead(this.evaluationContext, this.targetObject, this.name)) {
760760
if (accessor instanceof ReflectivePropertyAccessor reflectivePropertyAccessor) {
@@ -797,7 +797,7 @@ public void setValue(@Nullable Object newValue) {
797797
Indexer.this.cachedPropertyWriteState = null;
798798
}
799799
List<PropertyAccessor> accessorsToTry =
800-
AstUtils.getAccessorsToTry(targetType, this.evaluationContext.getPropertyAccessors());
800+
AccessorUtils.getAccessorsToTry(targetType, this.evaluationContext.getPropertyAccessors());
801801
for (PropertyAccessor accessor : accessorsToTry) {
802802
if (accessor.canWrite(this.evaluationContext, this.targetObject, this.name)) {
803803
accessor.write(this.evaluationContext, this.targetObject, this.name, newValue);
@@ -1014,7 +1014,7 @@ public TypedValue getValue() {
10141014
Indexer.this.cachedIndexReadState = null;
10151015
}
10161016
List<IndexAccessor> accessorsToTry =
1017-
AstUtils.getAccessorsToTry(this.target, this.evaluationContext.getIndexAccessors());
1017+
AccessorUtils.getAccessorsToTry(this.target, this.evaluationContext.getIndexAccessors());
10181018
for (IndexAccessor indexAccessor : accessorsToTry) {
10191019
if (indexAccessor.canRead(this.evaluationContext, this.target, this.index)) {
10201020
TypedValue result = indexAccessor.read(this.evaluationContext, this.target, this.index);
@@ -1069,7 +1069,7 @@ public void setValue(@Nullable Object newValue) {
10691069
Indexer.this.cachedIndexWriteState = null;
10701070
}
10711071
List<IndexAccessor> accessorsToTry =
1072-
AstUtils.getAccessorsToTry(this.target, this.evaluationContext.getIndexAccessors());
1072+
AccessorUtils.getAccessorsToTry(this.target, this.evaluationContext.getIndexAccessors());
10731073
for (IndexAccessor indexAccessor : accessorsToTry) {
10741074
if (indexAccessor.canWrite(this.evaluationContext, this.target, this.index)) {
10751075
indexAccessor.write(this.evaluationContext, this.target, this.index, newValue);

spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ private TypedValue readProperty(TypedValue contextObject, EvaluationContext eval
201201
}
202202

203203
List<PropertyAccessor> accessorsToTry =
204-
AstUtils.getAccessorsToTry(targetObject, evalContext.getPropertyAccessors());
204+
AccessorUtils.getAccessorsToTry(targetObject, evalContext.getPropertyAccessors());
205205
// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
206206
// get the accessor and use it. If they are not cacheable but report they can read the property
207207
// then ask them to read it
@@ -259,7 +259,7 @@ private void writeProperty(
259259
}
260260

261261
List<PropertyAccessor> accessorsToTry =
262-
AstUtils.getAccessorsToTry(targetObject, evalContext.getPropertyAccessors());
262+
AccessorUtils.getAccessorsToTry(targetObject, evalContext.getPropertyAccessors());
263263
try {
264264
for (PropertyAccessor accessor : accessorsToTry) {
265265
if (accessor.canWrite(evalContext, targetObject, name)) {
@@ -284,7 +284,7 @@ public boolean isWritableProperty(String name, TypedValue contextObject, Evaluat
284284
Object targetObject = contextObject.getValue();
285285
if (targetObject != null) {
286286
List<PropertyAccessor> accessorsToTry =
287-
AstUtils.getAccessorsToTry(targetObject, evalContext.getPropertyAccessors());
287+
AccessorUtils.getAccessorsToTry(targetObject, evalContext.getPropertyAccessors());
288288
for (PropertyAccessor accessor : accessorsToTry) {
289289
try {
290290
if (accessor.canWrite(evalContext, targetObject, name)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.expression.spel.ast;
18+
19+
import java.util.List;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.expression.TargetedAccessor;
24+
import org.springframework.lang.Nullable;
25+
26+
import static org.assertj.core.api.Assertions.assertThat;
27+
28+
/**
29+
* Tests for {@link AccessorUtils}.
30+
*
31+
* @author Sam Brannen
32+
* @since 6.1.15
33+
*/
34+
class AccessorUtilsTests {
35+
36+
private final TargetedAccessor animal1Accessor = createAccessor("Animal1", Animal.class);
37+
38+
private final TargetedAccessor animal2Accessor = createAccessor("Animal2", Animal.class);
39+
40+
private final TargetedAccessor cat1Accessor = createAccessor("Cat1", Cat.class);
41+
42+
private final TargetedAccessor cat2Accessor = createAccessor("Cat2", Cat.class);
43+
44+
private final TargetedAccessor generic1Accessor = createAccessor("Generic1", null);
45+
46+
private final TargetedAccessor generic2Accessor = createAccessor("Generic2", null);
47+
48+
private final List<TargetedAccessor> accessors = List.of(
49+
generic1Accessor,
50+
cat1Accessor,
51+
animal1Accessor,
52+
animal2Accessor,
53+
cat2Accessor,
54+
generic2Accessor
55+
);
56+
57+
58+
@Test
59+
void emptyAccessorsList() {
60+
List<TargetedAccessor> accessorsToTry = AccessorUtils.getAccessorsToTry(new Cat(), List.of());
61+
assertThat(accessorsToTry).isEmpty();
62+
}
63+
64+
@Test
65+
void noMatch() {
66+
List<TargetedAccessor> accessorsToTry = AccessorUtils.getAccessorsToTry(new Dog(), List.of(cat1Accessor));
67+
assertThat(accessorsToTry).isEmpty();
68+
}
69+
70+
@Test
71+
void singleExactTypeMatch() {
72+
List<TargetedAccessor> accessorsToTry = AccessorUtils.getAccessorsToTry(new Cat(), List.of(cat1Accessor));
73+
assertThat(accessorsToTry).containsExactly(cat1Accessor);
74+
}
75+
76+
@Test
77+
void exactTypeSupertypeAndGenericMatches() {
78+
List<TargetedAccessor> accessorsToTry = AccessorUtils.getAccessorsToTry(new Cat(), accessors);
79+
assertThat(accessorsToTry).containsExactly(
80+
cat1Accessor, cat2Accessor, animal1Accessor, animal2Accessor, generic1Accessor, generic2Accessor);
81+
}
82+
83+
@Test
84+
void supertypeAndGenericMatches() {
85+
List<TargetedAccessor> accessorsToTry = AccessorUtils.getAccessorsToTry(new Dog(), accessors);
86+
assertThat(accessorsToTry).containsExactly(
87+
animal1Accessor, animal2Accessor, generic1Accessor, generic2Accessor);
88+
}
89+
90+
@Test
91+
void genericMatches() {
92+
List<TargetedAccessor> accessorsToTry = AccessorUtils.getAccessorsToTry("not an Animal", accessors);
93+
assertThat(accessorsToTry).containsExactly(generic1Accessor, generic2Accessor);
94+
}
95+
96+
97+
private static TargetedAccessor createAccessor(String name, Class<?> type) {
98+
return new DemoAccessor(name, (type != null ? new Class<?>[] { type } : null));
99+
}
100+
101+
102+
private record DemoAccessor(String name, Class<?>[] types) implements TargetedAccessor {
103+
104+
@Override
105+
@Nullable
106+
public Class<?>[] getSpecificTargetClasses() {
107+
return this.types;
108+
}
109+
110+
@Override
111+
public final String toString() {
112+
return this.name;
113+
}
114+
}
115+
116+
sealed interface Animal permits Cat, Dog {
117+
}
118+
119+
static final class Cat implements Animal {
120+
}
121+
122+
static final class Dog implements Animal {
123+
}
124+
125+
}

spring-expression/src/test/java/org/springframework/expression/spel/ast/AstUtilsTests.java

-162
This file was deleted.

0 commit comments

Comments
 (0)