View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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   * Represents a ternary expression, for example: "someCheck()?true:false".
30   *
31   * @author Andy Clement
32   * @author Juergen Hoeller
33   * @since 3.0
34   */
35  public class Ternary extends SpelNodeImpl {
36  
37  	public Ternary(int pos, SpelNodeImpl... args) {
38  		super(pos,args);
39  	}
40  
41  
42  	/**
43  	 * Evaluate the condition and if true evaluate the first alternative, otherwise
44  	 * evaluate the second alternative.
45  	 * @param state the expression state
46  	 * @throws EvaluationException if the condition does not evaluate correctly to
47  	 * a boolean or there is a problem executing the chosen alternative
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  				// Use the easiest to compute common super type
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  		// May reach here without it computed if all elements are literals
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 }