Skip to content

Commit 1e30997

Browse files
committed
Polishing
1 parent 0e0c298 commit 1e30997

File tree

3 files changed

+78
-86
lines changed

3 files changed

+78
-86
lines changed

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
*/
4141
public class InlineList extends SpelNodeImpl {
4242

43-
// If the list is purely literals, it is a constant value and can be computed and cached
4443
@Nullable
4544
private final TypedValue constant;
4645

@@ -52,10 +51,9 @@ public InlineList(int startPos, int endPos, SpelNodeImpl... args) {
5251

5352

5453
/**
55-
* If all the components of the list are constants, or lists
56-
* that themselves contain constants, then a constant list
57-
* can be built to represent this node. This will speed up
58-
* later getValue calls and reduce the amount of garbage
54+
* If all the components of the list are constants, or lists that themselves
55+
* contain constants, then a constant list can be built to represent this node.
56+
* <p>This will speed up later getValue calls and reduce the amount of garbage
5957
* created.
6058
*/
6159
@Nullable

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,21 @@
3737
*/
3838
public class InlineMap extends SpelNodeImpl {
3939

40-
// If the map is purely literals, it is a constant value and can be computed and cached
4140
@Nullable
4241
private final TypedValue constant;
4342

4443

4544
public InlineMap(int startPos, int endPos, SpelNodeImpl... args) {
4645
super(startPos, endPos, args);
47-
this.constant = computeConstantValue();
46+
this.constant = computeConstantValue();
4847
}
4948

5049

5150
/**
5251
* If all the components of the map are constants, or lists/maps that themselves
5352
* contain constants, then a constant list can be built to represent this node.
54-
* This will speed up later getValue calls and reduce the amount of garbage created.
53+
* <p>This will speed up later getValue calls and reduce the amount of garbage
54+
* created.
5555
*/
5656
@Nullable
5757
private TypedValue computeConstantValue() {
@@ -78,9 +78,7 @@ else if (!(c % 2 == 0 && child instanceof PropertyOrFieldReference)) {
7878
int childCount = getChildCount();
7979
for (int c = 0; c < childCount; c++) {
8080
SpelNode keyChild = getChild(c++);
81-
SpelNode valueChild = getChild(c);
8281
Object key;
83-
Object value = null;
8482
if (keyChild instanceof Literal literal) {
8583
key = literal.getLiteralValue().getValue();
8684
}
@@ -90,6 +88,9 @@ else if (keyChild instanceof PropertyOrFieldReference propertyOrFieldReference)
9088
else {
9189
return null;
9290
}
91+
92+
SpelNode valueChild = getChild(c);
93+
Object value = null;
9394
if (valueChild instanceof Literal literal) {
9495
value = literal.getLiteralValue().getValue();
9596
}
@@ -113,7 +114,6 @@ public TypedValue getValueInternal(ExpressionState expressionState) throws Evalu
113114
Map<Object, Object> returnValue = new LinkedHashMap<>();
114115
int childcount = getChildCount();
115116
for (int c = 0; c < childcount; c++) {
116-
// TODO allow for key being PropertyOrFieldReference like Indexer on maps
117117
SpelNode keyChild = getChild(c++);
118118
Object key = null;
119119
if (keyChild instanceof PropertyOrFieldReference reference) {
@@ -146,7 +146,7 @@ public String toStringAST() {
146146
}
147147

148148
/**
149-
* Return whether this list is a constant value.
149+
* Return whether this map is a constant value.
150150
*/
151151
public boolean isConstant() {
152152
return this.constant != null;
Lines changed: 68 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424

2525
import org.junit.jupiter.api.Test;
2626

27+
import org.springframework.expression.Expression;
2728
import org.springframework.expression.spel.ast.InlineMap;
2829
import org.springframework.expression.spel.standard.SpelExpression;
2930
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -35,33 +36,26 @@
3536
* Test usage of inline maps.
3637
*
3738
* @author Andy Clement
39+
* @author Sam Brannen
3840
* @since 4.1
3941
*/
40-
public class MapTests extends AbstractExpressionTests {
42+
class MapTests extends AbstractExpressionTests {
4143

42-
// if the list is full of literals then it will be of the type unmodifiableClass
44+
// if the list is full of literals then it will be of the type unmodifiableMapClass
4345
// rather than HashMap (or similar)
44-
Class<?> unmodifiableClass = Collections.unmodifiableMap(new LinkedHashMap<>()).getClass();
46+
private static final Class<?> unmodifiableMapClass = Collections.unmodifiableMap(Map.of()).getClass();
4547

4648

4749
@Test
48-
public void testInlineMapCreation01() {
49-
evaluate("{'a':1, 'b':2, 'c':3, 'd':4, 'e':5}", "{a=1, b=2, c=3, d=4, e=5}", unmodifiableClass);
50-
evaluate("{'a':1}", "{a=1}", unmodifiableClass);
50+
void inlineMapCreationForLiterals() {
51+
evaluate("{'a':1, 'b':2, 'c':3, 'd':4, 'e':5}", "{a=1, b=2, c=3, d=4, e=5}", unmodifiableMapClass);
52+
evaluate("{'a':1}", "{a=1}", unmodifiableMapClass);
53+
evaluate("{'abc':'def', 'uvw':'xyz'}", "{abc=def, uvw=xyz}", unmodifiableMapClass);
54+
evaluate("{:}", "{}", unmodifiableMapClass);
5155
}
5256

5357
@Test
54-
public void testInlineMapCreation02() {
55-
evaluate("{'abc':'def', 'uvw':'xyz'}", "{abc=def, uvw=xyz}", unmodifiableClass);
56-
}
57-
58-
@Test
59-
public void testInlineMapCreation03() {
60-
evaluate("{:}", "{}", unmodifiableClass);
61-
}
62-
63-
@Test
64-
public void testInlineMapCreation04() {
58+
void inlineMapCreationForNonLiterals() {
6559
evaluate("{'key':'abc'=='xyz'}", "{key=false}", LinkedHashMap.class);
6660
evaluate("{key:'abc'=='xyz'}", "{key=false}", LinkedHashMap.class);
6761
evaluate("{key:'abc'=='xyz',key2:true}[key]", "false", Boolean.class);
@@ -70,43 +64,48 @@ public void testInlineMapCreation04() {
7064
}
7165

7266
@Test
73-
public void testInlineMapAndNesting() {
74-
evaluate("{a:{a:1,b:2,c:3},b:{d:4,e:5,f:6}}", "{a={a=1, b=2, c=3}, b={d=4, e=5, f=6}}", unmodifiableClass);
75-
evaluate("{a:{x:1,y:'2',z:3},b:{u:4,v:{'a','b'},w:5,x:6}}", "{a={x=1, y=2, z=3}, b={u=4, v=[a, b], w=5, x=6}}", unmodifiableClass);
76-
evaluate("{a:{1,2,3},b:{4,5,6}}", "{a=[1, 2, 3], b=[4, 5, 6]}", unmodifiableClass);
67+
void inlineMapAndNesting() {
68+
evaluate("{a:{a:1,b:2,c:3},b:{d:4,e:5,f:6}}", "{a={a=1, b=2, c=3}, b={d=4, e=5, f=6}}", unmodifiableMapClass);
69+
evaluate("{a:{x:1,y:'2',z:3},b:{u:4,v:{'a','b'},w:5,x:6}}", "{a={x=1, y=2, z=3}, b={u=4, v=[a, b], w=5, x=6}}", unmodifiableMapClass);
70+
evaluate("{a:{1,2,3},b:{4,5,6}}", "{a=[1, 2, 3], b=[4, 5, 6]}", unmodifiableMapClass);
7771
}
7872

7973
@Test
80-
public void testInlineMapWithFunkyKeys() {
81-
evaluate("{#root.name:true}","{Nikola Tesla=true}",LinkedHashMap.class);
74+
void inlineMapWithFunkyKeys() {
75+
evaluate("{#root.name:true}", "{Nikola Tesla=true}", LinkedHashMap.class);
8276
}
8377

8478
@Test
85-
public void testInlineMapError() {
79+
void inlineMapSyntaxError() {
8680
parseAndCheckError("{key:'abc'", SpelMessage.OOD);
8781
}
8882

8983
@Test
90-
public void testRelOperatorsIs02() {
91-
evaluate("{a:1, b:2, c:3, d:4, e:5} instanceof T(java.util.Map)", "true", Boolean.class);
84+
void inelineMapIsInstanceOfMap() {
85+
evaluate("{a:1, b:2} instanceof T(java.util.Map)", "true", Boolean.class);
9286
}
9387

9488
@Test
95-
public void testInlineMapAndProjectionSelection() {
96-
evaluate("{a:1,b:2,c:3,d:4,e:5,f:6}.![value>3]", "[false, false, false, true, true, true]", ArrayList.class);
97-
evaluate("{a:1,b:2,c:3,d:4,e:5,f:6}.?[value>3]", "{d=4, e=5, f=6}", HashMap.class);
98-
evaluate("{a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8,i:9,j:10}.?[value%2==0]", "{b=2, d=4, f=6, h=8, j=10}", HashMap.class);
99-
// TODO this looks like a serious issue (but not a new one): the context object against which arguments are evaluated seems wrong:
100-
// evaluate("{a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8,i:9,j:10}.?[isEven(value) == 'y']", "[2, 4, 6, 8, 10]", ArrayList.class);
89+
void inlineMapProjection() {
90+
evaluate("{a:1,b:2,c:3,d:4}.![value > 2]", "[false, false, true, true]", ArrayList.class);
91+
evaluate("{a:1,b:2,c:3,d:4}.![value % 2 == 0]", "[false, true, false, true]", ArrayList.class);
92+
evaluate("{a:1,b:2,c:3,d:4}.![#isEven(value) == 'y']", "[false, true, false, true]", ArrayList.class);
10193
}
10294

10395
@Test
104-
public void testSetConstruction01() {
105-
evaluate("new java.util.HashMap().putAll({a:'a',b:'b',c:'c'})", null, Object.class);
96+
void inlineMapSelection() {
97+
evaluate("{a:1,b:2,c:3,d:4}.?[value > 2]", "{c=3, d=4}", HashMap.class);
98+
evaluate("{a:1,b:2,c:3,d:4,e:5,f:6}.?[value % 2 == 0]", "{b=2, d=4, f=6}", HashMap.class);
99+
evaluate("{a:1,b:2,c:3,d:4,e:5,f:6}.?[#isEven(value) == 'y']", "{b=2, d=4, f=6}", HashMap.class);
106100
}
107101

108102
@Test
109-
public void testConstantRepresentation1() {
103+
void mapConstruction() {
104+
evaluate("new java.util.HashMap().putAll({a:'a',b:'b'})", null, Object.class);
105+
}
106+
107+
@Test
108+
void constantMaps() {
110109
checkConstantMap("{f:{'a','b','c'}}", true);
111110
checkConstantMap("{'a':1,'b':2,'c':3,'d':4,'e':5}", true);
112111
checkConstantMap("{aaa:'abc'}", true);
@@ -117,43 +116,37 @@ public void testConstantRepresentation1() {
117116
checkConstantMap("{#root.name:true}",false);
118117
checkConstantMap("{a:1,b:2,c:{d:true,e:false}}", true);
119118
checkConstantMap("{a:1,b:2,c:{d:{1,2,3},e:{4,5,6},f:{'a','b','c'}}}", true);
120-
// for nested InlineMap
121-
checkConstantMap("{a:{k:#d}}", false);
119+
checkConstantMap("{a:{k:#d}}", false); // nested InlineMap
122120
checkConstantMap("{@bean:@bean}", false);
123121
}
124122

125123
private void checkConstantMap(String expressionText, boolean expectedToBeConstant) {
126124
SpelExpressionParser parser = new SpelExpressionParser();
127125
SpelExpression expression = (SpelExpression) parser.parseExpression(expressionText);
128126
SpelNode node = expression.getAST();
129-
boolean condition = node instanceof InlineMap;
130-
assertThat(condition).isTrue();
131-
InlineMap inlineMap = (InlineMap) node;
132-
if (expectedToBeConstant) {
133-
assertThat(inlineMap.isConstant()).isTrue();
134-
}
135-
else {
136-
assertThat(inlineMap.isConstant()).isFalse();
137-
}
127+
assertThat(node).isInstanceOfSatisfying(InlineMap.class, inlineMap -> {
128+
if (expectedToBeConstant) {
129+
assertThat(inlineMap.isConstant()).isTrue();
130+
}
131+
else {
132+
assertThat(inlineMap.isConstant()).isFalse();
133+
}
134+
});
138135
}
139136

140137
@Test
141-
public void testInlineMapWriting() {
142-
// list should be unmodifiable
143-
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
144-
evaluate("{a:1, b:2, c:3, d:4, e:5}[a]=6", "[a:1,b: 2,c: 3,d: 4,e: 5]", unmodifiableClass));
138+
void inlineMapIsUnmodifiable() {
139+
Expression expr = parser.parseExpression("{a:1}[a] = 6");
140+
assertThatExceptionOfType(UnsupportedOperationException.class)
141+
.isThrownBy(() -> expr.getValue(context));
145142
}
146143

147144
@Test
148-
public void testMapKeysThatAreAlsoSpELKeywords() {
145+
void mapKeysThatAreAlsoSpELKeywords() {
149146
SpelExpressionParser parser = new SpelExpressionParser();
150147
SpelExpression expression = null;
151148
Object o = null;
152149

153-
// expression = (SpelExpression) parser.parseExpression("foo['NEW']");
154-
// o = expression.getValue(new MapHolder());
155-
// assertEquals("VALUE",o);
156-
157150
expression = (SpelExpression) parser.parseExpression("foo[T]");
158151
o = expression.getValue(new MapHolder());
159152
assertThat(o).isEqualTo("TV");
@@ -162,6 +155,10 @@ public void testMapKeysThatAreAlsoSpELKeywords() {
162155
o = expression.getValue(new MapHolder());
163156
assertThat(o).isEqualTo("tv");
164157

158+
expression = (SpelExpression) parser.parseExpression("foo['NEW']");
159+
o = expression.getValue(new MapHolder());
160+
assertThat(o).isEqualTo("VALUE");
161+
165162
expression = (SpelExpression) parser.parseExpression("foo[NEW]");
166163
o = expression.getValue(new MapHolder());
167164
assertThat(o).isEqualTo("VALUE");
@@ -174,35 +171,32 @@ public void testMapKeysThatAreAlsoSpELKeywords() {
174171
o = expression.getValue(new MapHolder());
175172
assertThat(o).isEqualTo("value");
176173

177-
expression = (SpelExpression)parser.parseExpression("foo[foo[NEW]]");
174+
expression = (SpelExpression) parser.parseExpression("foo[foo[NEW]]");
178175
o = expression.getValue(new MapHolder());
179176
assertThat(o).isEqualTo("37");
180177

181-
expression = (SpelExpression)parser.parseExpression("foo[foo[new]]");
178+
expression = (SpelExpression) parser.parseExpression("foo[foo[new]]");
182179
o = expression.getValue(new MapHolder());
183180
assertThat(o).isEqualTo("38");
184181

185-
expression = (SpelExpression)parser.parseExpression("foo[foo[foo[T]]]");
182+
expression = (SpelExpression) parser.parseExpression("foo[foo[foo[T]]]");
186183
o = expression.getValue(new MapHolder());
187184
assertThat(o).isEqualTo("value");
188185
}
189186

190187
@SuppressWarnings({ "rawtypes", "unchecked" })
191-
public static class MapHolder {
192-
193-
public Map foo;
194-
195-
public MapHolder() {
196-
foo = new HashMap();
197-
foo.put("NEW", "VALUE");
198-
foo.put("new", "value");
199-
foo.put("T", "TV");
200-
foo.put("t", "tv");
201-
foo.put("abc.def", "value");
202-
foo.put("VALUE","37");
203-
foo.put("value","38");
204-
foo.put("TV","new");
205-
}
188+
static class MapHolder {
189+
190+
public Map foo = Map.of(
191+
"NEW", "VALUE",
192+
"new", "value",
193+
"T", "TV",
194+
"t", "tv",
195+
"abc.def", "value",
196+
"VALUE","37",
197+
"value","38",
198+
"TV","new"
199+
);
206200
}
207201

208202
}

0 commit comments

Comments
 (0)