001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.expression.spel.ast; 018 019import org.springframework.asm.MethodVisitor; 020import org.springframework.expression.EvaluationException; 021import org.springframework.expression.TypedValue; 022import org.springframework.expression.spel.CodeFlow; 023import org.springframework.expression.spel.ExpressionState; 024import org.springframework.expression.spel.SpelEvaluationException; 025 026/** 027 * Represents a DOT separated expression sequence, such as 028 * {@code 'property1.property2.methodOne()'}. 029 * 030 * @author Andy Clement 031 * @since 3.0 032 */ 033public class CompoundExpression extends SpelNodeImpl { 034 035 public CompoundExpression(int pos, SpelNodeImpl... expressionComponents) { 036 super(pos, expressionComponents); 037 if (expressionComponents.length < 2) { 038 throw new IllegalStateException("Do not build compound expressions with less than two entries: " + 039 expressionComponents.length); 040 } 041 } 042 043 044 @Override 045 protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { 046 if (getChildCount() == 1) { 047 return this.children[0].getValueRef(state); 048 } 049 050 SpelNodeImpl nextNode = this.children[0]; 051 try { 052 TypedValue result = nextNode.getValueInternal(state); 053 int cc = getChildCount(); 054 for (int i = 1; i < cc - 1; i++) { 055 try { 056 state.pushActiveContextObject(result); 057 nextNode = this.children[i]; 058 result = nextNode.getValueInternal(state); 059 } 060 finally { 061 state.popActiveContextObject(); 062 } 063 } 064 try { 065 state.pushActiveContextObject(result); 066 nextNode = this.children[cc - 1]; 067 return nextNode.getValueRef(state); 068 } 069 finally { 070 state.popActiveContextObject(); 071 } 072 } 073 catch (SpelEvaluationException ex) { 074 // Correct the position for the error before re-throwing 075 ex.setPosition(nextNode.getStartPosition()); 076 throw ex; 077 } 078 } 079 080 /** 081 * Evaluates a compound expression. This involves evaluating each piece in turn and the 082 * return value from each piece is the active context object for the subsequent piece. 083 * @param state the state in which the expression is being evaluated 084 * @return the final value from the last piece of the compound expression 085 */ 086 @Override 087 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { 088 ValueRef ref = getValueRef(state); 089 TypedValue result = ref.getValue(); 090 this.exitTypeDescriptor = this.children[this.children.length - 1].exitTypeDescriptor; 091 return result; 092 } 093 094 @Override 095 public void setValue(ExpressionState state, Object value) throws EvaluationException { 096 getValueRef(state).setValue(value); 097 } 098 099 @Override 100 public boolean isWritable(ExpressionState state) throws EvaluationException { 101 return getValueRef(state).isWritable(); 102 } 103 104 @Override 105 public String toStringAST() { 106 StringBuilder sb = new StringBuilder(); 107 for (int i = 0; i < getChildCount(); i++) { 108 if (i > 0) { 109 sb.append("."); 110 } 111 sb.append(getChild(i).toStringAST()); 112 } 113 return sb.toString(); 114 } 115 116 @Override 117 public boolean isCompilable() { 118 for (SpelNodeImpl child: this.children) { 119 if (!child.isCompilable()) { 120 return false; 121 } 122 } 123 return true; 124 } 125 126 @Override 127 public void generateCode(MethodVisitor mv, CodeFlow cf) { 128 for (SpelNodeImpl child : this.children) { 129 child.generateCode(mv, cf); 130 } 131 cf.pushDescriptor(this.exitTypeDescriptor); 132 } 133 134}