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