Browse Source

SpEL indexer uses direct access to specific List elements instead of iterating over the Collection

Issue: SPR-10035
pull/550/head
Juergen Hoeller 13 years ago
parent
commit
e080af8d85
  1. 439
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java

439
spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java

@ -48,22 +48,30 @@ public class Indexer extends SpelNodeImpl {
// If the name and target type match these cached values then the cachedReadAccessor // If the name and target type match these cached values then the cachedReadAccessor
// is used to read the property. If they do not match, the correct accessor is // is used to read the property. If they do not match, the correct accessor is
// discovered and then cached for later use. // discovered and then cached for later use.
private String cachedReadName; private String cachedReadName;
private Class<?> cachedReadTargetType; private Class<?> cachedReadTargetType;
private PropertyAccessor cachedReadAccessor; private PropertyAccessor cachedReadAccessor;
// These fields are used when the indexer is being used as a property write accessor. // These fields are used when the indexer is being used as a property write accessor.
// If the name and target type match these cached values then the cachedWriteAccessor // If the name and target type match these cached values then the cachedWriteAccessor
// is used to write the property. If they do not match, the correct accessor is // is used to write the property. If they do not match, the correct accessor is
// discovered and then cached for later use. // discovered and then cached for later use.
private String cachedWriteName; private String cachedWriteName;
private Class<?> cachedWriteTargetType; private Class<?> cachedWriteTargetType;
private PropertyAccessor cachedWriteAccessor; private PropertyAccessor cachedWriteAccessor;
public Indexer(int pos, SpelNodeImpl expr) { public Indexer(int pos, SpelNodeImpl expr) {
super(pos, expr); super(pos, expr);
} }
@Override @Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
return getValueRef(state).getValue(); return getValueRef(state).getValue();
@ -75,20 +83,22 @@ public class Indexer extends SpelNodeImpl {
} }
@Override @Override
public boolean isWritable(ExpressionState expressionState) public boolean isWritable(ExpressionState expressionState) throws SpelEvaluationException {
throws SpelEvaluationException {
return true; return true;
} }
class ArrayIndexingValueRef implements ValueRef {
private TypeConverter typeConverter; private class ArrayIndexingValueRef implements ValueRef {
private Object array;
private int idx; private final TypeConverter typeConverter;
private TypeDescriptor typeDescriptor;
ArrayIndexingValueRef(TypeConverter typeConverter, Object array, private final Object array;
int idx, TypeDescriptor typeDescriptor) {
private final int idx;
private final TypeDescriptor typeDescriptor;
ArrayIndexingValueRef(TypeConverter typeConverter, Object array, int idx, TypeDescriptor typeDescriptor) {
this.typeConverter = typeConverter; this.typeConverter = typeConverter;
this.array = array; this.array = array;
this.idx = idx; this.idx = idx;
@ -96,14 +106,13 @@ public class Indexer extends SpelNodeImpl {
} }
public TypedValue getValue() { public TypedValue getValue() {
Object arrayElement = accessArrayElement(array, idx); Object arrayElement = accessArrayElement(this.array, this.idx);
return new TypedValue(arrayElement, return new TypedValue(arrayElement, this.typeDescriptor.elementTypeDescriptor(arrayElement));
typeDescriptor.elementTypeDescriptor(arrayElement));
} }
public void setValue(Object newValue) { public void setValue(Object newValue) {
setArrayElement(typeConverter, array, idx, newValue, typeDescriptor setArrayElement(this.typeConverter, this.array, this.idx, newValue,
.getElementTypeDescriptor().getType()); this.typeDescriptor.getElementTypeDescriptor().getType());
} }
public boolean isWritable() { public boolean isWritable() {
@ -111,16 +120,19 @@ public class Indexer extends SpelNodeImpl {
} }
} }
@SuppressWarnings({ "rawtypes", "unchecked" })
class MapIndexingValueRef implements ValueRef {
private TypeConverter typeConverter; @SuppressWarnings({"rawtypes", "unchecked"})
private Map map; private class MapIndexingValueRef implements ValueRef {
private Object key;
private TypeDescriptor mapEntryTypeDescriptor; private final TypeConverter typeConverter;
MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, private final Map map;
TypeDescriptor mapEntryTypeDescriptor) {
private final Object key;
private final TypeDescriptor mapEntryTypeDescriptor;
MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryTypeDescriptor) {
this.typeConverter = typeConverter; this.typeConverter = typeConverter;
this.map = map; this.map = map;
this.key = key; this.key = key;
@ -128,35 +140,35 @@ public class Indexer extends SpelNodeImpl {
} }
public TypedValue getValue() { public TypedValue getValue() {
Object value = map.get(key); Object value = this.map.get(this.key);
return new TypedValue(value, return new TypedValue(value, this.mapEntryTypeDescriptor.getMapValueTypeDescriptor(value));
mapEntryTypeDescriptor.getMapValueTypeDescriptor(value));
} }
public void setValue(Object newValue) { public void setValue(Object newValue) {
if (mapEntryTypeDescriptor.getMapValueTypeDescriptor() != null) { if (this.mapEntryTypeDescriptor.getMapValueTypeDescriptor() != null) {
newValue = typeConverter.convertValue(newValue, newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue), this.mapEntryTypeDescriptor.getMapValueTypeDescriptor());
mapEntryTypeDescriptor.getMapValueTypeDescriptor());
} }
map.put(key, newValue); this.map.put(this.key, newValue);
} }
public boolean isWritable() { public boolean isWritable() {
return true; return true;
} }
} }
class PropertyIndexingValueRef implements ValueRef {
private Object targetObject; private class PropertyIndexingValueRef implements ValueRef {
private String name;
private EvaluationContext eContext;
private TypeDescriptor td;
public PropertyIndexingValueRef(Object targetObject, String value, private final Object targetObject;
EvaluationContext evaluationContext,
private final String name;
private final EvaluationContext eContext;
private final TypeDescriptor td;
public PropertyIndexingValueRef(Object targetObject, String value, EvaluationContext evaluationContext,
TypeDescriptor targetObjectTypeDescriptor) { TypeDescriptor targetObjectTypeDescriptor) {
this.targetObject = targetObject; this.targetObject = targetObject;
this.name = value; this.name = value;
@ -166,78 +178,63 @@ public class Indexer extends SpelNodeImpl {
public TypedValue getValue() { public TypedValue getValue() {
Class<?> targetObjectRuntimeClass = getObjectClass(targetObject); Class<?> targetObjectRuntimeClass = getObjectClass(targetObject);
try { try {
if (cachedReadName != null if (cachedReadName != null && cachedReadName.equals(name) && cachedReadTargetType != null &&
&& cachedReadName.equals(name) cachedReadTargetType.equals(targetObjectRuntimeClass)) {
&& cachedReadTargetType != null
&& cachedReadTargetType
.equals(targetObjectRuntimeClass)) {
// it is OK to use the cached accessor // it is OK to use the cached accessor
return cachedReadAccessor return cachedReadAccessor.read(this.eContext, this.targetObject, this.name);
.read(eContext, targetObject, name);
} }
List<PropertyAccessor> accessorsToTry =
List<PropertyAccessor> accessorsToTry = AstUtils AstUtils.getPropertyAccessorsToTry(targetObjectRuntimeClass, eContext.getPropertyAccessors());
.getPropertyAccessorsToTry(targetObjectRuntimeClass,
eContext.getPropertyAccessors());
if (accessorsToTry != null) { if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) { for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canRead(eContext, targetObject, name)) { if (accessor.canRead(this.eContext, this.targetObject, this.name)) {
if (accessor instanceof ReflectivePropertyAccessor) { if (accessor instanceof ReflectivePropertyAccessor) {
accessor = ((ReflectivePropertyAccessor) accessor) accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
.createOptimalAccessor(eContext, this.eContext, this.targetObject, this.name);
targetObject, name);
} }
cachedReadAccessor = accessor; cachedReadAccessor = accessor;
cachedReadName = name; cachedReadName = this.name;
cachedReadTargetType = targetObjectRuntimeClass; cachedReadTargetType = targetObjectRuntimeClass;
return accessor.read(eContext, targetObject, name); return accessor.read(this.eContext, this.targetObject, this.name);
} }
} }
} }
} catch (AccessException e) {
throw new SpelEvaluationException(getStartPosition(), e,
SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
td.toString());
} }
throw new SpelEvaluationException(getStartPosition(), catch (AccessException ex) {
SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, td.toString()); throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
this.td.toString());
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
this.td.toString());
} }
public void setValue(Object newValue) { public void setValue(Object newValue) {
Class<?> contextObjectClass = getObjectClass(targetObject); Class<?> contextObjectClass = getObjectClass(targetObject);
try { try {
if (cachedWriteName != null && cachedWriteName.equals(name) if (cachedWriteName != null && cachedWriteName.equals(name) && cachedWriteTargetType != null &&
&& cachedWriteTargetType != null cachedWriteTargetType.equals(contextObjectClass)) {
&& cachedWriteTargetType.equals(contextObjectClass)) {
// it is OK to use the cached accessor // it is OK to use the cached accessor
cachedWriteAccessor.write(eContext, targetObject, name, cachedWriteAccessor.write(this.eContext, this.targetObject, this.name, newValue);
newValue);
return; return;
} }
List<PropertyAccessor> accessorsToTry =
List<PropertyAccessor> accessorsToTry = AstUtils AstUtils.getPropertyAccessorsToTry(contextObjectClass, this.eContext.getPropertyAccessors());
.getPropertyAccessorsToTry(contextObjectClass,
eContext.getPropertyAccessors());
if (accessorsToTry != null) { if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) { for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canWrite(eContext, targetObject, name)) { if (accessor.canWrite(this.eContext, this.targetObject, this.name)) {
cachedWriteName = name; cachedWriteName = this.name;
cachedWriteTargetType = contextObjectClass; cachedWriteTargetType = contextObjectClass;
cachedWriteAccessor = accessor; cachedWriteAccessor = accessor;
accessor.write(eContext, targetObject, name, accessor.write(this.eContext, this.targetObject, this.name, newValue);
newValue);
return; return;
} }
} }
} }
} catch (AccessException ae) { }
throw new SpelEvaluationException(getStartPosition(), ae, catch (AccessException ex) {
SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE, name, throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE,
ae.getMessage()); this.name, ex.getMessage());
} }
} }
@ -246,70 +243,74 @@ public class Indexer extends SpelNodeImpl {
} }
} }
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
class CollectionIndexingValueRef implements ValueRef { private class CollectionIndexingValueRef implements ValueRef {
private TypeConverter typeConverter; private final Collection collection;
private Collection collection;
private int index;
private TypeDescriptor collectionEntryTypeDescriptor;
private boolean growCollection;
CollectionIndexingValueRef(Collection collection, int index, private final int index;
TypeDescriptor collectionEntryTypeDescriptor, TypeConverter typeConverter, boolean growCollection) {
this.typeConverter = typeConverter; private final TypeDescriptor collectionEntryTypeDescriptor;
this.growCollection = growCollection;
private final TypeConverter typeConverter;
private final boolean growCollection;
CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryTypeDescriptor,
TypeConverter typeConverter, boolean growCollection) {
this.collection = collection; this.collection = collection;
this.index = index; this.index = index;
this.collectionEntryTypeDescriptor = collectionEntryTypeDescriptor; this.collectionEntryTypeDescriptor = collectionEntryTypeDescriptor;
this.typeConverter = typeConverter;
this.growCollection = growCollection;
} }
public TypedValue getValue() { public TypedValue getValue() {
if (index >= collection.size()) { if (this.index >= this.collection.size()) {
if (growCollection) { if (this.growCollection) {
growCollection(collectionEntryTypeDescriptor,index,collection); growCollection(this.collectionEntryTypeDescriptor, this.index, this.collection);
} else { }
throw new SpelEvaluationException(getStartPosition(), else {
SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS,
collection.size(), index); this.collection.size(), this.index);
} }
} }
if (this.collection instanceof List) {
Object o = ((List) this.collection).get(this.index);
return new TypedValue(o, this.collectionEntryTypeDescriptor.elementTypeDescriptor(o));
}
int pos = 0; int pos = 0;
for (Object o : collection) { for (Object o : this.collection) {
if (pos == index) { if (pos == this.index) {
return new TypedValue(o, return new TypedValue(o, this.collectionEntryTypeDescriptor.elementTypeDescriptor(o));
collectionEntryTypeDescriptor
.elementTypeDescriptor(o));
} }
pos++; pos++;
} }
throw new IllegalStateException(); throw new IllegalStateException("Failed to find indexed element " + this.index + ": " + this.collection);
} }
public void setValue(Object newValue) { public void setValue(Object newValue) {
if (index >= collection.size()) { if (this.index >= this.collection.size()) {
if (growCollection) { if (this.growCollection) {
growCollection(collectionEntryTypeDescriptor, index, collection); growCollection(this.collectionEntryTypeDescriptor, this.index, this.collection);
} else { }
throw new SpelEvaluationException(getStartPosition(), else {
SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS,
collection.size(), index); this.collection.size(), this.index);
} }
} }
if (collection instanceof List) { if (this.collection instanceof List) {
List list = (List) collection; List list = (List) this.collection;
if (collectionEntryTypeDescriptor.getElementTypeDescriptor() != null) { if (this.collectionEntryTypeDescriptor.getElementTypeDescriptor() != null) {
newValue = typeConverter.convertValue(newValue, newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue), this.collectionEntryTypeDescriptor.getElementTypeDescriptor());
collectionEntryTypeDescriptor
.getElementTypeDescriptor());
} }
list.set(index, newValue); list.set(this.index, newValue);
return; }
} else { else {
throw new SpelEvaluationException(getStartPosition(), throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, this.collectionEntryTypeDescriptor.toString());
collectionEntryTypeDescriptor.toString());
} }
} }
@ -318,11 +319,14 @@ public class Indexer extends SpelNodeImpl {
} }
} }
class StringIndexingLValue implements ValueRef {
private String target; private class StringIndexingLValue implements ValueRef {
private int index;
private TypeDescriptor td; private final String target;
private final int index;
private final TypeDescriptor td;
public StringIndexingLValue(String target, int index, TypeDescriptor td) { public StringIndexingLValue(String target, int index, TypeDescriptor td) {
this.target = target; this.target = target;
@ -331,28 +335,25 @@ public class Indexer extends SpelNodeImpl {
} }
public TypedValue getValue() { public TypedValue getValue() {
if (index >= target.length()) { if (this.index >= this.target.length()) {
throw new SpelEvaluationException(getStartPosition(), throw new SpelEvaluationException(getStartPosition(), SpelMessage.STRING_INDEX_OUT_OF_BOUNDS,
SpelMessage.STRING_INDEX_OUT_OF_BOUNDS, this.target.length(), index);
target.length(), index);
} }
return new TypedValue(String.valueOf(target.charAt(index))); return new TypedValue(String.valueOf(this.target.charAt(this.index)));
} }
public void setValue(Object newValue) { public void setValue(Object newValue) {
throw new SpelEvaluationException(getStartPosition(), throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, td.toString()); this.td.toString());
} }
public boolean isWritable() { public boolean isWritable() {
return true; return true;
} }
} }
@Override @Override
protected ValueRef getValueRef(ExpressionState state) protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
throws EvaluationException {
TypedValue context = state.getActiveContextObject(); TypedValue context = state.getActiveContextObject();
Object targetObject = context.getValue(); Object targetObject = context.getValue();
TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor(); TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor();
@ -361,18 +362,20 @@ public class Indexer extends SpelNodeImpl {
// This first part of the if clause prevents a 'double dereference' of // This first part of the if clause prevents a 'double dereference' of
// the property (SPR-5847) // the property (SPR-5847)
if (targetObject instanceof Map && (children[0] instanceof PropertyOrFieldReference)) { if (targetObject instanceof Map && (this.children[0] instanceof PropertyOrFieldReference)) {
PropertyOrFieldReference reference = (PropertyOrFieldReference) children[0]; PropertyOrFieldReference reference = (PropertyOrFieldReference) this.children[0];
index = reference.getName(); index = reference.getName();
indexValue = new TypedValue(index); indexValue = new TypedValue(index);
} else { }
else {
// In case the map key is unqualified, we want it evaluated against // In case the map key is unqualified, we want it evaluated against
// the root object so temporarily push that on whilst evaluating the key // the root object so temporarily push that on whilst evaluating the key
try { try {
state.pushActiveContextObject(state.getRootContextObject()); state.pushActiveContextObject(state.getRootContextObject());
indexValue = children[0].getValueInternal(state); indexValue = this.children[0].getValueInternal(state);
index = indexValue.getValue(); index = indexValue.getValue();
} finally { }
finally {
state.popActiveContextObject(); state.popActiveContextObject();
} }
} }
@ -381,69 +384,55 @@ public class Indexer extends SpelNodeImpl {
if (targetObject instanceof Map) { if (targetObject instanceof Map) {
Object key = index; Object key = index;
if (targetObjectTypeDescriptor.getMapKeyTypeDescriptor() != null) { if (targetObjectTypeDescriptor.getMapKeyTypeDescriptor() != null) {
key = state.convertValue(key, key = state.convertValue(key, targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
} }
return new MapIndexingValueRef(state.getTypeConverter(), return new MapIndexingValueRef(state.getTypeConverter(), (Map<?, ?>) targetObject, key,
(Map<?, ?>) targetObject, key, targetObjectTypeDescriptor); targetObjectTypeDescriptor);
} }
if (targetObject == null) { if (targetObject == null) {
throw new SpelEvaluationException(getStartPosition(), throw new SpelEvaluationException(getStartPosition(), SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
} }
// if the object is something that looks indexable by an integer, // if the object is something that looks indexable by an integer,
// attempt to treat the index value as a number // attempt to treat the index value as a number
if (targetObject instanceof Collection if (targetObject.getClass().isArray() || targetObject instanceof Collection || targetObject instanceof String) {
|| targetObject.getClass().isArray() int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
|| targetObject instanceof String) {
int idx = (Integer) state.convertValue(index,
TypeDescriptor.valueOf(Integer.class));
if (targetObject.getClass().isArray()) { if (targetObject.getClass().isArray()) {
return new ArrayIndexingValueRef(state.getTypeConverter(), return new ArrayIndexingValueRef(state.getTypeConverter(), targetObject, idx, targetObjectTypeDescriptor);
targetObject, idx, targetObjectTypeDescriptor); }
} else if (targetObject instanceof Collection) { else if (targetObject instanceof Collection) {
return new CollectionIndexingValueRef( return new CollectionIndexingValueRef((Collection<?>) targetObject, idx, targetObjectTypeDescriptor,
(Collection<?>) targetObject, idx, state.getTypeConverter(), state.getConfiguration().isAutoGrowCollections());
targetObjectTypeDescriptor,state.getTypeConverter(), }
state.getConfiguration().isAutoGrowCollections()); else if (targetObject instanceof String) {
} else if (targetObject instanceof String) { return new StringIndexingLValue((String) targetObject, idx, targetObjectTypeDescriptor);
return new StringIndexingLValue((String) targetObject, idx,
targetObjectTypeDescriptor);
} }
} }
// Try and treat the index value as a property of the context object // 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 // TODO could call the conversion service to convert the value to a String
// String
if (indexValue.getTypeDescriptor().getType() == String.class) { if (indexValue.getTypeDescriptor().getType() == String.class) {
return new PropertyIndexingValueRef(targetObject, return new PropertyIndexingValueRef(targetObject, (String) indexValue.getValue(),
(String) indexValue.getValue(),
state.getEvaluationContext(), targetObjectTypeDescriptor); state.getEvaluationContext(), targetObjectTypeDescriptor);
} }
throw new SpelEvaluationException(getStartPosition(), throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
targetObjectTypeDescriptor.toString()); targetObjectTypeDescriptor.toString());
} }
/** /**
* Attempt to grow the specified collection so that the specified index is * Attempt to grow the specified collection so that the specified index is valid.
* valid. * @param targetType the type of the elements in the collection
*
* @param elementType the type of the elements in the collection
* @param index the index into the collection that needs to be valid * @param index the index into the collection that needs to be valid
* @param collection the collection to grow with elements * @param collection the collection to grow with elements
*/ */
private void growCollection(TypeDescriptor targetType, int index, Collection<Object> collection) { private void growCollection(TypeDescriptor targetType, int index, Collection<Object> collection) {
if (targetType.getElementTypeDescriptor() == null) { if (targetType.getElementTypeDescriptor() == null) {
throw new SpelEvaluationException( throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE);
getStartPosition(),
SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE);
} }
TypeDescriptor elementType = targetType.getElementTypeDescriptor(); TypeDescriptor elementType = targetType.getElementTypeDescriptor();
Object newCollectionElement = null; Object newCollectionElement;
try { try {
int newElements = index - collection.size(); int newElements = index - collection.size();
while (newElements > 0) { while (newElements > 0) {
@ -451,9 +440,9 @@ public class Indexer extends SpelNodeImpl {
newElements--; newElements--;
} }
newCollectionElement = elementType.getType().newInstance(); newCollectionElement = elementType.getType().newInstance();
} catch (Exception ex) { }
throw new SpelEvaluationException(getStartPosition(), ex, catch (Exception ex) {
SpelMessage.UNABLE_TO_GROW_COLLECTION); throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION);
} }
collection.add(newCollectionElement); collection.add(newCollectionElement);
} }
@ -463,70 +452,70 @@ public class Indexer extends SpelNodeImpl {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("["); sb.append("[");
for (int i = 0; i < getChildCount(); i++) { for (int i = 0; i < getChildCount(); i++) {
if (i > 0) if (i > 0) {
sb.append(","); sb.append(",");
}
sb.append(getChild(i).toStringAST()); sb.append(getChild(i).toStringAST());
} }
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();
} }
private void setArrayElement(TypeConverter converter, Object ctx, int idx, private void setArrayElement(TypeConverter converter, Object ctx, int idx, Object newValue, Class<?> clazz)
Object newValue, Class<?> clazz) throws EvaluationException { throws EvaluationException {
Class<?> arrayComponentType = clazz; Class<?> arrayComponentType = clazz;
if (arrayComponentType == Integer.TYPE) { if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx; int[] array = (int[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
array[idx] = (Integer) converter.convertValue(newValue, array[idx] = (Integer) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Integer.class)); TypeDescriptor.valueOf(Integer.class));
} else if (arrayComponentType == Boolean.TYPE) { }
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx; boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
array[idx] = (Boolean) converter.convertValue(newValue, array[idx] = (Boolean) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Boolean.class)); TypeDescriptor.valueOf(Boolean.class));
} else if (arrayComponentType == Character.TYPE) { }
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx; char[] array = (char[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
array[idx] = (Character) converter.convertValue(newValue, array[idx] = (Character) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Character.class)); TypeDescriptor.valueOf(Character.class));
} else if (arrayComponentType == Long.TYPE) { }
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx; long[] array = (long[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
array[idx] = (Long) converter.convertValue(newValue, array[idx] = (Long) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Long.class)); TypeDescriptor.valueOf(Long.class));
} else if (arrayComponentType == Short.TYPE) { }
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx; short[] array = (short[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
array[idx] = (Short) converter.convertValue(newValue, array[idx] = (Short) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Short.class)); TypeDescriptor.valueOf(Short.class));
} else if (arrayComponentType == Double.TYPE) { }
else if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx; double[] array = (double[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
array[idx] = (Double) converter.convertValue(newValue, array[idx] = (Double) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Double.class)); TypeDescriptor.valueOf(Double.class));
} else if (arrayComponentType == Float.TYPE) { }
else if (arrayComponentType == Float.TYPE) {
float[] array = (float[]) ctx; float[] array = (float[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
array[idx] = (Float) converter.convertValue(newValue, array[idx] = (Float) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Float.class)); TypeDescriptor.valueOf(Float.class));
} else if (arrayComponentType == Byte.TYPE) { }
else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx; byte[] array = (byte[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
array[idx] = (Byte) converter.convertValue(newValue, array[idx] = (Byte) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Byte.class)); TypeDescriptor.valueOf(Byte.class));
} else { }
else {
Object[] array = (Object[]) ctx; Object[] array = (Object[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
array[idx] = converter.convertValue(newValue, array[idx] = converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(clazz)); TypeDescriptor.valueOf(clazz));
} }
} }
@ -537,35 +526,43 @@ public class Indexer extends SpelNodeImpl {
int[] array = (int[]) ctx; int[] array = (int[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
return array[idx]; return array[idx];
} else if (arrayComponentType == Boolean.TYPE) { }
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx; boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
return array[idx]; return array[idx];
} else if (arrayComponentType == Character.TYPE) { }
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx; char[] array = (char[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
return array[idx]; return array[idx];
} else if (arrayComponentType == Long.TYPE) { }
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx; long[] array = (long[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
return array[idx]; return array[idx];
} else if (arrayComponentType == Short.TYPE) { }
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx; short[] array = (short[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
return array[idx]; return array[idx];
} else if (arrayComponentType == Double.TYPE) { }
else if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx; double[] array = (double[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
return array[idx]; return array[idx];
} else if (arrayComponentType == Float.TYPE) { }
else if (arrayComponentType == Float.TYPE) {
float[] array = (float[]) ctx; float[] array = (float[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
return array[idx]; return array[idx];
} else if (arrayComponentType == Byte.TYPE) { }
else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx; byte[] array = (byte[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
return array[idx]; return array[idx];
} else { }
else {
Object[] array = (Object[]) ctx; Object[] array = (Object[]) ctx;
checkAccess(array.length, idx); checkAccess(array.length, idx);
return array[idx]; return array[idx];
@ -574,9 +571,9 @@ public class Indexer extends SpelNodeImpl {
private void checkAccess(int arrayLength, int index) throws SpelEvaluationException { private void checkAccess(int arrayLength, int index) throws SpelEvaluationException {
if (index > arrayLength) { if (index > arrayLength) {
throw new SpelEvaluationException(getStartPosition(), throw new SpelEvaluationException(getStartPosition(), SpelMessage.ARRAY_INDEX_OUT_OF_BOUNDS,
SpelMessage.ARRAY_INDEX_OUT_OF_BOUNDS, arrayLength, index); arrayLength, index);
} }
} }
} }

Loading…
Cancel
Save