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 java.util.StringJoiner; 020 021import org.springframework.asm.MethodVisitor; 022import org.springframework.expression.EvaluationException; 023import org.springframework.expression.TypedValue; 024import org.springframework.expression.spel.CodeFlow; 025import org.springframework.expression.spel.ExpressionState; 026import org.springframework.expression.spel.SpelEvaluationException; 027import org.springframework.lang.Nullable; 028 029/** 030 * Represents a DOT separated expression sequence, such as 031 * {@code 'property1.property2.methodOne()'}. 032 * 033 * @author Andy Clement 034 * @since 3.0 035 */ 036public class CompoundExpression extends SpelNodeImpl { 037 038 public CompoundExpression(int startPos, int endPos, SpelNodeImpl... expressionComponents) { 039 super(startPos, endPos, expressionComponents); 040 if (expressionComponents.length < 2) { 041 throw new IllegalStateException("Do not build compound expressions with less than two entries: " + 042 expressionComponents.length); 043 } 044 } 045 046 047 @Override 048 protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { 049 if (getChildCount() == 1) { 050 return this.children[0].getValueRef(state); 051 } 052 053 SpelNodeImpl nextNode = this.children[0]; 054 try { 055 TypedValue result = nextNode.getValueInternal(state); 056 int cc = getChildCount(); 057 for (int i = 1; i < cc - 1; i++) { 058 try { 059 state.pushActiveContextObject(result); 060 nextNode = this.children[i]; 061 result = nextNode.getValueInternal(state); 062 } 063 finally { 064 state.popActiveContextObject(); 065 } 066 } 067 try { 068 state.pushActiveContextObject(result); 069 nextNode = this.children[cc - 1]; 070 return nextNode.getValueRef(state); 071 } 072 finally { 073 state.popActiveContextObject(); 074 } 075 } 076 catch (SpelEvaluationException ex) { 077 // Correct the position for the error before re-throwing 078 ex.setPosition(nextNode.getStartPosition()); 079 throw ex; 080 } 081 } 082 083 /** 084 * Evaluates a compound expression. This involves evaluating each piece in turn and the 085 * return value from each piece is the active context object for the subsequent piece. 086 * @param state the state in which the expression is being evaluated 087 * @return the final value from the last piece of the compound expression 088 */ 089 @Override 090 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { 091 ValueRef ref = getValueRef(state); 092 TypedValue result = ref.getValue(); 093 this.exitTypeDescriptor = this.children[this.children.length - 1].exitTypeDescriptor; 094 return result; 095 } 096 097 @Override 098 public void setValue(ExpressionState state, @Nullable Object value) throws EvaluationException { 099 getValueRef(state).setValue(value); 100 } 101 102 @Override 103 public boolean isWritable(ExpressionState state) throws EvaluationException { 104 return getValueRef(state).isWritable(); 105 } 106 107 @Override 108 public String toStringAST() { 109 StringJoiner sj = new StringJoiner("."); 110 for (int i = 0; i < getChildCount(); i++) { 111 sj.add(getChild(i).toStringAST()); 112 } 113 return sj.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}