diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java index 8a540a908d..593e5164b0 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import org.springframework.expression.TypedValue; import org.springframework.expression.spel.CodeFlow; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelEvaluationException; -import org.springframework.expression.spel.SpelNode; /** * Represents a DOT separated expression sequence, such as @@ -120,14 +119,13 @@ public class CompoundExpression extends SpelNodeImpl { for (int i = 0; i < getChildCount(); i++) { sb.append(getChild(i).toStringAST()); if (i < getChildCount() - 1) { - SpelNode nextChild = getChild(i + 1); + SpelNodeImpl nextChild = this.children[i + 1]; + if (nextChild.isNullSafe()) { + sb.append("?."); + } // Don't append a '.' if the next child is an Indexer. // For example, we want 'myVar[0]' instead of 'myVar.[0]'. - if (!(nextChild instanceof Indexer)) { - if ((nextChild instanceof MethodReference methodRef && methodRef.isNullSafe()) || - (nextChild instanceof PropertyOrFieldReference pofRef && pofRef.isNullSafe())) { - sb.append('?'); - } + else if (!(nextChild instanceof Indexer)) { sb.append('.'); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java index 3b60cff3ae..ed0cdca884 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,6 +77,7 @@ public class MethodReference extends SpelNodeImpl { * Does this node represent a null-safe method reference? * @since 6.0.13 */ + @Override public final boolean isNullSafe() { return this.nullSafe; } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java index a033f6c14b..bb2e171973 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java @@ -76,6 +76,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl { /** * Does this node represent a null-safe property or field reference? */ + @Override public boolean isNullSafe() { return this.nullSafe; } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java index f6cbaa6c97..3fb9208bc1 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java @@ -181,6 +181,16 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes { return this.endPos; } + /** + * Determine if this node is the target of a null-safe navigation operation. + *

The default implementation returns {@code false}. + * @return {@code true} if this node is the target of a null-safe operation + * @since 6.1.6 + */ + public boolean isNullSafe() { + return false; + } + /** * Check whether a node can be compiled to bytecode. The reasoning in each node may * be different but will typically involve checking whether the exit type descriptor