MethodBasedEvaluationContext reliably exposes varargs
Issue: SPR-14554
(cherry picked from commit fae503d)
			
			
This commit is contained in:
		
							parent
							
								
									aade2d1ec9
								
							
						
					
					
						commit
						4543a28e51
					
				| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2002-2015 the original author or authors.
 | 
			
		||||
 * Copyright 2002-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
| 
						 | 
				
			
			@ -17,8 +17,8 @@
 | 
			
		|||
package org.springframework.cache.interceptor;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import org.springframework.context.expression.MethodBasedEvaluationContext;
 | 
			
		||||
import org.springframework.core.ParameterNameDiscoverer;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,25 +38,27 @@ import org.springframework.core.ParameterNameDiscoverer;
 | 
			
		|||
 *
 | 
			
		||||
 * @author Costin Leau
 | 
			
		||||
 * @author Stephane Nicoll
 | 
			
		||||
 * @author Juergen Hoeller
 | 
			
		||||
 * @since 3.1
 | 
			
		||||
 */
 | 
			
		||||
class CacheEvaluationContext extends MethodBasedEvaluationContext {
 | 
			
		||||
 | 
			
		||||
	private final List<String> unavailableVariables;
 | 
			
		||||
	private final Set<String> unavailableVariables = new HashSet<String>(1);
 | 
			
		||||
 | 
			
		||||
	CacheEvaluationContext(Object rootObject, Method method, Object[] args,
 | 
			
		||||
			ParameterNameDiscoverer paramDiscoverer) {
 | 
			
		||||
 | 
			
		||||
		super(rootObject, method, args, paramDiscoverer);
 | 
			
		||||
		this.unavailableVariables = new ArrayList<String>();
 | 
			
		||||
	CacheEvaluationContext(Object rootObject, Method method, Object[] arguments,
 | 
			
		||||
			ParameterNameDiscoverer parameterNameDiscoverer) {
 | 
			
		||||
 | 
			
		||||
		super(rootObject, method, arguments, parameterNameDiscoverer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Add the specified variable name as unavailable for that context. Any expression trying
 | 
			
		||||
	 * to access this variable should lead to an exception.
 | 
			
		||||
	 * <p>This permits the validation of expressions that could potentially a variable even
 | 
			
		||||
	 * when such variable isn't available yet. Any expression trying to use that variable should
 | 
			
		||||
	 * therefore fail to evaluate.
 | 
			
		||||
	 * Add the specified variable name as unavailable for that context.
 | 
			
		||||
	 * Any expression trying to access this variable should lead to an exception.
 | 
			
		||||
	 * <p>This permits the validation of expressions that could potentially a
 | 
			
		||||
	 * variable even when such variable isn't available yet. Any expression
 | 
			
		||||
	 * trying to use that variable should therefore fail to evaluate.
 | 
			
		||||
	 */
 | 
			
		||||
	public void addUnavailableVariable(String name) {
 | 
			
		||||
		this.unavailableVariables.add(name);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
package org.springframework.context.expression;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.ParameterNameDiscoverer;
 | 
			
		||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
 | 
			
		||||
| 
						 | 
				
			
			@ -34,27 +35,27 @@ import org.springframework.util.ObjectUtils;
 | 
			
		|||
 * </ol>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Stephane Nicoll
 | 
			
		||||
 * @author Sergey Podgurskiy
 | 
			
		||||
 * @author Juergen Hoeller
 | 
			
		||||
 * @since 4.2
 | 
			
		||||
 */
 | 
			
		||||
public class MethodBasedEvaluationContext extends StandardEvaluationContext {
 | 
			
		||||
 | 
			
		||||
	private final Method method;
 | 
			
		||||
 | 
			
		||||
	private final Object[] args;
 | 
			
		||||
	private final Object[] arguments;
 | 
			
		||||
 | 
			
		||||
	private final ParameterNameDiscoverer paramDiscoverer;
 | 
			
		||||
	private final ParameterNameDiscoverer parameterNameDiscoverer;
 | 
			
		||||
 | 
			
		||||
	private boolean paramLoaded = false;
 | 
			
		||||
	private boolean argumentsLoaded = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] args,
 | 
			
		||||
			ParameterNameDiscoverer paramDiscoverer) {
 | 
			
		||||
	public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] arguments,
 | 
			
		||||
			ParameterNameDiscoverer parameterNameDiscoverer) {
 | 
			
		||||
 | 
			
		||||
		super(rootObject);
 | 
			
		||||
		this.method = method;
 | 
			
		||||
		this.args = args;
 | 
			
		||||
		this.paramDiscoverer = paramDiscoverer;
 | 
			
		||||
		this.arguments = arguments;
 | 
			
		||||
		this.parameterNameDiscoverer = parameterNameDiscoverer;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -64,9 +65,9 @@ public class MethodBasedEvaluationContext extends StandardEvaluationContext {
 | 
			
		|||
		if (variable != null) {
 | 
			
		||||
			return variable;
 | 
			
		||||
		}
 | 
			
		||||
		if (!this.paramLoaded) {
 | 
			
		||||
		if (!this.argumentsLoaded) {
 | 
			
		||||
			lazyLoadArguments();
 | 
			
		||||
			this.paramLoaded = true;
 | 
			
		||||
			this.argumentsLoaded = true;
 | 
			
		||||
			variable = super.lookupVariable(name);
 | 
			
		||||
		}
 | 
			
		||||
		return variable;
 | 
			
		||||
| 
						 | 
				
			
			@ -76,22 +77,30 @@ public class MethodBasedEvaluationContext extends StandardEvaluationContext {
 | 
			
		|||
	 * Load the param information only when needed.
 | 
			
		||||
	 */
 | 
			
		||||
	protected void lazyLoadArguments() {
 | 
			
		||||
		// shortcut if no args need to be loaded
 | 
			
		||||
		if (ObjectUtils.isEmpty(this.args)) {
 | 
			
		||||
		// Shortcut if no args need to be loaded
 | 
			
		||||
		if (ObjectUtils.isEmpty(this.arguments)) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// save arguments as indexed variables
 | 
			
		||||
		for (int i = 0; i < this.args.length; i++) {
 | 
			
		||||
			setVariable("a" + i, this.args[i]);
 | 
			
		||||
			setVariable("p" + i, this.args[i]);
 | 
			
		||||
		}
 | 
			
		||||
		// Expose indexed variables as well as parameter names (if discoverable)
 | 
			
		||||
		String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method);
 | 
			
		||||
		int paramCount = (paramNames != null ? paramNames.length : this.method.getParameterTypes().length);
 | 
			
		||||
		int argsCount = this.arguments.length;
 | 
			
		||||
 | 
			
		||||
		String[] parameterNames = this.paramDiscoverer.getParameterNames(this.method);
 | 
			
		||||
		// save parameter names (if discovered)
 | 
			
		||||
		if (parameterNames != null) {
 | 
			
		||||
			for (int i = 0; i < this.args.length; i++) {
 | 
			
		||||
				setVariable(parameterNames[i], this.args[i]);
 | 
			
		||||
		for (int i = 0; i < paramCount; i++) {
 | 
			
		||||
			Object value = null;
 | 
			
		||||
			if (argsCount > paramCount && i == paramCount - 1) {
 | 
			
		||||
				// Expose remaining arguments as vararg array for last parameter
 | 
			
		||||
				value = Arrays.copyOfRange(this.arguments, i, argsCount);
 | 
			
		||||
			}
 | 
			
		||||
			else if (argsCount > i) {
 | 
			
		||||
				// Actual argument found - otherwise left as null
 | 
			
		||||
				value = this.arguments[i];
 | 
			
		||||
			}
 | 
			
		||||
			setVariable("a" + i, value);
 | 
			
		||||
			setVariable("p" + i, value);
 | 
			
		||||
			if (paramNames != null) {
 | 
			
		||||
				setVariable(paramNames[i], value);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,17 +30,18 @@ import static org.junit.Assert.*;
 | 
			
		|||
 * Unit tests for {@link MethodBasedEvaluationContext}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Stephane Nicoll
 | 
			
		||||
 * @author Juergen Hoeller
 | 
			
		||||
 * @author Sergey Podgurskiy
 | 
			
		||||
 */
 | 
			
		||||
public class MethodBasedEvaluationContextTests {
 | 
			
		||||
 | 
			
		||||
	private final ParameterNameDiscoverer paramDiscover = new DefaultParameterNameDiscoverer();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void simpleArguments() {
 | 
			
		||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
 | 
			
		||||
				String.class, Boolean.class);
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {"test", true});
 | 
			
		||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", String.class, Boolean.class);
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, "test", true);
 | 
			
		||||
 | 
			
		||||
		assertEquals("test", context.lookupVariable("a0"));
 | 
			
		||||
		assertEquals("test", context.lookupVariable("p0"));
 | 
			
		||||
| 
						 | 
				
			
			@ -51,16 +52,21 @@ public class MethodBasedEvaluationContextTests {
 | 
			
		|||
		assertEquals(true, context.lookupVariable("flag"));
 | 
			
		||||
 | 
			
		||||
		assertNull(context.lookupVariable("a2"));
 | 
			
		||||
		assertNull(context.lookupVariable("p2"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void nullArgument() {
 | 
			
		||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello",
 | 
			
		||||
				String.class, Boolean.class);
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, null});
 | 
			
		||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", String.class, Boolean.class);
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, null, null);
 | 
			
		||||
 | 
			
		||||
		assertNull(context.lookupVariable("a0"));
 | 
			
		||||
		assertNull(context.lookupVariable("p0"));
 | 
			
		||||
		assertNull(context.lookupVariable("foo"));
 | 
			
		||||
 | 
			
		||||
		assertNull(context.lookupVariable("a1"));
 | 
			
		||||
		assertNull(context.lookupVariable("p1"));
 | 
			
		||||
		assertNull(context.lookupVariable("flag"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
| 
						 | 
				
			
			@ -68,39 +74,58 @@ public class MethodBasedEvaluationContextTests {
 | 
			
		|||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null});
 | 
			
		||||
 | 
			
		||||
		assertNull(context.lookupVariable("a0"));
 | 
			
		||||
		assertNull(context.lookupVariable("p0"));
 | 
			
		||||
		assertNull(context.lookupVariable("flag"));
 | 
			
		||||
 | 
			
		||||
		assertNull(context.lookupVariable("a1"));
 | 
			
		||||
		assertNull(context.lookupVariable("p1"));
 | 
			
		||||
		assertNull(context.lookupVariable("vararg"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void varArgNull() {
 | 
			
		||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, null});
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, null, null);
 | 
			
		||||
 | 
			
		||||
		assertNull(context.lookupVariable("a0"));
 | 
			
		||||
		assertNull(context.lookupVariable("p0"));
 | 
			
		||||
		assertNull(context.lookupVariable("flag"));
 | 
			
		||||
 | 
			
		||||
		assertNull(context.lookupVariable("a1"));
 | 
			
		||||
		assertNull(context.lookupVariable("p1"));
 | 
			
		||||
		assertNull(context.lookupVariable("vararg"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void varArgSingle() {
 | 
			
		||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, "hello"});
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, null, "hello");
 | 
			
		||||
 | 
			
		||||
		assertNull(context.lookupVariable("a0"));
 | 
			
		||||
		assertNull(context.lookupVariable("p0"));
 | 
			
		||||
		assertNull(context.lookupVariable("flag"));
 | 
			
		||||
 | 
			
		||||
		assertEquals("hello", context.lookupVariable("a1"));
 | 
			
		||||
		assertEquals("hello", context.lookupVariable("p1"));
 | 
			
		||||
		assertEquals("hello", context.lookupVariable("vararg"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void varArgMultiple() {
 | 
			
		||||
		Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class);
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method,
 | 
			
		||||
				new Object[] {null, new String[]{"hello", "hi"}});
 | 
			
		||||
		MethodBasedEvaluationContext context = createEvaluationContext(method, null, "hello", "hi");
 | 
			
		||||
 | 
			
		||||
		assertNull(context.lookupVariable("a0"));
 | 
			
		||||
		assertNull(context.lookupVariable("p0"));
 | 
			
		||||
		assertArrayEquals(new String[]{"hello", "hi"}, (String[]) context.lookupVariable("p1"));
 | 
			
		||||
		assertNull(context.lookupVariable("flag"));
 | 
			
		||||
 | 
			
		||||
		assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("a1"));
 | 
			
		||||
		assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("p1"));
 | 
			
		||||
		assertArrayEquals(new Object[] {"hello", "hi"}, (Object[]) context.lookupVariable("vararg"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private MethodBasedEvaluationContext createEvaluationContext(Method method, Object[] args) {
 | 
			
		||||
	private MethodBasedEvaluationContext createEvaluationContext(Method method, Object... args) {
 | 
			
		||||
		return new MethodBasedEvaluationContext(this, method, args, this.paramDiscover);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -112,9 +137,7 @@ public class MethodBasedEvaluationContextTests {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		private void hello(Boolean flag, String... vararg){
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue