Skip to content

Commit f941754

Browse files
committed
Introduce isNullSafe() in SpelNodeImpl
Prior to this commit, MethodReference and PropertyOrFieldReference already defined local isNullSafe() methods, but we need identical methods in Selection, Projection, and Indexer, and we may potentially need null-safe support for additional operators in the future. To address the common need for an is-null-safe check, this commit introduces an isNullSafe() method in SpelNodeImpl with a default implementation that returns false. Closes gh-32516
1 parent d4495a5 commit f941754

File tree

4 files changed

+19
-9
lines changed

4 files changed

+19
-9
lines changed

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

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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,7 +24,6 @@
2424
import org.springframework.expression.spel.CodeFlow;
2525
import org.springframework.expression.spel.ExpressionState;
2626
import org.springframework.expression.spel.SpelEvaluationException;
27-
import org.springframework.expression.spel.SpelNode;
2827

2928
/**
3029
* Represents a DOT separated expression sequence, such as
@@ -120,14 +119,13 @@ public String toStringAST() {
120119
for (int i = 0; i < getChildCount(); i++) {
121120
sb.append(getChild(i).toStringAST());
122121
if (i < getChildCount() - 1) {
123-
SpelNode nextChild = getChild(i + 1);
122+
SpelNodeImpl nextChild = this.children[i + 1];
123+
if (nextChild.isNullSafe()) {
124+
sb.append("?.");
125+
}
124126
// Don't append a '.' if the next child is an Indexer.
125127
// For example, we want 'myVar[0]' instead of 'myVar.[0]'.
126-
if (!(nextChild instanceof Indexer)) {
127-
if ((nextChild instanceof MethodReference methodRef && methodRef.isNullSafe()) ||
128-
(nextChild instanceof PropertyOrFieldReference pofRef && pofRef.isNullSafe())) {
129-
sb.append('?');
130-
}
128+
else if (!(nextChild instanceof Indexer)) {
131129
sb.append('.');
132130
}
133131
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -77,6 +77,7 @@ public MethodReference(boolean nullSafe, String methodName, int startPos, int en
7777
* Does this node represent a null-safe method reference?
7878
* @since 6.0.13
7979
*/
80+
@Override
8081
public final boolean isNullSafe() {
8182
return this.nullSafe;
8283
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public PropertyOrFieldReference(boolean nullSafe, String propertyOrFieldName, in
7676
/**
7777
* Does this node represent a null-safe property or field reference?
7878
*/
79+
@Override
7980
public boolean isNullSafe() {
8081
return this.nullSafe;
8182
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ public int getEndPosition() {
181181
return this.endPos;
182182
}
183183

184+
/**
185+
* Determine if this node is the target of a null-safe navigation operation.
186+
* <p>The default implementation returns {@code false}.
187+
* @return {@code true} if this node is the target of a null-safe operation
188+
* @since 6.1.6
189+
*/
190+
public boolean isNullSafe() {
191+
return false;
192+
}
193+
184194
/**
185195
* Check whether a node can be compiled to bytecode. The reasoning in each node may
186196
* be different but will typically involve checking whether the exit type descriptor

0 commit comments

Comments
 (0)