Backport selected refinements from the nullability efforts

Issue: SPR-15656
This commit is contained in:
Juergen Hoeller
2017-09-27 00:09:58 +02:00
parent 18a3322d2f
commit 9fdc4404a5
194 changed files with 1236 additions and 1218 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2017 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.
@@ -25,6 +25,14 @@ package org.springframework.expression;
@SuppressWarnings("serial")
public class AccessException extends Exception {
/**
* Create an AccessException with a specific message.
* @param message the message
*/
public AccessException(String message) {
super(message);
}
/**
* Create an AccessException with a specific message and cause.
* @param message the message
@@ -34,12 +42,4 @@ public class AccessException extends Exception {
super(message, cause);
}
/**
* Create an AccessException with a specific message.
* @param message the message
*/
public AccessException(String message) {
super(message);
}
}

View File

@@ -36,13 +36,12 @@ public interface ConstructorExecutor {
/**
* Execute a constructor in the specified context using the specified arguments.
*
* @param context the evaluation context in which the command is being executed
* @param arguments the arguments to the constructor call, should match (in terms of
* number and type) whatever the command will need to run
* @param arguments the arguments to the constructor call, should match (in terms
* of number and type) whatever the command will need to run
* @return the new object
* @throws AccessException if there is a problem executing the command or the
* CommandExecutor is no longer valid
* CommandExecutor is no longer valid
*/
TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2017 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,9 +17,9 @@
package org.springframework.expression;
/**
* By default the mathematical operators {@link Operation} support simple types like
* numbers. By providing an implementation of OperatorOverloader, a user of the expression
* language can support these operations on other types.
* By default the mathematical operators {@link Operation} support simple types
* like numbers. By providing an implementation of OperatorOverloader, a user
* of the expression language can support these operations on other types.
*
* @author Andy Clement
* @since 3.0
@@ -27,21 +27,21 @@ package org.springframework.expression;
public interface OperatorOverloader {
/**
* Return true if the operator overloader supports the specified operation between the
* two operands and so should be invoked to handle it.
* Return true if the operator overloader supports the specified operation
* between the two operands and so should be invoked to handle it.
* @param operation the operation to be performed
* @param leftOperand the left operand
* @param rightOperand the right operand
* @return true if the OperatorOverloader supports the specified operation between the
* two operands
* @return true if the OperatorOverloader supports the specified operation
* between the two operands
* @throws EvaluationException if there is a problem performing the operation
*/
boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand)
throws EvaluationException;
/**
* Execute the specified operation on two operands, returning a result. See
* {@link Operation} for supported operations.
* Execute the specified operation on two operands, returning a result.
* See {@link Operation} for supported operations.
* @param operation the operation to be performed
* @param leftOperand the left operand
* @param rightOperand the right operand

View File

@@ -112,6 +112,7 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
String prefix = context.getExpressionPrefix();
String suffix = context.getExpressionSuffix();
int startIdx = 0;
while (startIdx < expressionString.length()) {
int prefixIndex = expressionString.indexOf(prefix, startIdx);
if (prefixIndex >= startIdx) {
@@ -126,22 +127,18 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
"No ending suffix '" + suffix + "' for expression starting at character " +
prefixIndex + ": " + expressionString.substring(prefixIndex));
}
if (suffixIndex == afterPrefixIndex) {
throw new ParseException(expressionString, prefixIndex,
"No expression defined within delimiter '" + prefix + suffix +
"' at character " + prefixIndex);
}
String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex);
expr = expr.trim();
if (expr.isEmpty()) {
throw new ParseException(expressionString, prefixIndex,
"No expression defined within delimiter '" + prefix + suffix +
"' at character " + prefixIndex);
}
expressions.add(doParseExpression(expr, context));
startIdx = suffixIndex + suffix.length();
}
@@ -151,6 +148,7 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
startIdx = expressionString.length();
}
}
return expressions.toArray(new Expression[expressions.size()]);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -32,6 +32,7 @@ import org.springframework.expression.TypeComparator;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to
@@ -53,6 +54,12 @@ public class ExpressionState {
private final TypedValue rootObject;
private final SpelParserConfiguration configuration;
private Stack<TypedValue> contextObjects;
private Stack<VariableScope> variableScopes;
// When entering a new scope there is a new base object which should be used
// for '#this' references (or to act as a target for unqualified references).
// This stack captures those objects at each nested scope level.
@@ -62,12 +69,6 @@ public class ExpressionState {
// element from list1
private Stack<TypedValue> scopeRootObjects;
private final SpelParserConfiguration configuration;
private Stack<VariableScope> variableScopes;
private Stack<TypedValue> contextObjects;
public ExpressionState(EvaluationContext context) {
this(context, context.getRootObject(), new SpelParserConfiguration(false, false));
@@ -105,7 +106,7 @@ public class ExpressionState {
* The active context object is what unqualified references to properties/etc are resolved against.
*/
public TypedValue getActiveContextObject() {
if (this.contextObjects == null || this.contextObjects.isEmpty()) {
if (CollectionUtils.isEmpty(this.contextObjects)) {
return this.rootObject;
}
return this.contextObjects.peek();
@@ -130,7 +131,7 @@ public class ExpressionState {
}
public TypedValue getScopeRootContextObject() {
if (this.scopeRootObjects == null || this.scopeRootObjects.isEmpty()) {
if (CollectionUtils.isEmpty(this.scopeRootObjects)) {
return this.rootObject;
}
return this.scopeRootObjects.peek();
@@ -142,12 +143,7 @@ public class ExpressionState {
public TypedValue lookupVariable(String name) {
Object value = this.relatedContext.lookupVariable(name);
if (value == null) {
return TypedValue.NULL;
}
else {
return new TypedValue(value);
}
return (value != null ? new TypedValue(value) : TypedValue.NULL);
}
public TypeComparator getTypeComparator() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -18,7 +18,6 @@ package org.springframework.expression.spel;
import org.springframework.core.SpringProperties;
/**
* Configuration object for the SpEL expression parser.
*
@@ -106,35 +105,35 @@ public class SpelParserConfiguration {
/**
* @return the configuration mode for parsers using this configuration object
* Return the configuration mode for parsers using this configuration object.
*/
public SpelCompilerMode getCompilerMode() {
return this.compilerMode;
}
/**
* @return the ClassLoader to use as the basis for expression compilation
* Return the ClassLoader to use as the basis for expression compilation.
*/
public ClassLoader getCompilerClassLoader() {
return this.compilerClassLoader;
}
/**
* @return {@code true} if {@code null} references should be automatically grown
* Return {@code true} if {@code null} references should be automatically grown.
*/
public boolean isAutoGrowNullReferences() {
return this.autoGrowNullReferences;
}
/**
* @return {@code true} if collections should be automatically grown
* Return {@code true} if collections should be automatically grown.
*/
public boolean isAutoGrowCollections() {
return this.autoGrowCollections;
}
/**
* @return the maximum size that a collection can auto grow
* Return the maximum size that a collection can auto grow.
*/
public int getMaximumAutoGrowSize() {
return this.maximumAutoGrowSize;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -37,7 +37,7 @@ public class BeanReference extends SpelNodeImpl {
private final String beanName;
public BeanReference(int pos,String beanName) {
public BeanReference(int pos, String beanName) {
super(pos);
this.beanName = beanName;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -31,7 +31,7 @@ import org.springframework.expression.spel.SpelEvaluationException;
*/
public class CompoundExpression extends SpelNodeImpl {
public CompoundExpression(int pos,SpelNodeImpl... expressionComponents) {
public CompoundExpression(int pos, SpelNodeImpl... expressionComponents) {
super(pos, expressionComponents);
if (expressionComponents.length < 2) {
throw new IllegalStateException("Do not build compound expressions with less than two entries: " +

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -59,7 +59,7 @@ public class FunctionReference extends SpelNodeImpl {
public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments) {
super(pos,arguments);
super(pos, arguments);
this.name = functionName;
}
@@ -107,15 +107,15 @@ public class FunctionReference extends SpelNodeImpl {
SpelMessage.FUNCTION_MUST_BE_STATIC, ClassUtils.getQualifiedMethodName(method), this.name);
}
argumentConversionOccurred = false;
this.argumentConversionOccurred = false;
// Convert arguments if necessary and remap them for varargs if required
if (functionArgs != null) {
TypeConverter converter = state.getEvaluationContext().getTypeConverter();
argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
this.argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
}
if (method.isVarArgs()) {
functionArgs =
ReflectionHelper.setupArgumentsForVarargsInvocation(method.getParameterTypes(), functionArgs);
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(
method.getParameterTypes(), functionArgs);
}
try {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -51,7 +51,7 @@ import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
// TODO support correct syntax for multidimensional [][][] and not [,,,]
public class Indexer extends SpelNodeImpl {
private static enum IndexedType {ARRAY, LIST, MAP, STRING, OBJECT}
private enum IndexedType {ARRAY, LIST, MAP, STRING, OBJECT}
// These fields are used when the indexer is being used as a property read accessor.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -29,7 +29,7 @@ import org.springframework.expression.spel.CodeFlow;
public class NullLiteral extends Literal {
public NullLiteral(int pos) {
super(null,pos);
super(null, pos);
this.exitTypeDescriptor = "Ljava/lang/Object";
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -44,7 +44,7 @@ public class OpAnd extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
if (getBooleanValue(state, getLeftOperand()) == false) {
if (!getBooleanValue(state, getLeftOperand())) {
// no need to evaluate right operand
return BooleanTypedValue.FALSE;
}

View File

@@ -192,8 +192,8 @@ public class OpPlus extends Operator {
private void walk(MethodVisitor mv, CodeFlow cf, SpelNodeImpl operand) {
if (operand instanceof OpPlus) {
OpPlus plus = (OpPlus)operand;
walk(mv,cf,plus.getLeftOperand());
walk(mv,cf,plus.getRightOperand());
walk(mv, cf, plus.getLeftOperand());
walk(mv, cf, plus.getRightOperand());
}
else {
cf.enterCompilationScope();

View File

@@ -87,7 +87,7 @@ public abstract class Operator extends SpelNodeImpl {
protected boolean isCompilableOperatorUsingNumerics() {
SpelNodeImpl left = getLeftOperand();
SpelNodeImpl right= getRightOperand();
SpelNodeImpl right = getRightOperand();
if (!left.isCompilable() || !right.isCompilable()) {
return false;
}
@@ -273,11 +273,11 @@ public abstract class Operator extends SpelNodeImpl {
boolean rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
// If the declared descriptors aren't providing the information, try the actual descriptors
if (!leftNumeric && !ld.equals(leftActualDescriptor)) {
if (!leftNumeric && !ObjectUtils.nullSafeEquals(ld, leftActualDescriptor)) {
ld = leftActualDescriptor;
leftNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(ld);
}
if (!rightNumeric && !rd.equals(rightActualDescriptor)) {
if (!rightNumeric && !ObjectUtils.nullSafeEquals(rd, rightActualDescriptor)) {
rd = rightActualDescriptor;
rightNumeric = CodeFlow.isPrimitiveOrUnboxableSupportedNumberOrBoolean(rd);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -77,6 +77,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
}
@Deprecated
protected SpelNodeImpl getPreviousChild() {
SpelNodeImpl result = null;
if (this.parent != null) {
@@ -214,9 +215,9 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
/**
* Generate code that handles building the argument values for the specified method. This method will take account
* of whether the invoked method is a varargs method and if it is then the argument values will be appropriately
* packaged into an array.
* Generate code that handles building the argument values for the specified method.
* This method will take account of whether the invoked method is a varargs method
* and if it is then the argument values will be appropriately packaged into an array.
* @param mv the method visitor where code should be generated
* @param cf the current codeflow
* @param member the method or constructor for which arguments are being setup
@@ -226,7 +227,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
String[] paramDescriptors = null;
boolean isVarargs = false;
if (member instanceof Constructor) {
Constructor<?> ctor = (Constructor<?>)member;
Constructor<?> ctor = (Constructor<?>) member;
paramDescriptors = CodeFlow.toDescriptors(ctor.getParameterTypes());
isVarargs = ctor.isVarArgs();
}
@@ -246,25 +247,25 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
generateCodeForArgument(mv, cf, arguments[p], paramDescriptors[p]);
}
SpelNodeImpl lastchild = (childCount == 0 ? null : arguments[childCount - 1]);
String arraytype = paramDescriptors[paramDescriptors.length - 1];
SpelNodeImpl lastChild = (childCount == 0 ? null : arguments[childCount - 1]);
String arrayType = paramDescriptors[paramDescriptors.length - 1];
// Determine if the final passed argument is already suitably packaged in array
// form to be passed to the method
if (lastchild != null && lastchild.getExitDescriptor().equals(arraytype)) {
generateCodeForArgument(mv, cf, lastchild, paramDescriptors[p]);
if (lastChild != null && arrayType.equals(lastChild.getExitDescriptor())) {
generateCodeForArgument(mv, cf, lastChild, paramDescriptors[p]);
}
else {
arraytype = arraytype.substring(1); // trim the leading '[', may leave other '['
arrayType = arrayType.substring(1); // trim the leading '[', may leave other '['
// build array big enough to hold remaining arguments
CodeFlow.insertNewArrayCode(mv, childCount - p, arraytype);
CodeFlow.insertNewArrayCode(mv, childCount - p, arrayType);
// Package up the remaining arguments into the array
int arrayindex = 0;
while (p < childCount) {
SpelNodeImpl child = arguments[p];
mv.visitInsn(DUP);
CodeFlow.insertOptimalLoad(mv, arrayindex++);
generateCodeForArgument(mv, cf, child, arraytype);
CodeFlow.insertArrayStore(mv, arraytype);
generateCodeForArgument(mv, cf, child, arrayType);
CodeFlow.insertArrayStore(mv, arrayType);
p++;
}
}
@@ -283,15 +284,16 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
protected static void generateCodeForArgument(MethodVisitor mv, CodeFlow cf, SpelNodeImpl argument, String paramDesc) {
cf.enterCompilationScope();
argument.generateCode(mv, cf);
boolean primitiveOnStack = CodeFlow.isPrimitive(cf.lastDescriptor());
String lastDesc = cf.lastDescriptor();
boolean primitiveOnStack = CodeFlow.isPrimitive(lastDesc);
// Check if need to box it for the method reference?
if (primitiveOnStack && paramDesc.charAt(0) == 'L') {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
CodeFlow.insertBoxIfNecessary(mv, lastDesc.charAt(0));
}
else if (paramDesc.length() == 1 && !primitiveOnStack) {
CodeFlow.insertUnboxInsns(mv, paramDesc.charAt(0), cf.lastDescriptor());
CodeFlow.insertUnboxInsns(mv, paramDesc.charAt(0), lastDesc);
}
else if (!cf.lastDescriptor().equals(paramDesc)) {
else if (!paramDesc.equals(lastDesc)) {
// This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in)
CodeFlow.insertCheckCast(mv, paramDesc);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -88,14 +88,13 @@ public class SpelCompiler implements Opcodes {
/**
* Attempt compilation of the supplied expression. A check is
* made to see if it is compilable before compilation proceeds. The
* check involves visiting all the nodes in the expression Ast and
* ensuring enough state is known about them that bytecode can
* be generated for them.
* Attempt compilation of the supplied expression. A check is made to see
* if it is compilable before compilation proceeds. The check involves
* visiting all the nodes in the expression Ast and ensuring enough state
* is known about them that bytecode can be generated for them.
* @param expression the expression to compile
* @return an instance of the class implementing the compiled expression, or null
* if compilation is not possible
* @return an instance of the class implementing the compiled expression,
* or {@code null} if compilation is not possible
*/
public CompiledExpression compile(SpelNodeImpl expression) {
if (expression.isCompilable()) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -27,6 +27,7 @@ import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MethodInvoker;
/**
@@ -141,8 +142,8 @@ public class ReflectionHelper {
static ArgumentsMatchInfo compareArgumentsVarargs(
List<TypeDescriptor> expectedArgTypes, List<TypeDescriptor> suppliedArgTypes, TypeConverter typeConverter) {
Assert.isTrue(expectedArgTypes != null && expectedArgTypes.size() > 0,
"Expected arguments must at least include one array (the vargargs parameter)");
Assert.isTrue(!CollectionUtils.isEmpty(expectedArgTypes),
"Expected arguments must at least include one array (the varargs parameter)");
Assert.isTrue(expectedArgTypes.get(expectedArgTypes.size() - 1).isArray(),
"Final expected argument should be array type (the varargs parameter)");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2017 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.
@@ -44,6 +44,7 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
private boolean argumentConversionOccurred = false;
public ReflectiveMethodExecutor(Method method) {
this.method = method;
if (method.isVarArgs()) {
@@ -55,6 +56,7 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
}
}
public Method getMethod() {
return this.method;
}
@@ -68,8 +70,8 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
* method (if there is one!). For toString() it may walk as far as Object.
*/
public Class<?> getPublicDeclaringClass() {
if (!computedPublicDeclaringClass) {
this.publicDeclaringClass = discoverPublicClass(method, method.getDeclaringClass());
if (!this.computedPublicDeclaringClass) {
this.publicDeclaringClass = discoverPublicClass(this.method, this.method.getDeclaringClass());
this.computedPublicDeclaringClass = true;
}
return this.publicDeclaringClass;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2017 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.
@@ -93,14 +93,23 @@ public class StandardEvaluationContext implements EvaluationContext {
return this.rootObject;
}
public void addConstructorResolver(ConstructorResolver resolver) {
ensureConstructorResolversInitialized();
this.constructorResolvers.add(this.constructorResolvers.size() - 1, resolver);
public void setPropertyAccessors(List<PropertyAccessor> propertyAccessors) {
this.propertyAccessors = propertyAccessors;
}
public boolean removeConstructorResolver(ConstructorResolver resolver) {
ensureConstructorResolversInitialized();
return this.constructorResolvers.remove(resolver);
@Override
public List<PropertyAccessor> getPropertyAccessors() {
ensurePropertyAccessorsInitialized();
return this.propertyAccessors;
}
public void addPropertyAccessor(PropertyAccessor accessor) {
ensurePropertyAccessorsInitialized();
this.propertyAccessors.add(this.propertyAccessors.size() - 1, accessor);
}
public boolean removePropertyAccessor(PropertyAccessor accessor) {
return this.propertyAccessors.remove(accessor);
}
public void setConstructorResolvers(List<ConstructorResolver> constructorResolvers) {
@@ -113,14 +122,14 @@ public class StandardEvaluationContext implements EvaluationContext {
return this.constructorResolvers;
}
public void addMethodResolver(MethodResolver resolver) {
ensureMethodResolversInitialized();
this.methodResolvers.add(this.methodResolvers.size() - 1, resolver);
public void addConstructorResolver(ConstructorResolver resolver) {
ensureConstructorResolversInitialized();
this.constructorResolvers.add(this.constructorResolvers.size() - 1, resolver);
}
public boolean removeMethodResolver(MethodResolver methodResolver) {
ensureMethodResolversInitialized();
return this.methodResolvers.remove(methodResolver);
public boolean removeConstructorResolver(ConstructorResolver resolver) {
ensureConstructorResolversInitialized();
return this.constructorResolvers.remove(resolver);
}
public void setMethodResolvers(List<MethodResolver> methodResolvers) {
@@ -133,6 +142,16 @@ public class StandardEvaluationContext implements EvaluationContext {
return this.methodResolvers;
}
public void addMethodResolver(MethodResolver resolver) {
ensureMethodResolversInitialized();
this.methodResolvers.add(this.methodResolvers.size() - 1, resolver);
}
public boolean removeMethodResolver(MethodResolver methodResolver) {
ensureMethodResolversInitialized();
return this.methodResolvers.remove(methodResolver);
}
public void setBeanResolver(BeanResolver beanResolver) {
this.beanResolver = beanResolver;
}
@@ -142,25 +161,6 @@ public class StandardEvaluationContext implements EvaluationContext {
return this.beanResolver;
}
public void addPropertyAccessor(PropertyAccessor accessor) {
ensurePropertyAccessorsInitialized();
this.propertyAccessors.add(this.propertyAccessors.size() - 1, accessor);
}
public boolean removePropertyAccessor(PropertyAccessor accessor) {
return this.propertyAccessors.remove(accessor);
}
public void setPropertyAccessors(List<PropertyAccessor> propertyAccessors) {
this.propertyAccessors = propertyAccessors;
}
@Override
public List<PropertyAccessor> getPropertyAccessors() {
ensurePropertyAccessorsInitialized();
return this.propertyAccessors;
}
public void setTypeLocator(TypeLocator typeLocator) {
Assert.notNull(typeLocator, "TypeLocator must not be null");
this.typeLocator = typeLocator;
@@ -169,7 +169,7 @@ public class StandardEvaluationContext implements EvaluationContext {
@Override
public TypeLocator getTypeLocator() {
if (this.typeLocator == null) {
this.typeLocator = new StandardTypeLocator();
this.typeLocator = new StandardTypeLocator();
}
return this.typeLocator;
}
@@ -244,6 +244,7 @@ public class StandardEvaluationContext implements EvaluationContext {
}
}
private void ensurePropertyAccessorsInitialized() {
if (this.propertyAccessors == null) {
initializePropertyAccessors();
@@ -258,6 +259,20 @@ public class StandardEvaluationContext implements EvaluationContext {
}
}
private void ensureConstructorResolversInitialized() {
if (this.constructorResolvers == null) {
initializeConstructorResolvers();
}
}
private synchronized void initializeConstructorResolvers() {
if (this.constructorResolvers == null) {
List<ConstructorResolver> defaultResolvers = new ArrayList<ConstructorResolver>();
defaultResolvers.add(new ReflectiveConstructorResolver());
this.constructorResolvers = defaultResolvers;
}
}
private void ensureMethodResolversInitialized() {
if (this.methodResolvers == null) {
initializeMethodResolvers();
@@ -273,18 +288,4 @@ public class StandardEvaluationContext implements EvaluationContext {
}
}
private void ensureConstructorResolversInitialized() {
if (this.constructorResolvers == null) {
initializeConstructorResolvers();
}
}
private synchronized void initializeConstructorResolvers() {
if (this.constructorResolvers == null) {
List<ConstructorResolver> defaultResolvers = new ArrayList<ConstructorResolver>();
defaultResolvers.add(new ReflectiveConstructorResolver());
this.constructorResolvers = defaultResolvers;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -67,8 +67,9 @@ public class StandardTypeConverter implements TypeConverter {
return this.conversionService.convert(value, sourceType, targetType);
}
catch (ConversionException ex) {
throw new SpelEvaluationException(
ex, SpelMessage.TYPE_CONVERSION_ERROR, sourceType.toString(), targetType.toString());
throw new SpelEvaluationException(ex, SpelMessage.TYPE_CONVERSION_ERROR,
(sourceType != null ? sourceType.toString() : (value != null ? value.getClass().getName() : "null")),
targetType.toString());
}
}