001/* 002 * Copyright 2002-2016 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.context.expression; 018 019import java.lang.reflect.Method; 020import java.util.Arrays; 021 022import org.springframework.core.ParameterNameDiscoverer; 023import org.springframework.expression.spel.support.StandardEvaluationContext; 024import org.springframework.util.ObjectUtils; 025 026/** 027 * A method-based {@link org.springframework.expression.EvaluationContext} that 028 * provides explicit support for method-based invocations. 029 * 030 * <p>Expose the actual method arguments using the following aliases: 031 * <ol> 032 * <li>pX where X is the index of the argument (p0 for the first argument)</li> 033 * <li>aX where X is the index of the argument (a1 for the second argument)</li> 034 * <li>the name of the parameter as discovered by a configurable {@link ParameterNameDiscoverer}</li> 035 * </ol> 036 * 037 * @author Stephane Nicoll 038 * @author Juergen Hoeller 039 * @since 4.2 040 */ 041public class MethodBasedEvaluationContext extends StandardEvaluationContext { 042 043 private final Method method; 044 045 private final Object[] arguments; 046 047 private final ParameterNameDiscoverer parameterNameDiscoverer; 048 049 private boolean argumentsLoaded = false; 050 051 052 public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] arguments, 053 ParameterNameDiscoverer parameterNameDiscoverer) { 054 055 super(rootObject); 056 this.method = method; 057 this.arguments = arguments; 058 this.parameterNameDiscoverer = parameterNameDiscoverer; 059 } 060 061 062 @Override 063 public Object lookupVariable(String name) { 064 Object variable = super.lookupVariable(name); 065 if (variable != null) { 066 return variable; 067 } 068 if (!this.argumentsLoaded) { 069 lazyLoadArguments(); 070 this.argumentsLoaded = true; 071 variable = super.lookupVariable(name); 072 } 073 return variable; 074 } 075 076 /** 077 * Load the param information only when needed. 078 */ 079 protected void lazyLoadArguments() { 080 // Shortcut if no args need to be loaded 081 if (ObjectUtils.isEmpty(this.arguments)) { 082 return; 083 } 084 085 // Expose indexed variables as well as parameter names (if discoverable) 086 String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method); 087 int paramCount = (paramNames != null ? paramNames.length : this.method.getParameterTypes().length); 088 int argsCount = this.arguments.length; 089 090 for (int i = 0; i < paramCount; i++) { 091 Object value = null; 092 if (argsCount > paramCount && i == paramCount - 1) { 093 // Expose remaining arguments as vararg array for last parameter 094 value = Arrays.copyOfRange(this.arguments, i, argsCount); 095 } 096 else if (argsCount > i) { 097 // Actual argument found - otherwise left as null 098 value = this.arguments[i]; 099 } 100 setVariable("a" + i, value); 101 setVariable("p" + i, value); 102 if (paramNames != null) { 103 setVariable(paramNames[i], value); 104 } 105 } 106 } 107 108}