Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce null-safe index operator in SpEL #29847

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,12 @@ private enum IndexedType {ARRAY, LIST, MAP, STRING, OBJECT}
@Nullable
private IndexedType indexedType;

private final boolean nullSafe;

public Indexer(int startPos, int endPos, SpelNodeImpl expr) {

public Indexer(boolean nullSafe, int startPos, int endPos, SpelNodeImpl expr) {
super(startPos, endPos, expr);
this.nullSafe = nullSafe;
}


Expand Down Expand Up @@ -142,6 +145,9 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException

// Raise a proper exception in case of a null target
if (target == null) {
if (this.nullSafe) {
return ValueRef.NullValueRef.INSTANCE;
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
}
// At this point, we need a TypeDescriptor for a non-null target object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ private SpelNodeImpl eatNode() {
@Nullable
private SpelNodeImpl eatNonDottedNode() {
if (peekToken(TokenKind.LSQUARE)) {
if (maybeEatIndexer()) {
if (maybeEatIndexer(false)) {
return pop();
}
}
Expand All @@ -395,7 +395,8 @@ private SpelNodeImpl eatDottedNode() {
Token t = takeToken(); // it was a '.' or a '?.'
boolean nullSafeNavigation = (t.kind == TokenKind.SAFE_NAVI);
if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() ||
maybeEatProjection(nullSafeNavigation) || maybeEatSelection(nullSafeNavigation)) {
maybeEatProjection(nullSafeNavigation) || maybeEatSelection(nullSafeNavigation) ||
maybeEatIndexer(nullSafeNavigation)) {
return pop();
}
if (peekToken() == null) {
Expand Down Expand Up @@ -513,7 +514,8 @@ else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstruct
else if (maybeEatBeanReference()) {
return pop();
}
else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) {
else if (maybeEatProjection(false) || maybeEatSelection(false) ||
maybeEatIndexer(false)) {
return pop();
}
else if (maybeEatInlineListOrMap()) {
Expand Down Expand Up @@ -678,7 +680,7 @@ else if (peekToken(TokenKind.COLON, true)) { // map!
return true;
}

private boolean maybeEatIndexer() {
private boolean maybeEatIndexer(boolean nullSafeNavigation) {
Token t = peekToken();
if (!peekToken(TokenKind.LSQUARE, true)) {
return false;
Expand All @@ -687,7 +689,7 @@ private boolean maybeEatIndexer() {
SpelNodeImpl expr = eatExpression();
Assert.state(expr != null, "No node");
eatToken(TokenKind.RSQUARE);
this.constructedNodes.push(new Indexer(t.startPos, t.endPos, expr));
this.constructedNodes.push(new Indexer(nullSafeNavigation, t.startPos, t.endPos, expr));
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,4 +409,23 @@ void testListsOfMap() {

public List listOfMapsNotGeneric;

@Test
void nullSafeIndex() {
ContextWithNullCollections testContext = new ContextWithNullCollections();
StandardEvaluationContext context = new StandardEvaluationContext(testContext);
Expression expr = new SpelExpressionParser().parseRaw("nullList?.[4]");
assertThat(expr.getValue(context)).isNull();

expr = new SpelExpressionParser().parseRaw("nullArray?.[4]");
assertThat(expr.getValue(context)).isNull();

expr = new SpelExpressionParser().parseRaw("nullMap:?.[4]");
assertThat(expr.getValue(context)).isNull();
}

public static class ContextWithNullCollections {
public List nullList = null;
public String[] nullArray = null;
public Map nullMap = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,16 @@ void SPR5804() {
assertThat(expr.getValue(context)).isEqualTo("bar");
}

@Test
void SPR16929() {
C testContext = new C();
testContext.ls = null;
StandardEvaluationContext context = new StandardEvaluationContext(testContext);
context.addPropertyAccessor(new MapAccessor());
Expression expr = new SpelExpressionParser().parseRaw("ls?.[4]");
assertThat(expr.getValue(context)).isNull();
}

@Test
void SPR5847() {
StandardEvaluationContext context = new StandardEvaluationContext(new TestProperties());
Expand Down