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:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ public class OpDec extends Operator {
|
||||
|
||||
@Override
|
||||
public SpelNodeImpl getRightOperand() {
|
||||
return null;
|
||||
throw new IllegalStateException("No right operand");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ public class OpInc extends Operator {
|
||||
|
||||
@Override
|
||||
public SpelNodeImpl getRightOperand() {
|
||||
return null;
|
||||
throw new IllegalStateException("No right operand");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<>();
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user