1
1
/*
2
- * Copyright 2002-2023 the original author or authors.
2
+ * Copyright 2002-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .expression .spel .ast ;
18
18
19
+ import java .util .Optional ;
20
+
19
21
import org .springframework .asm .Label ;
20
22
import org .springframework .asm .MethodVisitor ;
21
23
import org .springframework .expression .EvaluationException ;
26
28
import org .springframework .util .ObjectUtils ;
27
29
28
30
/**
29
- * Represents the Elvis operator <code>?:</code>. For an expression <code>a?:b</code> if <code>a</code> is neither null
30
- * nor an empty String, the value of the expression is <code>a</code>.
31
- * If <code>a</code> is null or the empty String, then the value of the expression is <code>b</code>.
31
+ * Represents the Elvis operator {@code ?:}.
32
+ *
33
+ * <p>For the expression "{@code A ?: B}", if {@code A} is neither {@code null},
34
+ * an empty {@link Optional}, nor an empty {@link String}, the value of the
35
+ * expression is {@code A}, or {@code A.get()} for an {@code Optional}. If
36
+ * {@code A} is {@code null}, an empty {@code Optional}, or an
37
+ * empty {@code String}, the value of the expression is {@code B}.
32
38
*
33
39
* @author Andy Clement
34
40
* @author Juergen Hoeller
@@ -43,18 +49,32 @@ public Elvis(int startPos, int endPos, SpelNodeImpl... args) {
43
49
44
50
45
51
/**
46
- * Evaluate the condition and if neither null nor an empty String, return it.
47
- * If it is null or an empty String, return the other value.
52
+ * If the left-hand operand is neither neither {@code null}, an empty
53
+ * {@link Optional}, nor an empty {@link String}, return its value, or the
54
+ * value contained in the {@code Optional}. If the left-hand operand is
55
+ * {@code null}, an empty {@code Optional}, or an empty {@code String},
56
+ * return the other value.
48
57
* @param state the expression state
49
- * @throws EvaluationException if the condition does not evaluate correctly
50
- * to a boolean or there is a problem executing the chosen alternative
58
+ * @throws EvaluationException if the null/empty check does not evaluate correctly
59
+ * or there is a problem evaluating the alternative
51
60
*/
52
61
@ Override
53
62
public TypedValue getValueInternal (ExpressionState state ) throws EvaluationException {
54
- TypedValue value = this .children [0 ].getValueInternal (state );
63
+ TypedValue leftHandTypedValue = this .children [0 ].getValueInternal (state );
64
+ Object leftHandValue = leftHandTypedValue .getValue ();
65
+
66
+ if (leftHandValue instanceof Optional <?> optional ) {
67
+ // Compilation is currently not supported for Optional with the Elvis operator.
68
+ this .exitTypeDescriptor = null ;
69
+ if (optional .isPresent ()) {
70
+ return new TypedValue (optional .get ());
71
+ }
72
+ return this .children [1 ].getValueInternal (state );
73
+ }
74
+
55
75
// If this check is changed, the generateCode method will need changing too
56
- if (value . getValue () != null && !"" .equals (value . getValue () )) {
57
- return value ;
76
+ if (leftHandValue != null && !"" .equals (leftHandValue )) {
77
+ return leftHandTypedValue ;
58
78
}
59
79
else {
60
80
TypedValue result = this .children [1 ].getValueInternal (state );
0 commit comments