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.MethodVisitor;
20  import org.springframework.expression.EvaluationException;
21  import org.springframework.expression.TypedValue;
22  import org.springframework.expression.spel.CodeFlow;
23  import org.springframework.expression.spel.ExpressionState;
24  import org.springframework.expression.spel.SpelEvaluationException;
25  
26  /**
27   * Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()'
28   *
29   * @author Andy Clement
30   * @since 3.0
31   */
32  public class CompoundExpression extends SpelNodeImpl {
33  
34  	public CompoundExpression(int pos,SpelNodeImpl... expressionComponents) {
35  		super(pos, expressionComponents);
36  		if (expressionComponents.length < 2) {
37  			throw new IllegalStateException("Do not build compound expressions with less than two entries: " +
38  					expressionComponents.length);
39  		}
40  	}
41  
42  
43  	@Override
44  	protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
45  		if (getChildCount() == 1) {
46  			return this.children[0].getValueRef(state);
47  		}
48  
49  		SpelNodeImpl nextNode = this.children[0];
50  		try {
51  			TypedValue result = nextNode.getValueInternal(state);
52  			int cc = getChildCount();
53  			for (int i = 1; i < cc - 1; i++) {
54  				try {
55  					state.pushActiveContextObject(result);
56  					nextNode = this.children[i];
57  					result = nextNode.getValueInternal(state);
58  				}
59  				finally {
60  					state.popActiveContextObject();
61  				}
62  			}
63  			try {
64  				state.pushActiveContextObject(result);
65  				nextNode = this.children[cc-1];
66  				return nextNode.getValueRef(state);
67  			}
68  			finally {
69  				state.popActiveContextObject();
70  			}
71  		}
72  		catch (SpelEvaluationException ex) {
73  			// Correct the position for the error before re-throwing
74  			ex.setPosition(nextNode.getStartPosition());
75  			throw ex;
76  		}
77  	}
78  
79  	/**
80  	 * Evaluates a compound expression. This involves evaluating each piece in turn and the
81  	 * return value from each piece is the active context object for the subsequent piece.
82  	 * @param state the state in which the expression is being evaluated
83  	 * @return the final value from the last piece of the compound expression
84  	 */
85  	@Override
86  	public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
87  		ValueRef ref = getValueRef(state);
88  		TypedValue result = ref.getValue();
89  		this.exitTypeDescriptor = this.children[this.children.length - 1].exitTypeDescriptor;
90  		return result;
91  	}
92  
93  	@Override
94  	public void setValue(ExpressionState state, Object value) throws EvaluationException {
95  		getValueRef(state).setValue(value);
96  	}
97  
98  	@Override
99  	public boolean isWritable(ExpressionState state) throws EvaluationException {
100 		return getValueRef(state).isWritable();
101 	}
102 
103 	@Override
104 	public String toStringAST() {
105 		StringBuilder sb = new StringBuilder();
106 		for (int i = 0; i < getChildCount(); i++) {
107 			if (i > 0) {
108 				sb.append(".");
109 			}
110 			sb.append(getChild(i).toStringAST());
111 		}
112 		return sb.toString();
113 	}
114 	
115 	@Override
116 	public boolean isCompilable() {
117 		for (SpelNodeImpl child: this.children) {
118 			if (!child.isCompilable()) {
119 				return false;
120 			}
121 		}
122 		return true;
123 	}
124 	
125 	@Override
126 	public void generateCode(MethodVisitor mv, CodeFlow cf) {
127 		// TODO could optimize T(SomeType).staticMethod - no need to generate the T() part
128 		for (int i = 0; i < this.children.length;i++) {
129 			SpelNodeImpl child = this.children[i];
130 			if (child instanceof TypeReference && (i + 1) < this.children.length &&
131 					this.children[i + 1] instanceof MethodReference) {
132 				continue;
133 			}
134 			child.generateCode(mv, cf);
135 		}
136 		cf.pushDescriptor(this.exitTypeDescriptor);
137 	}
138 
139 }