Consistent use of @Nullable across the codebase (even for internals)

Beyond just formally declaring the current behavior, this revision actually enforces non-null behavior in selected signatures now, not tolerating null values anymore when not explicitly documented. It also changes some utility methods with historic null-in/null-out tolerance towards enforced non-null return values, making them a proper citizen in non-null assignments.

Some issues are left as to-do: in particular a thorough revision of spring-test, and a few tests with unclear failures (ignored as "TODO: NULLABLE") to be sorted out in a follow-up commit.

Issue: SPR-15540
This commit is contained in:
Juergen Hoeller
2017-06-07 14:17:48 +02:00
parent ffc3f6d87d
commit f813712f5b
1493 changed files with 10670 additions and 9172 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.
@@ -18,13 +18,15 @@ package org.springframework.expression;
import java.util.List;
import org.springframework.lang.Nullable;
/**
* Expressions are executed in an evaluation context. It is in this context that
* references are resolved when encountered during expression evaluation.
*
* <p>There is a default implementation of the EvaluationContext,
* {@link org.springframework.expression.spel.support.StandardEvaluationContext} that can
* be extended, rather than having to implement everything.
* <p>There is a default implementation of this EvaluationContext interface:
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}
* which can be extended, rather than having to implement everything manually.
*
* @author Andy Clement
* @author Juergen Hoeller
@@ -79,6 +81,7 @@ public interface EvaluationContext {
/**
* Return a bean resolver that can look up beans by name.
*/
@Nullable
BeanResolver getBeanResolver();
/**
@@ -86,13 +89,14 @@ public interface EvaluationContext {
* @param name variable to set
* @param value value to be placed in the variable
*/
void setVariable(String name, Object value);
void setVariable(String name, @Nullable Object value);
/**
* Look up a named variable within this evaluation context.
* @param name variable to lookup
* @return the value of the variable
* @return the value of the variable, or {@code null} if not found
*/
@Nullable
Object lookupVariable(String name);
}

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,6 +17,7 @@
package org.springframework.expression;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
/**
* An expression capable of evaluating itself against context objects. Encapsulates the
@@ -34,6 +35,7 @@ public interface Expression {
* @return the evaluation result
* @throws EvaluationException if there is a problem during evaluation
*/
@Nullable
Object getValue() throws EvaluationException;
/**
@@ -42,6 +44,7 @@ public interface Expression {
* @return the evaluation result
* @throws EvaluationException if there is a problem during evaluation
*/
@Nullable
Object getValue(Object rootObject) throws EvaluationException;
/**
@@ -51,7 +54,8 @@ public interface Expression {
* @return the evaluation result
* @throws EvaluationException if there is a problem during evaluation
*/
<T> T getValue(Class<T> desiredResultType) throws EvaluationException;
@Nullable
<T> T getValue(@Nullable Class<T> desiredResultType) throws EvaluationException;
/**
* Evaluate the expression in the default context against the specified root object. If the
@@ -62,7 +66,8 @@ public interface Expression {
* @return the evaluation result
* @throws EvaluationException if there is a problem during evaluation
*/
<T> T getValue(Object rootObject, Class<T> desiredResultType) throws EvaluationException;
@Nullable
<T> T getValue(Object rootObject, @Nullable Class<T> desiredResultType) throws EvaluationException;
/**
* Evaluate this expression in the provided context and return the result of evaluation.
@@ -70,6 +75,7 @@ public interface Expression {
* @return the evaluation result
* @throws EvaluationException if there is a problem during evaluation
*/
@Nullable
Object getValue(EvaluationContext context) throws EvaluationException;
/**
@@ -80,6 +86,7 @@ public interface Expression {
* @return the evaluation result
* @throws EvaluationException if there is a problem during evaluation
*/
@Nullable
Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException;
/**
@@ -91,7 +98,8 @@ public interface Expression {
* @return the evaluation result
* @throws EvaluationException if there is a problem during evaluation
*/
<T> T getValue(EvaluationContext context, Class<T> desiredResultType) throws EvaluationException;
@Nullable
<T> T getValue(EvaluationContext context, @Nullable Class<T> desiredResultType) throws EvaluationException;
/**
* Evaluate the expression in a specified context which can resolve references to properties, methods, types, etc -
@@ -104,7 +112,9 @@ public interface Expression {
* @return the evaluation result
* @throws EvaluationException if there is a problem during evaluation
*/
<T> T getValue(EvaluationContext context, Object rootObject, Class<T> desiredResultType) throws EvaluationException;
@Nullable
<T> T getValue(EvaluationContext context, Object rootObject, @Nullable Class<T> desiredResultType)
throws EvaluationException;
/**
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)}
@@ -112,6 +122,7 @@ public interface Expression {
* @return the most general type of value that can be set on this context
* @throws EvaluationException if there is a problem determining the type
*/
@Nullable
Class<?> getValueType() throws EvaluationException;
/**
@@ -121,6 +132,7 @@ public interface Expression {
* @return the most general type of value that can be set on this context
* @throws EvaluationException if there is a problem determining the type
*/
@Nullable
Class<?> getValueType(Object rootObject) throws EvaluationException;
/**
@@ -130,6 +142,7 @@ public interface Expression {
* @return the most general type of value that can be set on this context
* @throws EvaluationException if there is a problem determining the type
*/
@Nullable
Class<?> getValueType(EvaluationContext context) throws EvaluationException;
/**
@@ -140,6 +153,7 @@ public interface Expression {
* @return the most general type of value that can be set on this context
* @throws EvaluationException if there is a problem determining the type
*/
@Nullable
Class<?> getValueType(EvaluationContext context, Object rootObject) throws EvaluationException;
/**
@@ -148,6 +162,7 @@ public interface Expression {
* @return a type descriptor for the most general type of value that can be set on this context
* @throws EvaluationException if there is a problem determining the type
*/
@Nullable
TypeDescriptor getValueTypeDescriptor() throws EvaluationException;
/**
@@ -157,6 +172,7 @@ public interface Expression {
* @return a type descriptor for the most general type of value that can be set on this context
* @throws EvaluationException if there is a problem determining the type
*/
@Nullable
TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException;
/**
@@ -166,16 +182,18 @@ public interface Expression {
* @return a type descriptor for the most general type of value that can be set on this context
* @throws EvaluationException if there is a problem determining the type
*/
@Nullable
TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException;
/**
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
* the given context. The supplied root object overrides any specified in the context.
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)}
* method for the given context. The supplied root object overrides any specified in the context.
* @param context the context in which to evaluate the expression
* @param rootObject the root object against which to evaluate the expression
* @return a type descriptor for the most general type of value that can be set on this context
* @throws EvaluationException if there is a problem determining the type
*/
@Nullable
TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject) throws EvaluationException;
/**

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.
@@ -16,10 +16,12 @@
package org.springframework.expression;
import org.springframework.lang.Nullable;
/**
* 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,28 +29,28 @@ 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)
boolean overridesOperation(Operation operation, @Nullable Object leftOperand, @Nullable 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
* @return the result of performing the operation on the two operands
* @throws EvaluationException if there is a problem performing the operation
*/
Object operate(Operation operation, Object leftOperand, Object rightOperand)
Object operate(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand)
throws EvaluationException;
}

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.
@@ -16,6 +16,8 @@
package org.springframework.expression;
import org.springframework.lang.Nullable;
/**
* Input provided to an expression parser that can influence an expression
* parsing/compilation routine.

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.
@@ -16,7 +16,6 @@
package org.springframework.expression;
import org.springframework.lang.Nullable;
/**
@@ -57,7 +56,7 @@ public interface PropertyAccessor {
* @return true if this resolver is able to read the property
* @throws AccessException if there is any problem determining whether the property can be read
*/
boolean canRead(EvaluationContext context, Object target, String name) throws AccessException;
boolean canRead(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
/**
* Called to read a property from a specified target object.
@@ -68,7 +67,7 @@ public interface PropertyAccessor {
* @return a TypedValue object wrapping the property value read and a type descriptor for it
* @throws AccessException if there is any problem accessing the property value
*/
TypedValue read(EvaluationContext context, Object target, String name) throws AccessException;
TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
/**
* Called to determine if a resolver instance is able to write to a specified
@@ -80,7 +79,7 @@ public interface PropertyAccessor {
* @throws AccessException if there is any problem determining whether the
* property can be written to
*/
boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException;
boolean canWrite(EvaluationContext context, @Nullable Object target, String name) throws AccessException;
/**
* Called to write to a property on a specified target object.
@@ -91,6 +90,7 @@ public interface PropertyAccessor {
* @param newValue the new value for the property
* @throws AccessException if there is any problem writing to the property value
*/
void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException;
void write(EvaluationContext context, @Nullable Object target, String name, @Nullable Object newValue)
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.
@@ -16,6 +16,8 @@
package org.springframework.expression;
import org.springframework.lang.Nullable;
/**
* Instances of a type comparator should be able to compare pairs of objects for equality.
* The specification of the return value is the same as for {@link java.lang.Comparable}.
@@ -32,7 +34,7 @@ public interface TypeComparator {
* @param secondObject the second object
* @return {@code true} if the comparator can compare these objects
*/
boolean canCompare(Object firstObject, Object secondObject);
boolean canCompare(@Nullable Object firstObject, @Nullable Object secondObject);
/**
* Compare two given objects.
@@ -43,6 +45,6 @@ public interface TypeComparator {
* @throws EvaluationException if a problem occurs during comparison
* (or if they are not comparable in the first place)
*/
int compare(Object firstObject, Object secondObject) throws EvaluationException;
int compare(@Nullable Object firstObject, @Nullable Object secondObject) throws EvaluationException;
}

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.
@@ -38,7 +38,7 @@ public interface TypeConverter {
* @param targetType a type descriptor that describes the requested result type
* @return {@code true} if that conversion can be performed
*/
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
/**
* Convert (or coerce) a value from one type to another, for example from a
@@ -55,6 +55,6 @@ public interface TypeConverter {
* @throws EvaluationException if conversion failed or is not possible to begin with
*/
@Nullable
Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType);
Object convertValue(@Nullable Object value, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
}

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.
@@ -66,6 +66,7 @@ public class TypedValue {
return this.value;
}
@Nullable
public TypeDescriptor getTypeDescriptor() {
if (this.typeDescriptor == null && this.value != null) {
this.typeDescriptor = TypeDescriptor.forObject(this.value);

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.
@@ -45,7 +45,10 @@ public abstract class ExpressionUtils {
* of the value to the specified type is not supported
*/
@SuppressWarnings("unchecked")
public static <T> T convertTypedValue(@Nullable EvaluationContext context, TypedValue typedValue, Class<T> targetType) {
@Nullable
public static <T> T convertTypedValue(
@Nullable EvaluationContext context, TypedValue typedValue, @Nullable Class<T> targetType) {
Object value = typedValue.getValue();
if (targetType == null) {
return (T) value;
@@ -64,64 +67,66 @@ public abstract class ExpressionUtils {
* Attempt to convert a typed value to an int using the supplied type converter.
*/
public static int toInt(TypeConverter typeConverter, TypedValue typedValue) {
return (Integer) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
TypeDescriptor.valueOf(Integer.class));
return convertValue(typeConverter, typedValue, Integer.class);
}
/**
* Attempt to convert a typed value to a boolean using the supplied type converter.
*/
public static boolean toBoolean(TypeConverter typeConverter, TypedValue typedValue) {
return (Boolean) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
TypeDescriptor.valueOf(Boolean.class));
return convertValue(typeConverter, typedValue, Boolean.class);
}
/**
* Attempt to convert a typed value to a double using the supplied type converter.
*/
public static double toDouble(TypeConverter typeConverter, TypedValue typedValue) {
return (Double) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
TypeDescriptor.valueOf(Double.class));
return convertValue(typeConverter, typedValue, Double.class);
}
/**
* Attempt to convert a typed value to a long using the supplied type converter.
*/
public static long toLong(TypeConverter typeConverter, TypedValue typedValue) {
return (Long) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
TypeDescriptor.valueOf(Long.class));
return convertValue(typeConverter, typedValue, Long.class);
}
/**
* Attempt to convert a typed value to a char using the supplied type converter.
*/
public static char toChar(TypeConverter typeConverter, TypedValue typedValue) {
return (Character) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
TypeDescriptor.valueOf(Character.class));
return convertValue(typeConverter, typedValue, Character.class);
}
/**
* Attempt to convert a typed value to a short using the supplied type converter.
*/
public static short toShort(TypeConverter typeConverter, TypedValue typedValue) {
return (Short) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
TypeDescriptor.valueOf(Short.class));
return convertValue(typeConverter, typedValue, Short.class);
}
/**
* Attempt to convert a typed value to a float using the supplied type converter.
*/
public static float toFloat(TypeConverter typeConverter, TypedValue typedValue) {
return (Float) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
TypeDescriptor.valueOf(Float.class));
return convertValue(typeConverter, typedValue, Float.class);
}
/**
* Attempt to convert a typed value to a byte using the supplied type converter.
*/
public static byte toByte(TypeConverter typeConverter, TypedValue typedValue) {
return (Byte) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
TypeDescriptor.valueOf(Byte.class));
return convertValue(typeConverter, typedValue, Byte.class);
}
@SuppressWarnings("unchecked")
private static <T> T convertValue(TypeConverter typeConverter, TypedValue typedValue, Class<T> targetType) {
Object result = typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
TypeDescriptor.valueOf(targetType));
if (result == null) {
throw new IllegalStateException("Null conversion result for value [" + typedValue.getValue() + "]");
}
return (T) result;
}
}

View File

@@ -41,17 +41,17 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
* Default ParserContext instance for non-template expressions.
*/
private static final ParserContext NON_TEMPLATE_PARSER_CONTEXT = new ParserContext() {
@Override
public boolean isTemplate() {
return false;
}
@Override
public String getExpressionPrefix() {
return null;
return "";
}
@Override
public String getExpressionSuffix() {
return null;
}
@Override
public boolean isTemplate() {
return false;
return "";
}
};
@@ -63,10 +63,6 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
@Override
public Expression parseExpression(String expressionString, ParserContext context) throws ParseException {
if (context == null) {
context = NON_TEMPLATE_PARSER_CONTEXT;
}
if (context.isTemplate()) {
return parseTemplate(expressionString, context);
}

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.
@@ -163,7 +163,7 @@ public class CodeFlow implements Opcodes {
* @param mv the visitor into which new instructions should be inserted
*/
public void unboxBooleanIfNecessary(MethodVisitor mv) {
if (lastDescriptor().equals("Ljava/lang/Boolean")) {
if ("Ljava/lang/Boolean".equals(lastDescriptor())) {
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
}
}
@@ -524,7 +524,7 @@ public class CodeFlow implements Opcodes {
* @param descriptor type descriptor
* @return {@code true} if the descriptor is for a boolean primitive or boolean reference type
*/
public static boolean isBooleanCompatible(String descriptor) {
public static boolean isBooleanCompatible(@Nullable String descriptor) {
return (descriptor != null && (descriptor.equals("Z") || descriptor.equals("Ljava/lang/Boolean")));
}
@@ -532,7 +532,7 @@ public class CodeFlow implements Opcodes {
* @param descriptor type descriptor
* @return {@code true} if the descriptor is for a primitive type
*/
public static boolean isPrimitive(String descriptor) {
public static boolean isPrimitive(@Nullable String descriptor) {
return (descriptor != null && descriptor.length() == 1);
}
@@ -606,7 +606,7 @@ public class CodeFlow implements Opcodes {
* @param descriptor the descriptor for a type
* @return {@code true} if the descriptor is for a supported numeric type or boolean
*/
public static boolean isPrimitiveOrUnboxableSupportedNumberOrBoolean(String descriptor) {
public static boolean isPrimitiveOrUnboxableSupportedNumberOrBoolean(@Nullable String descriptor) {
if (descriptor == null) {
return false;
}
@@ -623,7 +623,7 @@ public class CodeFlow implements Opcodes {
* @param descriptor the descriptor for a type
* @return {@code true} if the descriptor is for a supported numeric type
*/
public static boolean isPrimitiveOrUnboxableSupportedNumber(String descriptor) {
public static boolean isPrimitiveOrUnboxableSupportedNumber(@Nullable String descriptor) {
if (descriptor == null) {
return false;
}
@@ -716,8 +716,8 @@ public class CodeFlow implements Opcodes {
* @param mv the target visitor for the new instructions
* @param descriptor the descriptor of a type that may or may not need boxing
*/
public static void insertBoxIfNecessary(MethodVisitor mv, String descriptor) {
if (descriptor.length() == 1) {
public static void insertBoxIfNecessary(MethodVisitor mv, @Nullable String descriptor) {
if (descriptor != null && descriptor.length() == 1) {
insertBoxIfNecessary(mv, descriptor.charAt(0));
}
}

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.
@@ -34,6 +34,7 @@ public abstract class CompiledExpression {
* Subclasses of CompiledExpression generated by SpelCompiler will provide an
* implementation of this method.
*/
public abstract Object getValue(Object target, @Nullable EvaluationContext context) throws EvaluationException;
public abstract Object getValue(@Nullable Object target, @Nullable EvaluationContext context)
throws EvaluationException;
}

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.
@@ -137,18 +137,13 @@ public class ExpressionState {
return this.scopeRootObjects.peek();
}
public void setVariable(String name, Object value) {
public void setVariable(String name, @Nullable Object value) {
this.relatedContext.setVariable(name, value);
}
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() {
@@ -160,14 +155,19 @@ public class ExpressionState {
}
public Object convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
return this.relatedContext.getTypeConverter().convertValue(value,
Object result = this.relatedContext.getTypeConverter().convertValue(value,
TypeDescriptor.forObject(value), targetTypeDescriptor);
if (result == null) {
throw new IllegalStateException("Null conversion result for value [" + value + "]");
}
return result;
}
public TypeConverter getTypeConverter() {
return this.relatedContext.getTypeConverter();
}
@Nullable
public Object convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
Object val = value.getValue();
return this.relatedContext.getTypeConverter().convertValue(val, TypeDescriptor.forObject(val), targetTypeDescriptor);
@@ -217,7 +217,7 @@ public class ExpressionState {
return null;
}
public TypedValue operate(Operation op, Object left, @Nullable Object right) throws EvaluationException {
public TypedValue operate(Operation op, @Nullable Object left, @Nullable Object right) throws EvaluationException {
OperatorOverloader overloader = this.relatedContext.getOperatorOverloader();
if (overloader.overridesOperation(op, left, right)) {
Object returnValue = overloader.operate(op, left, right);
@@ -257,7 +257,7 @@ public class ExpressionState {
public VariableScope() {
}
public VariableScope(Map<String, Object> arguments) {
public VariableScope(@Nullable Map<String, Object> arguments) {
if (arguments != null) {
this.vars.putAll(arguments);
}

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.
@@ -34,6 +34,7 @@ public interface SpelNode {
* @param expressionState the current expression state (includes the context)
* @return the value of this node evaluated against the specified state
*/
@Nullable
Object getValue(ExpressionState expressionState) throws EvaluationException;
/**
@@ -62,7 +63,7 @@ public interface SpelNode {
* @throws EvaluationException if any problem occurs evaluating the expression or
* setting the new value
*/
void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException;
void setValue(ExpressionState expressionState, @Nullable Object newValue) throws EvaluationException;
/**
* @return the string form of this AST node
@@ -87,7 +88,7 @@ public interface SpelNode {
* or {@code null} if the object is {@code null}
*/
@Nullable
Class<?> getObjectClass(Object obj);
Class<?> getObjectClass(@Nullable Object obj);
/**
* @return the start position of this Ast node in the expression string

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.
@@ -19,7 +19,6 @@ package org.springframework.expression.spel;
import org.springframework.core.SpringProperties;
import org.springframework.lang.Nullable;
/**
* Configuration object for the SpEL expression parser.
*
@@ -63,7 +62,7 @@ public class SpelParserConfiguration {
* @param compilerMode the compiler mode for the parser
* @param compilerClassLoader the ClassLoader to use as the basis for expression compilation
*/
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, ClassLoader compilerClassLoader) {
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader) {
this(compilerMode, compilerClassLoader, false, false, Integer.MAX_VALUE);
}
@@ -95,7 +94,7 @@ public class SpelParserConfiguration {
* @param autoGrowCollections if collections should automatically grow
* @param maximumAutoGrowSize the maximum size that the collection can auto grow
*/
public SpelParserConfiguration(SpelCompilerMode compilerMode, ClassLoader compilerClassLoader,
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader,
boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {
this.compilerMode = (compilerMode != null ? compilerMode : defaultCompilerMode);

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.
@@ -21,6 +21,7 @@ import java.util.LinkedList;
import java.util.List;
import org.springframework.expression.PropertyAccessor;
import org.springframework.lang.Nullable;
/**
* Utilities methods for use in the Ast classes.
@@ -43,7 +44,7 @@ public abstract class AstUtils {
* @return a list of resolvers that should be tried in order to access the property
*/
public static List<PropertyAccessor> getPropertyAccessorsToTry(
Class<?> targetType, List<PropertyAccessor> propertyAccessors) {
@Nullable Class<?> targetType, List<PropertyAccessor> propertyAccessors) {
List<PropertyAccessor> specificAccessors = new ArrayList<>();
List<PropertyAccessor> generalAccessors = new ArrayList<>();

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.
@@ -39,7 +39,7 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.support.ReflectiveConstructorExecutor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Represents the invocation of a constructor. Either a constructor on a regular type or
@@ -153,6 +153,7 @@ public class ConstructorReference extends SpelNodeImpl {
// Either there was no accessor or it no longer exists
String typeName = (String) this.children[0].getValueInternal(state).getValue();
Assert.state(typeName != null, "No type name");
executorToUse = findExecutorForConstructor(typeName, argumentTypes, state);
try {
this.cachedExecutor = executorToUse;
@@ -179,27 +180,23 @@ public class ConstructorReference extends SpelNodeImpl {
* @return a reusable ConstructorExecutor that can be invoked to run the constructor or null
* @throws SpelEvaluationException if there is a problem locating the constructor
*/
@Nullable
private ConstructorExecutor findExecutorForConstructor(String typeName,
List<TypeDescriptor> argumentTypes, ExpressionState state)
throws SpelEvaluationException {
List<TypeDescriptor> argumentTypes, ExpressionState state) throws SpelEvaluationException {
EvaluationContext evalContext = state.getEvaluationContext();
List<ConstructorResolver> ctorResolvers = evalContext.getConstructorResolvers();
if (ctorResolvers != null) {
for (ConstructorResolver ctorResolver : ctorResolvers) {
try {
ConstructorExecutor ce = ctorResolver.resolve(state.getEvaluationContext(), typeName, argumentTypes);
if (ce != null) {
return ce;
}
}
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName,
FormatHelper.formatMethodForMessage("", argumentTypes));
for (ConstructorResolver ctorResolver : ctorResolvers) {
try {
ConstructorExecutor ce = ctorResolver.resolve(state.getEvaluationContext(), typeName, argumentTypes);
if (ce != null) {
return ce;
}
}
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName,
FormatHelper.formatMethodForMessage("", argumentTypes));
}
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.CONSTRUCTOR_NOT_FOUND, typeName,
FormatHelper.formatMethodForMessage("", argumentTypes));
@@ -233,7 +230,8 @@ public class ConstructorReference extends SpelNodeImpl {
if (!(intendedArrayType instanceof String)) {
throw new SpelEvaluationException(getChild(0).getStartPosition(),
SpelMessage.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION,
FormatHelper.formatClassNameForMessage(intendedArrayType.getClass()));
FormatHelper.formatClassNameForMessage(
intendedArrayType != null ? intendedArrayType.getClass() : null));
}
String type = (String) intendedArrayType;
Class<?> componentType;

View File

@@ -22,6 +22,7 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -78,7 +79,9 @@ public class Elvis extends SpelNodeImpl {
// exit type descriptor can be null if both components are literal expressions
computeExitTypeDescriptor();
this.children[0].generateCode(mv, cf);
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
String lastDesc = cf.lastDescriptor();
Assert.state(lastDesc != null, "No last descriptor");
CodeFlow.insertBoxIfNecessary(mv, lastDesc.charAt(0));
Label elseTarget = new Label();
Label endOfIf = new Label();
mv.visitInsn(DUP);
@@ -93,7 +96,9 @@ public class Elvis extends SpelNodeImpl {
mv.visitInsn(POP);
this.children[1].generateCode(mv, cf);
if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
lastDesc = cf.lastDescriptor();
Assert.state(lastDesc != null, "No last descriptor");
CodeFlow.insertBoxIfNecessary(mv, lastDesc.charAt(0));
}
mv.visitLabel(endOfIf);
cf.pushDescriptor(this.exitTypeDescriptor);

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,7 +67,7 @@ public class FunctionReference extends SpelNodeImpl {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue value = state.lookupVariable(this.name);
if (value == null) {
if (value == TypedValue.NULL) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, this.name);
}
@@ -107,15 +107,12 @@ public class FunctionReference extends SpelNodeImpl {
SpelMessage.FUNCTION_MUST_BE_STATIC, ClassUtils.getQualifiedMethodName(method), this.name);
}
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);
}
TypeConverter converter = state.getEvaluationContext().getTypeConverter();
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-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.
@@ -36,7 +36,7 @@ public class Identifier extends SpelNodeImpl {
@Override
public String toStringAST() {
return (String) this.id.getValue();
return String.valueOf(this.id.getValue());
}
@Override

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.
@@ -37,6 +37,8 @@ import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
@@ -91,7 +93,7 @@ public class Indexer extends SpelNodeImpl {
}
@Override
public void setValue(ExpressionState state, Object newValue) throws EvaluationException {
public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException {
getValueRef(state).setValue(newValue);
}
@@ -106,6 +108,7 @@ public class Indexer extends SpelNodeImpl {
TypedValue context = state.getActiveContextObject();
Object targetObject = context.getValue();
TypeDescriptor targetDescriptor = context.getTypeDescriptor();
Assert.state(targetDescriptor != null, "No type descriptor");
TypedValue indexValue = null;
Object index = null;
@@ -123,6 +126,7 @@ public class Indexer extends SpelNodeImpl {
state.pushActiveContextObject(state.getRootContextObject());
indexValue = this.children[0].getValueInternal(state);
index = indexValue.getValue();
Assert.state(index != null, "No index");
}
finally {
state.popActiveContextObject();
@@ -167,14 +171,14 @@ public class Indexer extends SpelNodeImpl {
// Try and treat the index value as a property of the context object
// TODO could call the conversion service to convert the value to a String
if (String.class == indexValue.getTypeDescriptor().getType()) {
TypeDescriptor valueType = indexValue.getTypeDescriptor();
if (valueType != null && String.class == valueType.getType()) {
this.indexedType = IndexedType.OBJECT;
return new PropertyIndexingValueRef(targetObject, (String) indexValue.getValue(),
state.getEvaluationContext(), targetDescriptor);
return new PropertyIndexingValueRef(targetObject, (String) index, state.getEvaluationContext(), targetDescriptor);
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
targetDescriptor.toString());
throw new SpelEvaluationException(
getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetDescriptor);
}
@Override
@@ -318,7 +322,7 @@ public class Indexer extends SpelNodeImpl {
}
private void setArrayElement(TypeConverter converter, Object ctx, int idx, Object newValue,
private void setArrayElement(TypeConverter converter, Object ctx, int idx, @Nullable Object newValue,
Class<?> arrayComponentType) throws EvaluationException {
if (arrayComponentType == Double.TYPE) {
@@ -435,8 +439,13 @@ public class Indexer extends SpelNodeImpl {
}
@SuppressWarnings("unchecked")
private <T> T convertValue(TypeConverter converter, Object value, Class<T> targetType) {
return (T) converter.convertValue(value, TypeDescriptor.forObject(value), TypeDescriptor.valueOf(targetType));
private <T> T convertValue(TypeConverter converter, @Nullable Object value, Class<T> targetType) {
T result = (T) converter.convertValue(
value, TypeDescriptor.forObject(value), TypeDescriptor.valueOf(targetType));
if (result == null) {
throw new IllegalStateException("Null conversion result for index [" + value + "]");
}
return result;
}
@@ -464,9 +473,10 @@ public class Indexer extends SpelNodeImpl {
}
@Override
public void setValue(Object newValue) {
setArrayElement(this.typeConverter, this.array, this.index, newValue,
this.typeDescriptor.getElementTypeDescriptor().getType());
public void setValue(@Nullable Object newValue) {
TypeDescriptor elementType = this.typeDescriptor.getElementTypeDescriptor();
Assert.state(elementType != null, "No element type");
setArrayElement(this.typeConverter, this.array, this.index, newValue, elementType.getType());
}
@Override
@@ -487,7 +497,9 @@ public class Indexer extends SpelNodeImpl {
private final TypeDescriptor mapEntryDescriptor;
public MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryDescriptor) {
public MapIndexingValueRef(
TypeConverter typeConverter, Map map, @Nullable Object key, TypeDescriptor mapEntryDescriptor) {
this.typeConverter = typeConverter;
this.map = map;
this.key = key;
@@ -502,7 +514,7 @@ public class Indexer extends SpelNodeImpl {
}
@Override
public void setValue(Object newValue) {
public void setValue(@Nullable Object newValue) {
if (this.mapEntryDescriptor.getMapValueTypeDescriptor() != null) {
newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue),
this.mapEntryDescriptor.getMapValueTypeDescriptor());
@@ -527,8 +539,9 @@ public class Indexer extends SpelNodeImpl {
private final TypeDescriptor targetObjectTypeDescriptor;
public PropertyIndexingValueRef(Object targetObject, String value, EvaluationContext evaluationContext,
TypeDescriptor targetObjectTypeDescriptor) {
public PropertyIndexingValueRef(Object targetObject, String value,
EvaluationContext evaluationContext, TypeDescriptor targetObjectTypeDescriptor) {
this.targetObject = targetObject;
this.name = value;
this.evaluationContext = evaluationContext;
@@ -547,25 +560,23 @@ public class Indexer extends SpelNodeImpl {
}
List<PropertyAccessor> accessorsToTry = AstUtils.getPropertyAccessorsToTry(
targetObjectRuntimeClass, this.evaluationContext.getPropertyAccessors());
if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canRead(this.evaluationContext, this.targetObject, this.name)) {
if (accessor instanceof ReflectivePropertyAccessor) {
accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
this.evaluationContext, this.targetObject, this.name);
}
Indexer.this.cachedReadAccessor = accessor;
Indexer.this.cachedReadName = this.name;
Indexer.this.cachedReadTargetType = targetObjectRuntimeClass;
if (accessor instanceof ReflectivePropertyAccessor.OptimalPropertyAccessor) {
ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor =
(ReflectivePropertyAccessor.OptimalPropertyAccessor) accessor;
Member member = optimalAccessor.member;
Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(member instanceof Method ?
((Method) member).getReturnType() : ((Field) member).getType());
}
return accessor.read(this.evaluationContext, this.targetObject, this.name);
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canRead(this.evaluationContext, this.targetObject, this.name)) {
if (accessor instanceof ReflectivePropertyAccessor) {
accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
this.evaluationContext, this.targetObject, this.name);
}
Indexer.this.cachedReadAccessor = accessor;
Indexer.this.cachedReadName = this.name;
Indexer.this.cachedReadTargetType = targetObjectRuntimeClass;
if (accessor instanceof ReflectivePropertyAccessor.OptimalPropertyAccessor) {
ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor =
(ReflectivePropertyAccessor.OptimalPropertyAccessor) accessor;
Member member = optimalAccessor.member;
Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(member instanceof Method ?
((Method) member).getReturnType() : ((Field) member).getType());
}
return accessor.read(this.evaluationContext, this.targetObject, this.name);
}
}
}
@@ -578,7 +589,7 @@ public class Indexer extends SpelNodeImpl {
}
@Override
public void setValue(Object newValue) {
public void setValue(@Nullable Object newValue) {
Class<?> contextObjectClass = getObjectClass(this.targetObject);
try {
if (Indexer.this.cachedWriteName != null && Indexer.this.cachedWriteName.equals(this.name) &&
@@ -590,15 +601,13 @@ public class Indexer extends SpelNodeImpl {
}
List<PropertyAccessor> accessorsToTry =
AstUtils.getPropertyAccessorsToTry(contextObjectClass, this.evaluationContext.getPropertyAccessors());
if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canWrite(this.evaluationContext, this.targetObject, this.name)) {
Indexer.this.cachedWriteName = this.name;
Indexer.this.cachedWriteTargetType = contextObjectClass;
Indexer.this.cachedWriteAccessor = accessor;
accessor.write(this.evaluationContext, this.targetObject, this.name, newValue);
return;
}
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canWrite(this.evaluationContext, this.targetObject, this.name)) {
Indexer.this.cachedWriteName = this.name;
Indexer.this.cachedWriteTargetType = contextObjectClass;
Indexer.this.cachedWriteAccessor = accessor;
accessor.write(this.evaluationContext, this.targetObject, this.name, newValue);
return;
}
}
}
@@ -659,7 +668,7 @@ public class Indexer extends SpelNodeImpl {
}
@Override
public void setValue(Object newValue) {
public void setValue(@Nullable Object newValue) {
growCollectionIfNecessary();
if (this.collection instanceof List) {
List list = (List) this.collection;
@@ -732,7 +741,7 @@ public class Indexer extends SpelNodeImpl {
}
@Override
public void setValue(Object newValue) {
public void setValue(@Nullable Object newValue) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
this.typeDescriptor.toString());
}

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.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelNode;
import org.springframework.lang.Nullable;
/**
* Represent a list in an expression, e.g. '{1,2,3}'
@@ -121,6 +122,7 @@ public class InlineList extends SpelNodeImpl {
}
@SuppressWarnings("unchecked")
@Nullable
public List<Object> getConstantValue() {
return (List<Object>) this.constant.getValue();
}
@@ -174,8 +176,9 @@ public class InlineList extends SpelNodeImpl {
}
else {
children[c].generateCode(mv, codeflow);
if (CodeFlow.isPrimitive(codeflow.lastDescriptor())) {
CodeFlow.insertBoxIfNecessary(mv, codeflow.lastDescriptor().charAt(0));
String lastDesc = codeflow.lastDescriptor();
if (CodeFlow.isPrimitive(lastDesc)) {
CodeFlow.insertBoxIfNecessary(mv, lastDesc.charAt(0));
}
}
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true);

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.
@@ -24,6 +24,7 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelNode;
import org.springframework.lang.Nullable;
/**
* Represent a map in an expression, e.g. '{name:'foo',age:12}'
@@ -155,6 +156,7 @@ public class InlineMap extends SpelNodeImpl {
}
@SuppressWarnings("unchecked")
@Nullable
public Map<Object,Object> getConstantValue() {
return (Map<Object,Object>) this.constant.getValue();
}

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.
@@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.springframework.asm.MethodVisitor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.util.Assert;
/**
* Expression language AST node that represents an integer literal.
@@ -50,7 +51,8 @@ public class IntLiteral extends Literal {
@Override
public void generateCode(MethodVisitor mv, CodeFlow cf) {
int intValue = (Integer) this.value.getValue();
Integer intValue = (Integer) this.value.getValue();
Assert.state(intValue != null, "No int value");
if (intValue == -1) {
// Not sure we can get here because -1 is OpMinus
mv.visitInsn(ICONST_M1);

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.
@@ -22,6 +22,7 @@ import org.springframework.expression.spel.InternalParseException;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelParseException;
import org.springframework.lang.Nullable;
/**
* Common superclass for nodes representing literals (boolean, string, number, etc).
@@ -34,7 +35,7 @@ public abstract class Literal extends SpelNodeImpl {
private final String originalValue;
public Literal(String originalValue, int pos) {
public Literal(@Nullable String originalValue, int pos) {
super(pos);
this.originalValue = originalValue;
}
@@ -51,7 +52,7 @@ public abstract class Literal extends SpelNodeImpl {
@Override
public String toString() {
return getLiteralValue().getValue().toString();
return String.valueOf(getLiteralValue().getValue());
}
@Override

View File

@@ -39,6 +39,8 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.ReflectiveMethodExecutor;
import org.springframework.expression.spel.support.ReflectiveMethodResolver;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Expression language AST node that represents a method reference.
@@ -89,7 +91,7 @@ public class MethodReference extends SpelNodeImpl {
}
private TypedValue getValueInternal(EvaluationContext evaluationContext,
Object value, TypeDescriptor targetType, Object[] arguments) {
@Nullable Object value, @Nullable TypeDescriptor targetType, Object[] arguments) {
List<TypeDescriptor> argumentTypes = getArgumentTypes(arguments);
if (value == null) {
@@ -171,10 +173,10 @@ public class MethodReference extends SpelNodeImpl {
@Nullable
private MethodExecutor getCachedExecutor(EvaluationContext evaluationContext, Object value,
TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
@Nullable TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
List<MethodResolver> methodResolvers = evaluationContext.getMethodResolvers();
if (methodResolvers == null || methodResolvers.size() != 1 ||
if (methodResolvers.size() != 1 ||
!(methodResolvers.get(0) instanceof ReflectiveMethodResolver)) {
// Not a default ReflectiveMethodResolver - don't know whether caching is valid
return null;
@@ -192,20 +194,18 @@ public class MethodReference extends SpelNodeImpl {
Object targetObject, EvaluationContext evaluationContext) throws SpelEvaluationException {
List<MethodResolver> methodResolvers = evaluationContext.getMethodResolvers();
if (methodResolvers != null) {
for (MethodResolver methodResolver : methodResolvers) {
try {
MethodExecutor methodExecutor = methodResolver.resolve(
evaluationContext, targetObject, name, argumentTypes);
if (methodExecutor != null) {
return methodExecutor;
}
}
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex,
SpelMessage.PROBLEM_LOCATING_METHOD, name, targetObject.getClass());
for (MethodResolver methodResolver : methodResolvers) {
try {
MethodExecutor methodExecutor = methodResolver.resolve(
evaluationContext, targetObject, name, argumentTypes);
if (methodExecutor != null) {
return methodExecutor;
}
}
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex,
SpelMessage.PROBLEM_LOCATING_METHOD, name, targetObject.getClass());
}
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_NOT_FOUND,
@@ -311,9 +311,16 @@ public class MethodReference extends SpelNodeImpl {
CodeFlow.insertBoxIfNecessary(mv, descriptor.charAt(0));
}
String classDesc = (Modifier.isPublic(method.getDeclaringClass().getModifiers()) ?
method.getDeclaringClass().getName().replace('.', '/') :
methodExecutor.getPublicDeclaringClass().getName().replace('.', '/'));
String classDesc = null;
if (Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
classDesc = method.getDeclaringClass().getName().replace('.', '/');
}
else {
Class<?> publicDeclaringClass = methodExecutor.getPublicDeclaringClass();
Assert.state(publicDeclaringClass != null, "No public declaring class");
classDesc = publicDeclaringClass.getName().replace('.', '/');
};
if (!isStaticMethod) {
if (descriptor == null || !descriptor.substring(1).equals(classDesc)) {
CodeFlow.insertCheckCast(mv, "L" + classDesc);
@@ -374,17 +381,18 @@ public class MethodReference extends SpelNodeImpl {
private final List<TypeDescriptor> argumentTypes;
public CachedMethodExecutor(MethodExecutor methodExecutor, Class<?> staticClass,
TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
public CachedMethodExecutor(MethodExecutor methodExecutor, @Nullable Class<?> staticClass,
@Nullable TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
this.methodExecutor = methodExecutor;
this.staticClass = staticClass;
this.target = target;
this.argumentTypes = argumentTypes;
}
public boolean isSuitable(Object value, TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
public boolean isSuitable(Object value, @Nullable TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
return ((this.staticClass == null || this.staticClass == value) &&
this.target.equals(target) && this.argumentTypes.equals(argumentTypes));
ObjectUtils.nullSafeEquals(this.target, target) && this.argumentTypes.equals(argumentTypes));
}
public MethodExecutor get() {

View File

@@ -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.
@@ -25,6 +25,7 @@ import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.lang.Nullable;
/**
* Represents the boolean AND operation.
@@ -44,7 +45,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;
}
@@ -63,7 +64,7 @@ public class OpAnd extends Operator {
}
}
private void assertValueNotNull(Boolean value) {
private void assertValueNotNull(@Nullable Boolean value) {
if (value == null) {
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
}

View File

@@ -138,7 +138,7 @@ public class OpDec extends Operator {
@Override
public SpelNodeImpl getRightOperand() {
return null;
throw new IllegalStateException("No right operand");
}
}

View File

@@ -133,7 +133,7 @@ public class OpInc extends Operator {
@Override
public SpelNodeImpl getRightOperand() {
return null;
throw new IllegalStateException("No right operand");
}
}

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.
@@ -54,9 +54,8 @@ public class OpMinus extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
if (rightOp == null) { // if only one operand, then this is unary minus
if (this.children.length < 2) { // if only one operand, then this is unary minus
Object operand = leftOp.getValueInternal(state).getValue();
if (operand instanceof Number) {
if (operand instanceof BigDecimal) {
@@ -96,7 +95,7 @@ public class OpMinus extends Operator {
}
Object left = leftOp.getValueInternal(state).getValue();
Object right = rightOp.getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
@@ -146,7 +145,7 @@ public class OpMinus extends Operator {
@Override
public String toStringAST() {
if (getRightOperand() == null) { // unary minus
if (this.children.length < 2) { // unary minus
return "-" + getLeftOperand().toStringAST();
}
return super.toStringAST();
@@ -155,7 +154,7 @@ public class OpMinus extends Operator {
@Override
public SpelNodeImpl getRightOperand() {
if (this.children.length < 2) {
return null;
throw new IllegalStateException("No right operand");
}
return this.children[1];
}

View File

@@ -23,6 +23,7 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.util.Assert;
/**
* Implements the not-equal operator.
@@ -40,12 +41,11 @@ public class OpNE extends Operator {
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
return BooleanTypedValue.forValue(
!equalityCheck(state.getEvaluationContext(), left, right));
Object leftValue = getLeftOperand().getValueInternal(state).getValue();
Object rightValue = getRightOperand().getValueInternal(state).getValue();
this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(leftValue);
this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(rightValue);
return BooleanTypedValue.forValue(!equalityCheck(state.getEvaluationContext(), leftValue, rightValue));
}
// This check is different to the one in the other numeric operators (OpLt/etc)

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.
@@ -24,6 +24,8 @@ import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.BooleanTypedValue;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Represents the boolean OR operation.
@@ -62,7 +64,7 @@ public class OpOr extends Operator {
}
}
private void assertValueNotNull(Boolean value) {
private void assertValueNotNull(@Nullable Boolean value) {
if (value == null) {
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
}

View File

@@ -27,6 +27,7 @@ import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
@@ -58,9 +59,8 @@ public class OpPlus extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
if (rightOp == null) { // if only one operand, then this is unary plus
if (this.children.length < 2) { // if only one operand, then this is unary plus
Object operandOne = leftOp.getValueInternal(state).getValue();
if (operandOne instanceof Number) {
if (operandOne instanceof Double) {
@@ -82,7 +82,7 @@ public class OpPlus extends Operator {
TypedValue operandOneValue = leftOp.getValueInternal(state);
Object leftOperand = operandOneValue.getValue();
TypedValue operandTwoValue = rightOp.getValueInternal(state);
TypedValue operandTwoValue = getRightOperand().getValueInternal(state);
Object rightOperand = operandTwoValue.getValue();
if (leftOperand instanceof Number && rightOperand instanceof Number) {
@@ -150,7 +150,7 @@ public class OpPlus extends Operator {
@Override
public SpelNodeImpl getRightOperand() {
if (this.children.length < 2) {
return null;
throw new IllegalStateException("No right operand");
}
return this.children[1];
}
@@ -189,13 +189,13 @@ public class OpPlus extends Operator {
* Walk through a possible tree of nodes that combine strings and append
* them all to the same (on stack) StringBuilder.
*/
private void walk(MethodVisitor mv, CodeFlow cf, SpelNodeImpl operand) {
private void walk(MethodVisitor mv, CodeFlow cf, @Nullable 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 {
else if (operand != null) {
cf.enterCompilationScope();
operand.generateCode(mv,cf);
if (!"Ljava/lang/String".equals(cf.lastDescriptor())) {
@@ -212,18 +212,18 @@ public class OpPlus extends Operator {
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
walk(mv,cf,getLeftOperand());
walk(mv,cf,getRightOperand());
walk(mv, cf, getLeftOperand());
walk(mv, cf, getRightOperand());
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
}
else {
getLeftOperand().generateCode(mv, cf);
String leftDesc = getLeftOperand().exitTypeDescriptor;
this.children[0].generateCode(mv, cf);
String leftDesc = this.children[0].exitTypeDescriptor;
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, leftDesc, this.exitTypeDescriptor.charAt(0));
if (this.children.length > 1) {
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
String rightDesc = getRightOperand().exitTypeDescriptor;
this.children[1].generateCode(mv, cf);
String rightDesc = this.children[1].exitTypeDescriptor;
cf.exitCompilationScope();
CodeFlow.insertNumericUnboxOrPrimitiveTypeCoercion(mv, rightDesc, this.exitTypeDescriptor.charAt(0));
switch (this.exitTypeDescriptor.charAt(0)) {

View File

@@ -24,6 +24,7 @@ import org.springframework.asm.MethodVisitor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.NumberUtils;
import org.springframework.util.ObjectUtils;
@@ -62,7 +63,6 @@ public abstract class Operator extends SpelNodeImpl {
return this.children[0];
}
@Nullable
public SpelNodeImpl getRightOperand() {
return this.children[1];
}
@@ -89,7 +89,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;
}
@@ -107,8 +107,10 @@ public abstract class Operator extends SpelNodeImpl {
* two comparison instructions.
*/
protected void generateComparisonCode(MethodVisitor mv, CodeFlow cf, int compInstruction1, int compInstruction2) {
String leftDesc = getLeftOperand().exitTypeDescriptor;
String rightDesc = getRightOperand().exitTypeDescriptor;
SpelNodeImpl left = getLeftOperand();
SpelNodeImpl right = getRightOperand();
String leftDesc = left.exitTypeDescriptor;
String rightDesc = right.exitTypeDescriptor;
boolean unboxLeft = !CodeFlow.isPrimitive(leftDesc);
boolean unboxRight = !CodeFlow.isPrimitive(rightDesc);
@@ -117,14 +119,14 @@ public abstract class Operator extends SpelNodeImpl {
char targetType = dc.compatibleType; // CodeFlow.toPrimitiveTargetDesc(leftDesc);
cf.enterCompilationScope();
getLeftOperand().generateCode(mv, cf);
left.generateCode(mv, cf);
cf.exitCompilationScope();
if (unboxLeft) {
CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
}
cf.enterCompilationScope();
getRightOperand().generateCode(mv, cf);
right.generateCode(mv, cf);
cf.exitCompilationScope();
if (unboxRight) {
CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
@@ -171,7 +173,7 @@ public abstract class Operator extends SpelNodeImpl {
* @param left the left-hand operand value
* @param right the right-hand operand value
*/
public static boolean equalityCheck(EvaluationContext context, Object left, Object right) {
public static boolean equalityCheck(EvaluationContext context, @Nullable Object left, @Nullable Object right) {
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
@@ -179,7 +181,7 @@ public abstract class Operator extends SpelNodeImpl {
if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) {
BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class);
BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class);
return (leftBigDecimal == null ? rightBigDecimal == null : leftBigDecimal.compareTo(rightBigDecimal) == 0);
return (leftBigDecimal.compareTo(rightBigDecimal) == 0);
}
else if (leftNumber instanceof Double || rightNumber instanceof Double) {
return (leftNumber.doubleValue() == rightNumber.doubleValue());
@@ -190,7 +192,7 @@ public abstract class Operator extends SpelNodeImpl {
else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) {
BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class);
BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class);
return (leftBigInteger == null ? rightBigInteger == null : leftBigInteger.compareTo(rightBigInteger) == 0);
return (leftBigInteger.compareTo(rightBigInteger) == 0);
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return (leftNumber.longValue() == rightNumber.longValue());

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.
@@ -60,12 +60,12 @@ public class OperatorMatches extends Operator {
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
Object left = leftOp.getValue(state, String.class);
Object right = getRightOperand().getValueInternal(state).getValue();
String left = leftOp.getValue(state, String.class);
Object right = getRightOperand().getValue(state);
if (!(left instanceof String)) {
if (left == null) {
throw new SpelEvaluationException(leftOp.getStartPosition(),
SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, left);
SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, (Object) null);
}
if (!(right instanceof String)) {
throw new SpelEvaluationException(rightOp.getStartPosition(),
@@ -73,14 +73,13 @@ public class OperatorMatches extends Operator {
}
try {
String leftString = (String) left;
String rightString = (String) right;
Pattern pattern = this.patternCache.get(rightString);
if (pattern == null) {
pattern = Pattern.compile(rightString);
this.patternCache.putIfAbsent(rightString, pattern);
}
Matcher matcher = pattern.matcher(leftString);
Matcher matcher = pattern.matcher(left);
return BooleanTypedValue.forValue(matcher.matches());
}
catch (PatternSyntaxException ex) {

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.TypedValue;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -138,7 +139,7 @@ public class Projection extends SpelNodeImpl {
return "![" + getChild(0).toStringAST() + "]";
}
private Class<?> determineCommonType(Class<?> oldType, Class<?> newType) {
private Class<?> determineCommonType(@Nullable Class<?> oldType, Class<?> newType) {
if (oldType == null) {
return newType;
}

View File

@@ -35,6 +35,8 @@ import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
@@ -99,6 +101,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
if (result.getValue() == null && isAutoGrowNullReferences &&
nextChildIs(Indexer.class, PropertyOrFieldReference.class)) {
TypeDescriptor resultDescriptor = result.getTypeDescriptor();
Assert.state(resultDescriptor != null, "No result type");
// Create a new collection or map ready for the indexer
if (List.class == resultDescriptor.getType()) {
if (isWritableProperty(this.name, contextObject, evalContext)) {
@@ -138,7 +141,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
@Override
public void setValue(ExpressionState state, Object newValue) throws EvaluationException {
public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException {
writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, newValue);
}
@@ -182,23 +185,22 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
// get the accessor and use it. If they are not cacheable but report they can read the property
// then ask them to read it
if (accessorsToTry != null) {
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canRead(evalContext, contextObject.getValue(), name)) {
if (accessor instanceof ReflectivePropertyAccessor) {
accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
evalContext, contextObject.getValue(), name);
}
this.cachedReadAccessor = accessor;
return accessor.read(evalContext, contextObject.getValue(), name);
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canRead(evalContext, contextObject.getValue(), name)) {
if (accessor instanceof ReflectivePropertyAccessor) {
accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
evalContext, contextObject.getValue(), name);
}
this.cachedReadAccessor = accessor;
return accessor.read(evalContext, contextObject.getValue(), name);
}
}
catch (Exception ex) {
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_DURING_PROPERTY_READ, name, ex.getMessage());
}
}
catch (Exception ex) {
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_DURING_PROPERTY_READ, name, ex.getMessage());
}
if (contextObject.getValue() == null) {
throw new SpelEvaluationException(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL, name);
}
@@ -208,12 +210,16 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
}
private void writeProperty(TypedValue contextObject, EvaluationContext evalContext, String name, Object newValue)
private void writeProperty(
TypedValue contextObject, EvaluationContext evalContext, String name, @Nullable Object newValue)
throws EvaluationException {
if (contextObject.getValue() == null && this.nullSafe) {
return;
}
if (contextObject.getValue() == null) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL, name);
}
PropertyAccessor accessorToUse = this.cachedWriteAccessor;
if (accessorToUse != null) {
@@ -230,39 +236,34 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
List<PropertyAccessor> accessorsToTry =
getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors());
if (accessorsToTry != null) {
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canWrite(evalContext, contextObject.getValue(), name)) {
this.cachedWriteAccessor = accessor;
accessor.write(evalContext, contextObject.getValue(), name, newValue);
return;
}
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canWrite(evalContext, contextObject.getValue(), name)) {
this.cachedWriteAccessor = accessor;
accessor.write(evalContext, contextObject.getValue(), name, newValue);
return;
}
}
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE,
name, ex.getMessage());
}
}
if (contextObject.getValue() == null) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL, name);
}
else {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE, name,
FormatHelper.formatClassNameForMessage(getObjectClass(contextObject.getValue())));
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE,
name, ex.getMessage());
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE, name,
FormatHelper.formatClassNameForMessage(getObjectClass(contextObject.getValue())));
}
public boolean isWritableProperty(String name, TypedValue contextObject, EvaluationContext evalContext)
throws EvaluationException {
List<PropertyAccessor> accessorsToTry =
getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors());
if (accessorsToTry != null) {
Object value = contextObject.getValue();
if (value != null) {
List<PropertyAccessor> accessorsToTry =
getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors());
for (PropertyAccessor accessor : accessorsToTry) {
try {
if (accessor.canWrite(evalContext, contextObject.getValue(), name)) {
if (accessor.canWrite(evalContext, value, name)) {
return true;
}
}
@@ -286,7 +287,9 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
* @param contextObject the object upon which property access is being attempted
* @return a list of resolvers that should be tried in order to access the property
*/
private List<PropertyAccessor> getPropertyAccessorsToTry(Object contextObject, List<PropertyAccessor> propertyAccessors) {
private List<PropertyAccessor> getPropertyAccessorsToTry(
@Nullable Object contextObject, List<PropertyAccessor> propertyAccessors) {
Class<?> targetType = (contextObject != null ? contextObject.getClass() : null);
List<PropertyAccessor> specificAccessors = new ArrayList<>();
@@ -346,6 +349,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
public AccessorLValue(PropertyOrFieldReference propertyOrFieldReference, TypedValue activeContextObject,
EvaluationContext evalContext, boolean autoGrowNullReferences) {
this.ref = propertyOrFieldReference;
this.contextObject = activeContextObject;
this.evalContext = evalContext;
@@ -365,7 +369,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
}
@Override
public void setValue(Object newValue) {
public void setValue(@Nullable Object newValue) {
this.ref.writeProperty(this.contextObject, this.evalContext, this.ref.name, newValue);
}

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.
@@ -47,7 +47,7 @@ public class QualifiedIdentifier extends SpelNodeImpl {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < getChildCount(); i++) {
Object value = this.children[i].getValueInternal(state).getValue();
if (i > 0 && !value.toString().startsWith("$")) {
if (i > 0 && (value == null || !value.toString().startsWith("$"))) {
sb.append(".");
}
sb.append(value);

View File

@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
@@ -167,18 +168,28 @@ public class Selection extends SpelNodeImpl {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this);
}
Class<?> elementType = ClassUtils.resolvePrimitiveIfNecessary(
op.getTypeDescriptor().getElementTypeDescriptor().getType());
Class<?> elementType = null;
TypeDescriptor typeDesc = op.getTypeDescriptor();
if (typeDesc != null) {
TypeDescriptor elementTypeDesc = typeDesc.getElementTypeDescriptor();
if (elementTypeDesc != null) {
elementType = ClassUtils.resolvePrimitiveIfNecessary(elementTypeDesc.getType());
}
}
Assert.state(elementType != null, "Unresolvable element type");
Object resultArray = Array.newInstance(elementType, result.size());
System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultArray), this);
}
if (operand == null) {
if (this.nullSafe) {
return ValueRef.NullValueRef.INSTANCE;
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION, "null");
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION,
operand.getClass().getName());
}

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.
@@ -30,7 +30,7 @@ import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -77,19 +77,6 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
}
protected SpelNodeImpl getPreviousChild() {
SpelNodeImpl result = null;
if (this.parent != null) {
for (SpelNodeImpl child : this.parent.children) {
if (this == child) {
break;
}
result = child;
}
}
return result;
}
/**
* @return true if the next child is one of the specified classes
*/
@@ -116,24 +103,12 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
@Override
public final Object getValue(ExpressionState expressionState) throws EvaluationException {
if (expressionState != null) {
return getValueInternal(expressionState).getValue();
}
else {
// configuration not set - does that matter?
return getValue(new ExpressionState(new StandardEvaluationContext()));
}
return getValueInternal(expressionState).getValue();
}
@Override
public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException {
if (expressionState != null) {
return getValueInternal(expressionState);
}
else {
// configuration not set - does that matter?
return getTypedValue(new ExpressionState(new StandardEvaluationContext()));
}
return getValueInternal(expressionState);
}
// by default Ast nodes are not writable
@@ -143,7 +118,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
}
@Override
public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException {
public void setValue(ExpressionState expressionState, @Nullable Object newValue) throws EvaluationException {
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
}
@@ -159,13 +134,14 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
}
@Override
public Class<?> getObjectClass(Object obj) {
public Class<?> getObjectClass(@Nullable Object obj) {
if (obj == null) {
return null;
}
return (obj instanceof Class ? ((Class<?>) obj) : obj.getClass());
}
@Nullable
protected final <T> T getValue(ExpressionState state, Class<T> desiredReturnType) throws EvaluationException {
return ExpressionUtils.convertTypedValue(state.getEvaluationContext(), getValueInternal(state), desiredReturnType);
}
@@ -283,15 +259,17 @@ 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();
Assert.state(lastDesc != null, "No last descriptor");
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

@@ -24,6 +24,7 @@ import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.Assert;
/**
* Represents a ternary expression, for example: "someCheck()?true:false".
@@ -94,8 +95,10 @@ public class Ternary extends SpelNodeImpl {
computeExitTypeDescriptor();
cf.enterCompilationScope();
this.children[0].generateCode(mv, cf);
if (!CodeFlow.isPrimitive(cf.lastDescriptor())) {
CodeFlow.insertUnboxInsns(mv, 'Z', cf.lastDescriptor());
String lastDesc = cf.lastDescriptor();
Assert.state(lastDesc != null, "No last descriptor");
if (!CodeFlow.isPrimitive(lastDesc)) {
CodeFlow.insertUnboxInsns(mv, 'Z', lastDesc);
}
cf.exitCompilationScope();
Label elseTarget = new Label();
@@ -104,7 +107,9 @@ public class Ternary extends SpelNodeImpl {
cf.enterCompilationScope();
this.children[1].generateCode(mv, cf);
if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
lastDesc = cf.lastDescriptor();
Assert.state(lastDesc != null, "No last descriptor");
CodeFlow.insertBoxIfNecessary(mv, lastDesc.charAt(0));
}
cf.exitCompilationScope();
mv.visitJumpInsn(GOTO, endOfIf);
@@ -112,7 +117,9 @@ public class Ternary extends SpelNodeImpl {
cf.enterCompilationScope();
this.children[2].generateCode(mv, cf);
if (!CodeFlow.isPrimitive(this.exitTypeDescriptor)) {
CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor().charAt(0));
lastDesc = cf.lastDescriptor();
Assert.state(lastDesc != null, "No last descriptor");
CodeFlow.insertBoxIfNecessary(mv, lastDesc.charAt(0));
}
cf.exitCompilationScope();
mv.visitLabel(endOfIf);

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.
@@ -24,6 +24,7 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.util.Assert;
/**
* Represents a reference to a type, for example "T(String)" or "T(com.somewhere.Foo)"
@@ -51,6 +52,7 @@ public class TypeReference extends SpelNodeImpl {
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
// TODO possible optimization here if we cache the discovered type reference, but can we do that?
String typeName = (String) this.children[0].getValueInternal(state).getValue();
Assert.state(typeName != null, "No type name");
if (!typeName.contains(".") && Character.isLowerCase(typeName.charAt(0))) {
TypeCode tc = TypeCode.valueOf(typeName.toUpperCase());
if (tc != TypeCode.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.
@@ -19,6 +19,7 @@ package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.lang.Nullable;
/**
* Represents a reference to a value. With a reference it is possible to get or set the
@@ -45,7 +46,7 @@ public interface ValueRef {
* re-evaluation.
* @param newValue the new value
*/
void setValue(Object newValue);
void setValue(@Nullable Object newValue);
/**
* Indicates whether calling setValue(Object) is supported.

View File

@@ -24,6 +24,7 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.lang.Nullable;
/**
* Represents a variable reference, eg. #someVar. Note this is different to a *local*
@@ -89,7 +90,7 @@ public class VariableReference extends SpelNodeImpl {
}
@Override
public void setValue(ExpressionState state, Object value) throws SpelEvaluationException {
public void setValue(ExpressionState state, @Nullable Object value) throws SpelEvaluationException {
state.setVariable(this.name, value);
}
@@ -127,7 +128,7 @@ public class VariableReference extends SpelNodeImpl {
}
@Override
public void setValue(Object newValue) {
public void setValue(@Nullable Object newValue) {
this.evaluationContext.setVariable(this.name, newValue);
}

View File

@@ -156,7 +156,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
SpelNodeImpl assignedValue = eatLogicalOrExpression();
return new Assign(toPos(t), expr, assignedValue);
}
if (t.kind == TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b)
if (expr == null) {
expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 2));
@@ -168,7 +167,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
return new Elvis(toPos(t), expr, valueIfNull);
}
if (t.kind == TokenKind.QMARK) { // a?b:c
if (expr == null) {
expr = new NullLiteral(toPos(t.startPos - 1, t.endPos - 1));
@@ -256,9 +254,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private SpelNodeImpl eatSumExpression() {
SpelNodeImpl expr = eatProductExpression();
while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) {
Token t = nextToken();//consume PLUS or MINUS or INC
Token t = nextToken(); //consume PLUS or MINUS or INC
SpelNodeImpl rhExpr = eatProductExpression();
checkRightOperand(t,rhExpr);
checkRightOperand(t, rhExpr);
if (t.kind == TokenKind.PLUS) {
expr = new OpPlus(toPos(t), expr, rhExpr);
}
@@ -296,10 +294,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
if (peekToken(TokenKind.POWER)) {
Token t = nextToken(); //consume POWER
SpelNodeImpl rhExpr = eatUnaryExpression();
checkRightOperand(t,rhExpr);
checkRightOperand(t, rhExpr);
return new OperatorPower(toPos(t), expr, rhExpr);
}
if (expr != null && peekToken(TokenKind.INC, TokenKind.DEC)) {
Token t = nextToken(); //consume INC/DEC
if (t.getKind() == TokenKind.INC) {
@@ -307,7 +304,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
return new OpDec(toPos(t), true, expr);
}
return expr;
}
@@ -481,7 +477,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
}
private int positionOf(Token t) {
private int positionOf(@Nullable Token t) {
if (t == null) {
// if null assume the problem is because the right token was
// not found at the end of the expression
@@ -743,7 +739,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
qualifiedIdPieces.toArray(new SpelNodeImpl[qualifiedIdPieces.size()]));
}
private boolean isValidQualifiedId(Token node) {
private boolean isValidQualifiedId(@Nullable Token node) {
if (node == null || node.kind == TokenKind.LITERAL_STRING) {
return false;
}
@@ -1026,13 +1022,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
checkRightOperand(token, right);
}
private void checkLeftOperand(Token token, SpelNodeImpl operandExpression) {
private void checkLeftOperand(Token token, @Nullable SpelNodeImpl operandExpression) {
if (operandExpression == null) {
raiseInternalException(token.startPos, SpelMessage.LEFT_OPERAND_PROBLEM);
}
}
private void checkRightOperand(Token token, SpelNodeImpl operandExpression) {
private void checkRightOperand(Token token, @Nullable SpelNodeImpl operandExpression) {
if (operandExpression == null) {
raiseInternalException(token.startPos, SpelMessage.RIGHT_OPERAND_PROBLEM);
}
@@ -1043,7 +1039,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
return (t.startPos<<16) + t.endPos;
}
private int toPos(int start,int end) {
private int toPos(int start, int end) {
return (start<<16) + end;
}

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.
@@ -75,8 +75,7 @@ public class SpelCompiler implements Opcodes {
// A compiler is created for each classloader, it manages a child class loader of that
// classloader and the child is used to load the compiled expressions.
private static final Map<ClassLoader, SpelCompiler> compilers =
new ConcurrentReferenceHashMap<>();
private static final Map<ClassLoader, SpelCompiler> compilers = new ConcurrentReferenceHashMap<>();
// The child ClassLoader used to load the compiled expression classes
private ChildClassLoader ccl;
@@ -84,10 +83,12 @@ public class SpelCompiler implements Opcodes {
// Counter suffix for generated classes within this SpelCompiler instance
private final AtomicInteger suffixId = new AtomicInteger(1);
private SpelCompiler(ClassLoader classloader) {
private SpelCompiler(@Nullable ClassLoader classloader) {
this.ccl = new ChildClassLoader(classloader);
}
/**
* Attempt compilation of the supplied expression. A check is
* made to see if it is compilable before compilation proceeds. The
@@ -212,7 +213,7 @@ public class SpelCompiler implements Opcodes {
* @param classLoader the ClassLoader to use as the basis for compilation
* @return a corresponding SpelCompiler instance
*/
public static SpelCompiler getCompiler(ClassLoader classLoader) {
public static SpelCompiler getCompiler(@Nullable ClassLoader classLoader) {
ClassLoader clToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
synchronized (compilers) {
SpelCompiler compiler = compilers.get(clToUse);
@@ -289,7 +290,7 @@ public class SpelCompiler implements Opcodes {
private int classesDefinedCount = 0;
public ChildClassLoader(ClassLoader classLoader) {
public ChildClassLoader(@Nullable ClassLoader classLoader) {
super(NO_URLS, classLoader);
}

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.
@@ -31,6 +31,7 @@ import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.ast.SpelNodeImpl;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -109,8 +110,10 @@ public class SpelExpression implements Expression {
Object result;
if (this.compiledAst != null) {
try {
TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject();
return this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext);
TypedValue contextRoot =
(this.evaluationContext != null ? this.evaluationContext.getRootObject() : null);
return this.compiledAst.getValue(
(contextRoot != null ? contextRoot.getValue() : null), this.evaluationContext);
}
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
@@ -157,13 +160,14 @@ public class SpelExpression implements Expression {
@SuppressWarnings("unchecked")
@Override
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationException {
if (this.compiledAst != null) {
try {
TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject();
Object result = this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext);
TypedValue contextRoot = (this.evaluationContext != null ? this.evaluationContext.getRootObject() : null);
Object result = this.compiledAst.getValue(
(contextRoot != null ? contextRoot.getValue() : null), this.evaluationContext);
if (expectedResultType == null) {
return (T)result;
return (T) result;
}
else {
return ExpressionUtils.convertTypedValue(getEvaluationContext(), new TypedValue(result), expectedResultType);
@@ -189,7 +193,7 @@ public class SpelExpression implements Expression {
@SuppressWarnings("unchecked")
@Override
public <T> T getValue(Object rootObject, Class<T> expectedResultType) throws EvaluationException {
public <T> T getValue(Object rootObject, @Nullable Class<T> expectedResultType) throws EvaluationException {
if (this.compiledAst != null) {
try {
Object result = this.compiledAst.getValue(rootObject, null);
@@ -223,8 +227,8 @@ public class SpelExpression implements Expression {
Assert.notNull(context, "EvaluationContext is required");
if (compiledAst!= null) {
try {
TypedValue contextRoot = context == null ? null : context.getRootObject();
return this.compiledAst.getValue(contextRoot != null ? contextRoot.getValue() : null, context);
TypedValue contextRoot = context.getRootObject();
return this.compiledAst.getValue(contextRoot.getValue(), context);
}
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
@@ -271,11 +275,11 @@ public class SpelExpression implements Expression {
@SuppressWarnings("unchecked")
@Override
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
public <T> T getValue(EvaluationContext context, @Nullable Class<T> expectedResultType) throws EvaluationException {
if (this.compiledAst != null) {
try {
TypedValue contextRoot = context == null ? null : context.getRootObject();
Object result = this.compiledAst.getValue(contextRoot==null?null:contextRoot.getValue(),context);
TypedValue contextRoot = context.getRootObject();
Object result = this.compiledAst.getValue(contextRoot.getValue(), context);
if (expectedResultType != null) {
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType);
}
@@ -303,7 +307,9 @@ public class SpelExpression implements Expression {
@SuppressWarnings("unchecked")
@Override
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType) throws EvaluationException {
public <T> T getValue(EvaluationContext context, Object rootObject, @Nullable Class<T> expectedResultType)
throws EvaluationException {
if (this.compiledAst != null) {
try {
Object result = this.compiledAst.getValue(rootObject,context);
@@ -501,13 +507,8 @@ public class SpelExpression implements Expression {
return this.ast.toStringAST();
}
private TypedValue toTypedValue(Object object) {
if (object == null) {
return TypedValue.NULL;
}
else {
return new TypedValue(object);
}
private TypedValue toTypedValue(@Nullable Object object) {
return (object != null ? new TypedValue(object) : TypedValue.NULL);
}
}

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.
@@ -29,6 +29,7 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MethodInvoker;
/**
@@ -62,25 +63,23 @@ public class ReflectionHelper {
for (int i = 0; i < expectedArgTypes.size() && match != null; i++) {
TypeDescriptor suppliedArg = suppliedArgTypes.get(i);
TypeDescriptor expectedArg = expectedArgTypes.get(i);
if (!expectedArg.equals(suppliedArg)) {
// The user may supply null - and that will be ok unless a primitive is expected
if (suppliedArg == null) {
if (expectedArg.isPrimitive()) {
match = null;
// The user may supply null - and that will be ok unless a primitive is expected
if (suppliedArg == null) {
if (expectedArg.isPrimitive()) {
match = null;
}
}
else if (!expectedArg.equals(suppliedArg)) {
if (suppliedArg.isAssignableTo(expectedArg)) {
if (match != ArgumentsMatchKind.REQUIRES_CONVERSION) {
match = ArgumentsMatchKind.CLOSE;
}
}
else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
match = ArgumentsMatchKind.REQUIRES_CONVERSION;
}
else {
if (suppliedArg.isAssignableTo(expectedArg)) {
if (match != ArgumentsMatchKind.REQUIRES_CONVERSION) {
match = ArgumentsMatchKind.CLOSE;
}
}
else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
match = ArgumentsMatchKind.REQUIRES_CONVERSION;
}
else {
match = null;
}
match = null;
}
}
}
@@ -145,8 +144,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)");
@@ -196,7 +195,9 @@ public class ReflectionHelper {
// Now... we have the final argument in the method we are checking as a match and we have 0
// or more other arguments left to pass to it.
TypeDescriptor varargsDesc = expectedArgTypes.get(expectedArgTypes.size() - 1);
Class<?> varargsParamType = varargsDesc.getElementTypeDescriptor().getType();
TypeDescriptor elementDesc = varargsDesc.getElementTypeDescriptor();
Assert.state(elementDesc != null, "No element type");
Class<?> varargsParamType = elementDesc.getType();
// All remaining parameters must be of this type or convertible to this type
for (int i = expectedArgTypes.size() - 1; i < suppliedArgTypes.size(); i++) {
@@ -300,6 +301,7 @@ public class ReflectionHelper {
else {
// Convert remaining arguments to the varargs element type
TypeDescriptor targetType = new TypeDescriptor(methodParam).getElementTypeDescriptor();
Assert.state(targetType != null, "No element type");
for (int i = varargsPosition; i < arguments.length; i++) {
Object argument = arguments[i];
arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
@@ -316,7 +318,7 @@ public class ReflectionHelper {
* @param possibleArray an array object that may have the supplied value as the first element
* @return true if the supplied value is the first entry in the array
*/
private static boolean isFirstEntryInArray(Object value, Object possibleArray) {
private static boolean isFirstEntryInArray(Object value, @Nullable Object possibleArray) {
if (possibleArray == null) {
return false;
}

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.
@@ -53,11 +53,11 @@ public class ReflectiveConstructorExecutor implements ConstructorExecutor {
@Override
public TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException {
try {
if (arguments != null) {
ReflectionHelper.convertArguments(context.getTypeConverter(), arguments, this.ctor, this.varargsPosition);
}
ReflectionHelper.convertArguments(
context.getTypeConverter(), arguments, this.ctor, this.varargsPosition);
if (this.ctor.isVarArgs()) {
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.ctor.getParameterTypes(), arguments);
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(
this.ctor.getParameterTypes(), arguments);
}
ReflectionUtils.makeAccessible(this.ctor);
return new TypedValue(this.ctor.newInstance(arguments));

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.
@@ -58,13 +58,10 @@ public class ReflectiveConstructorResolver implements ConstructorResolver {
Class<?> type = context.getTypeLocator().findType(typeName);
Constructor<?>[] ctors = type.getConstructors();
Arrays.sort(ctors, new Comparator<Constructor<?>>() {
@Override
public int compare(Constructor<?> c1, Constructor<?> c2) {
int c1pl = c1.getParameterCount();
int c2pl = c2.getParameterCount();
return (c1pl < c2pl ? -1 : (c1pl > c2pl ? 1 : 0));
}
Arrays.sort(ctors, (c1, c2) -> {
int c1pl = c1.getParameterCount();
int c2pl = c2.getParameterCount();
return (c1pl < c2pl ? -1 : (c1pl > c2pl ? 1 : 0));
});
Constructor<?> closeMatch = null;

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.
@@ -45,6 +45,7 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
private boolean argumentConversionOccurred = false;
public ReflectiveMethodExecutor(Method method) {
this.method = method;
if (method.isVarArgs()) {
@@ -68,9 +69,10 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
* helper method will walk up the type hierarchy to find the first public type that declares the
* method (if there is one!). For toString() it may walk as far as Object.
*/
@Nullable
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;
@@ -105,11 +107,11 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
@Override
public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException {
try {
if (arguments != null) {
this.argumentConversionOccurred = ReflectionHelper.convertArguments(context.getTypeConverter(), arguments, this.method, this.varargsPosition);
}
this.argumentConversionOccurred = ReflectionHelper.convertArguments(
context.getTypeConverter(), arguments, this.method, this.varargsPosition);
if (this.method.isVarArgs()) {
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.method.getParameterTypes(), arguments);
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(
this.method.getParameterTypes(), arguments);
}
ReflectionUtils.makeAccessible(this.method);
Object value = this.method.invoke(target, arguments);

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.
@@ -41,6 +41,7 @@ import org.springframework.expression.MethodResolver;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.lang.Nullable;
/**
* Reflection-based {@link MethodResolver} used by default in {@link StandardEvaluationContext}
@@ -80,7 +81,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
}
public void registerMethodFilter(Class<?> type, MethodFilter filter) {
public void registerMethodFilter(Class<?> type, @Nullable MethodFilter filter) {
if (this.filters == null) {
this.filters = new HashMap<>();
}

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.
@@ -41,6 +41,7 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.CodeFlow;
import org.springframework.expression.spel.CompilablePropertyAccessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@@ -70,14 +71,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
private final Map<PropertyCacheKey, InvokerPair> readerCache =
new ConcurrentHashMap<>(64);
private final Map<PropertyCacheKey, InvokerPair> readerCache = new ConcurrentHashMap<>(64);
private final Map<PropertyCacheKey, Member> writerCache =
new ConcurrentHashMap<>(64);
private final Map<PropertyCacheKey, Member> writerCache = new ConcurrentHashMap<>(64);
private final Map<PropertyCacheKey, TypeDescriptor> typeDescriptorCache =
new ConcurrentHashMap<>(64);
private final Map<PropertyCacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<>(64);
private InvokerPair lastReadInvokerPair;
@@ -91,7 +89,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
@Override
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
public boolean canRead(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
if (target == null) {
return false;
}
@@ -130,10 +128,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
@Override
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
if (target == null) {
throw new AccessException("Cannot read property of null target");
}
public TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
Assert.state(target != null, "Target must not be null");
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
if (type.isArray() && name.equals("length")) {
@@ -200,7 +196,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
@Override
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
public boolean canWrite(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
if (target == null) {
return false;
}
@@ -230,10 +226,10 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
@Override
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
if (target == null) {
throw new AccessException("Cannot write property on null target");
}
public void write(EvaluationContext context, @Nullable Object target, String name, @Nullable Object newValue)
throws AccessException {
Assert.state(target != null, "Target must not be null");
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
Object possiblyConvertedNewValue = newValue;
@@ -295,10 +291,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
throw new AccessException("Neither setter method nor field found for property '" + name + "'");
}
@Nullable
private TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
if (target == null) {
return null;
}
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
if (type.isArray() && name.equals("length")) {
@@ -323,6 +317,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return typeDescriptor;
}
@Nullable
private Method findGetterForProperty(String propertyName, Class<?> clazz, Object target) {
Method method = findGetterForProperty(propertyName, clazz, target instanceof Class);
if (method == null && target instanceof Class) {
@@ -331,6 +326,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return method;
}
@Nullable
private Method findSetterForProperty(String propertyName, Class<?> clazz, Object target) {
Method method = findSetterForProperty(propertyName, clazz, target instanceof Class);
if (method == null && target instanceof Class) {
@@ -339,6 +335,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return method;
}
@Nullable
private Field findField(String name, Class<?> clazz, Object target) {
Field field = findField(name, clazz, target instanceof Class);
if (field == null && target instanceof Class) {
@@ -350,6 +347,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
/**
* Find a getter method for the specified property.
*/
@Nullable
protected Method findGetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
Method method = findMethodForProperty(getPropertyMethodSuffixes(propertyName),
"get", clazz, mustBeStatic, 0, ANY_TYPES);
@@ -363,6 +361,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
/**
* Find a setter method for the specified property.
*/
@Nullable
protected Method findSetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
return findMethodForProperty(getPropertyMethodSuffixes(propertyName),
"set", clazz, mustBeStatic, 1, ANY_TYPES);
@@ -461,7 +460,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
* This method will just return the ReflectivePropertyAccessor instance if it is unable to build
* something more optimal.
*/
public PropertyAccessor createOptimalAccessor(EvaluationContext evalContext, Object target, String name) {
public PropertyAccessor createOptimalAccessor(EvaluationContext evalContext, @Nullable Object target, String name) {
// Don't be clever for arrays or null target
if (target == null) {
return this;
@@ -603,11 +602,10 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
@Override
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
public boolean canRead(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
if (target == null) {
return false;
}
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
if (type.isArray()) {
return false;
@@ -629,7 +627,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
@Override
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
public TypedValue read(EvaluationContext context, @Nullable Object target, String name) throws AccessException {
if (this.member instanceof Method) {
Method method = (Method) this.member;
try {
@@ -659,12 +657,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
@Override
public boolean canWrite(EvaluationContext context, Object target, String name) {
public boolean canWrite(EvaluationContext context, @Nullable Object target, String name) {
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
}
@Override
public void write(EvaluationContext context, Object target, String name, Object newValue) {
public void write(EvaluationContext context, @Nullable Object target, String name, @Nullable Object newValue) {
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
}

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.
@@ -170,7 +170,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;
}
@@ -209,7 +209,7 @@ public class StandardEvaluationContext implements EvaluationContext {
}
@Override
public void setVariable(String name, Object value) {
public void setVariable(String name, @Nullable Object value) {
this.variables.put(name, value);
}

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.
@@ -19,6 +19,7 @@ package org.springframework.expression.spel.support;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.OperatorOverloader;
import org.springframework.lang.Nullable;
/**
* @author Juergen Hoeller
@@ -27,13 +28,16 @@ import org.springframework.expression.OperatorOverloader;
public class StandardOperatorOverloader implements OperatorOverloader {
@Override
public boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand)
public boolean overridesOperation(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand)
throws EvaluationException {
return false;
}
@Override
public Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException {
public Object operate(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand)
throws EvaluationException {
throw new EvaluationException("No operation overloaded by default");
}

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.
@@ -22,6 +22,7 @@ import java.math.BigInteger;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.lang.Nullable;
import org.springframework.util.NumberUtils;
/**
@@ -36,7 +37,7 @@ import org.springframework.util.NumberUtils;
public class StandardTypeComparator implements TypeComparator {
@Override
public boolean canCompare(Object left, Object right) {
public boolean canCompare(@Nullable Object left, @Nullable Object right) {
if (left == null || right == null) {
return true;
}
@@ -51,7 +52,7 @@ public class StandardTypeComparator implements TypeComparator {
@Override
@SuppressWarnings("unchecked")
public int compare(Object left, Object right) throws SpelEvaluationException {
public int compare(@Nullable Object left, @Nullable Object right) throws SpelEvaluationException {
// If one is null, check if the other is
if (left == null) {
return (right == null ? 0 : -1);

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.
@@ -23,6 +23,7 @@ import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -57,18 +58,19 @@ public class StandardTypeConverter implements TypeConverter {
@Override
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType, targetType);
}
@Override
public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) {
public Object convertValue(@Nullable Object value, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
try {
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());
}
}

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.
@@ -24,6 +24,7 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
@@ -54,7 +55,7 @@ public class StandardTypeLocator implements TypeLocator {
* Create a StandardTypeLocator for the given ClassLoader.
* @param classLoader the ClassLoader to delegate to
*/
public StandardTypeLocator(ClassLoader classLoader) {
public StandardTypeLocator(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
// Similar to when writing regular Java code, it only knows about java.lang by default
registerImport("java.lang");

View File

@@ -244,6 +244,7 @@ public class EvaluationTests extends AbstractExpressionTests {
fail("Should have failed to parse");
}
catch (ParseException e) {
e.printStackTrace();
assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException) e;
assertEquals(SpelMessage.OOD, spe.getMessageCode());

View File

@@ -262,7 +262,7 @@ public class ExpressionStateTests extends AbstractExpressionTests {
@Test
public void testTypeConversion() throws EvaluationException {
ExpressionState state = getState();
String s = (String)state.convertValue(34, TypeDescriptor.valueOf(String.class));
String s = (String) state.convertValue(34, TypeDescriptor.valueOf(String.class));
assertEquals("34",s);
s = (String)state.convertValue(new TypedValue(34), TypeDescriptor.valueOf(String.class));

View File

@@ -62,7 +62,8 @@ public class SetValueTests extends AbstractExpressionTests {
@Test
public void testSetElementOfNull() {
setValueExpectError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
setValueExpectError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",
SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
}
@Test

View File

@@ -474,7 +474,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
expression = parser.parseExpression("T(Integer).valueOf(42)");
expression.getValue(Integer.class);
assertCanCompile(expression);
assertEquals(new Integer(42), expression.getValue(null, Integer.class));
assertEquals(new Integer(42), expression.getValue(new StandardEvaluationContext(), Integer.class));
// Code gen is different for -1 .. 6 because there are bytecode instructions specifically for those
// values

View File

@@ -360,16 +360,16 @@ public class SpelReproTests extends AbstractExpressionTests {
assertFalse(propertyAccessor.canWrite(context, null, "abc"));
try {
propertyAccessor.read(context, null, "abc");
fail("Should have failed with an AccessException");
fail("Should have failed with an IllegalStateException");
}
catch (AccessException ae) {
catch (IllegalStateException ae) {
// success
}
try {
propertyAccessor.write(context, null, "abc", "foo");
fail("Should have failed with an AccessException");
fail("Should have failed with an AccessEIllegalStateExceptionxception");
}
catch (AccessException ae) {
catch (IllegalStateException ae) {
// success
}
}

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.
@@ -213,23 +213,21 @@ public class TemplateExpressionParsingTests extends AbstractExpressionTests {
// Just wanting to use the prefix or suffix within the template:
Expression ex = parser.parseExpression("hello ${3+4} world",DEFAULT_TEMPLATE_PARSER_CONTEXT);
String s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
assertEquals("hello 7 world",s);
assertEquals("hello 7 world", s);
ex = parser.parseExpression("hello ${3+4} wo${'${'}rld",DEFAULT_TEMPLATE_PARSER_CONTEXT);
s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
assertEquals("hello 7 wo${rld",s);
assertEquals("hello 7 wo${rld", s);
ex = parser.parseExpression("hello ${3+4} wo}rld",DEFAULT_TEMPLATE_PARSER_CONTEXT);
s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
assertEquals("hello 7 wo}rld",s);
assertEquals("hello 7 wo}rld", s);
}
@Test
public void testParsingNormalExpressionThroughTemplateParser() throws Exception {
Expression expr = parser.parseExpression("1+2+3");
assertEquals(6,expr.getValue());
expr = parser.parseExpression("1+2+3",null);
assertEquals(6,expr.getValue());
assertEquals(6, expr.getValue());
}
@Test