1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.expression.spel.ast;
18
19 import org.springframework.asm.Label;
20 import org.springframework.asm.MethodVisitor;
21 import org.springframework.expression.EvaluationException;
22 import org.springframework.expression.TypedValue;
23 import org.springframework.expression.spel.CodeFlow;
24 import org.springframework.expression.spel.ExpressionState;
25 import org.springframework.expression.spel.SpelEvaluationException;
26 import org.springframework.expression.spel.SpelMessage;
27
28
29
30
31
32
33
34
35 public class Ternary extends SpelNodeImpl {
36
37 public Ternary(int pos, SpelNodeImpl... args) {
38 super(pos,args);
39 }
40
41
42
43
44
45
46
47
48
49 @Override
50 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
51 Boolean value = this.children[0].getValue(state, Boolean.class);
52 if (value == null) {
53 throw new SpelEvaluationException(getChild(0).getStartPosition(),
54 SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
55 }
56 TypedValue result = this.children[value ? 1 : 2].getValueInternal(state);
57 computeExitTypeDescriptor();
58 return result;
59 }
60
61 @Override
62 public String toStringAST() {
63 return getChild(0).toStringAST() + " ? " + getChild(1).toStringAST() + " : " + getChild(2).toStringAST();
64 }
65
66 private void computeExitTypeDescriptor() {
67 if (this.exitTypeDescriptor == null && this.children[1].exitTypeDescriptor != null &&
68 this.children[2].exitTypeDescriptor != null) {
69 String leftDescriptor = this.children[1].exitTypeDescriptor;
70 String rightDescriptor = this.children[2].exitTypeDescriptor;
71 if (leftDescriptor.equals(rightDescriptor)) {
72 this.exitTypeDescriptor = leftDescriptor;
73 }
74 else if (leftDescriptor.equals("Ljava/lang/Object") && !CodeFlow.isPrimitive(rightDescriptor)) {
75 this.exitTypeDescriptor = rightDescriptor;
76 }
77 else if (rightDescriptor.equals("Ljava/lang/Object") && !CodeFlow.isPrimitive(leftDescriptor)) {
78 this.exitTypeDescriptor = leftDescriptor;
79 }
80 else {
81
82 this.exitTypeDescriptor = "Ljava/lang/Object";
83 }
84 }
85 }
86
87 @Override
88 public boolean isCompilable() {
89 SpelNodeImpl condition = this.children[0];
90 SpelNodeImpl left = this.children[1];
91 SpelNodeImpl right = this.children[2];
92 return (condition.isCompilable() && left.isCompilable() && right.isCompilable() &&
93 CodeFlow.isBooleanCompatible(condition.exitTypeDescriptor) &&
94 left.exitTypeDescriptor != null && right.exitTypeDescriptor != null);
95 }
96
97 @Override
98 public void generateCode(MethodVisitor mv, CodeFlow cf) {
99
100 computeExitTypeDescriptor();
101 cf.enterCompilationScope();
102 this.children[0].generateCode(mv, cf);
103 if (!CodeFlow.isPrimitive(cf.lastDescriptor())) {
104 CodeFlow.insertUnboxInsns(mv, 'Z', cf.lastDescriptor());
105 }
106 cf.exitCompilationScope();
107 Label elseTarget = new Label();
108 Label endOfIf = new Label();
109 mv.visitJumpInsn(IFEQ, elseTarget);
110 cf.enterCompilationScope();
111 this.children[1].generateCode(mv, cf);
112 if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) {
113 CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
114 }
115 cf.exitCompilationScope();
116 mv.visitJumpInsn(GOTO, endOfIf);
117 mv.visitLabel(elseTarget);
118 cf.enterCompilationScope();
119 this.children[2].generateCode(mv, cf);
120 if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) {
121 CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
122 }
123 cf.exitCompilationScope();
124 mv.visitLabel(endOfIf);
125 cf.pushDescriptor(this.exitTypeDescriptor);
126 }
127
128 }