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