|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2017 the original author or authors. |
|
|
|
* Copyright 2002-2018 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -103,21 +103,20 @@ public class Indexer extends SpelNodeImpl { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { |
|
|
|
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { |
|
|
|
TypedValue context = state.getActiveContextObject(); |
|
|
|
TypedValue context = state.getActiveContextObject(); |
|
|
|
Object targetObject = context.getValue(); |
|
|
|
Object target = context.getValue(); |
|
|
|
TypeDescriptor targetDescriptor = context.getTypeDescriptor(); |
|
|
|
TypeDescriptor targetDescriptor = context.getTypeDescriptor(); |
|
|
|
TypedValue indexValue = null; |
|
|
|
TypedValue indexValue; |
|
|
|
Object index = null; |
|
|
|
Object index; |
|
|
|
|
|
|
|
|
|
|
|
// 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 (target instanceof Map && (this.children[0] instanceof PropertyOrFieldReference)) { |
|
|
|
if (targetObject instanceof Map && (this.children[0] instanceof PropertyOrFieldReference)) { |
|
|
|
|
|
|
|
PropertyOrFieldReference reference = (PropertyOrFieldReference) this.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
|
|
|
|
// the root object so temporarily push that on whilst evaluating the key
|
|
|
|
// so temporarily push that on whilst evaluating the key
|
|
|
|
try { |
|
|
|
try { |
|
|
|
state.pushActiveContextObject(state.getRootContextObject()); |
|
|
|
state.pushActiveContextObject(state.getRootContextObject()); |
|
|
|
indexValue = this.children[0].getValueInternal(state); |
|
|
|
indexValue = this.children[0].getValueInternal(state); |
|
|
|
@ -128,52 +127,53 @@ public class Indexer extends SpelNodeImpl { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Raise a proper exception in case of a null target
|
|
|
|
|
|
|
|
if (target == null) { |
|
|
|
|
|
|
|
throw new SpelEvaluationException(getStartPosition(), SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Indexing into a Map
|
|
|
|
// Indexing into a Map
|
|
|
|
if (targetObject instanceof Map) { |
|
|
|
if (target instanceof Map) { |
|
|
|
Object key = index; |
|
|
|
Object key = index; |
|
|
|
if (targetDescriptor.getMapKeyTypeDescriptor() != null) { |
|
|
|
if (targetDescriptor.getMapKeyTypeDescriptor() != null) { |
|
|
|
key = state.convertValue(key, targetDescriptor.getMapKeyTypeDescriptor()); |
|
|
|
key = state.convertValue(key, targetDescriptor.getMapKeyTypeDescriptor()); |
|
|
|
} |
|
|
|
} |
|
|
|
this.indexedType = IndexedType.MAP; |
|
|
|
this.indexedType = IndexedType.MAP; |
|
|
|
return new MapIndexingValueRef(state.getTypeConverter(), (Map<?, ?>) targetObject, key, targetDescriptor); |
|
|
|
return new MapIndexingValueRef(state.getTypeConverter(), (Map<?, ?>) target, key, targetDescriptor); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (targetObject == null) { |
|
|
|
|
|
|
|
throw new SpelEvaluationException(getStartPosition(), 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.getClass().isArray() || targetObject instanceof Collection || targetObject instanceof String) { |
|
|
|
if (target.getClass().isArray() || target instanceof Collection || target instanceof String) { |
|
|
|
int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class)); |
|
|
|
int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class)); |
|
|
|
if (targetObject.getClass().isArray()) { |
|
|
|
if (target.getClass().isArray()) { |
|
|
|
this.indexedType = IndexedType.ARRAY; |
|
|
|
this.indexedType = IndexedType.ARRAY; |
|
|
|
return new ArrayIndexingValueRef(state.getTypeConverter(), targetObject, idx, targetDescriptor); |
|
|
|
return new ArrayIndexingValueRef(state.getTypeConverter(), target, idx, targetDescriptor); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (targetObject instanceof Collection) { |
|
|
|
else if (target instanceof Collection) { |
|
|
|
if (targetObject instanceof List) { |
|
|
|
if (target instanceof List) { |
|
|
|
this.indexedType = IndexedType.LIST; |
|
|
|
this.indexedType = IndexedType.LIST; |
|
|
|
} |
|
|
|
} |
|
|
|
return new CollectionIndexingValueRef((Collection<?>) targetObject, idx, targetDescriptor, |
|
|
|
return new CollectionIndexingValueRef((Collection<?>) target, idx, targetDescriptor, |
|
|
|
state.getTypeConverter(), state.getConfiguration().isAutoGrowCollections(), |
|
|
|
state.getTypeConverter(), state.getConfiguration().isAutoGrowCollections(), |
|
|
|
state.getConfiguration().getMaximumAutoGrowSize()); |
|
|
|
state.getConfiguration().getMaximumAutoGrowSize()); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
this.indexedType = IndexedType.STRING; |
|
|
|
this.indexedType = IndexedType.STRING; |
|
|
|
return new StringIndexingLValue((String) targetObject, idx, targetDescriptor); |
|
|
|
return new StringIndexingLValue((String) target, idx, targetDescriptor); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 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 String
|
|
|
|
// TODO: could call the conversion service to convert the value to a String
|
|
|
|
if (String.class == indexValue.getTypeDescriptor().getType()) { |
|
|
|
if (String.class == indexValue.getTypeDescriptor().getType()) { |
|
|
|
this.indexedType = IndexedType.OBJECT; |
|
|
|
this.indexedType = IndexedType.OBJECT; |
|
|
|
return new PropertyIndexingValueRef(targetObject, (String) indexValue.getValue(), |
|
|
|
return new PropertyIndexingValueRef( |
|
|
|
state.getEvaluationContext(), targetDescriptor); |
|
|
|
target, (String) index, state.getEvaluationContext(), targetDescriptor); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, |
|
|
|
throw new SpelEvaluationException( |
|
|
|
targetDescriptor.toString()); |
|
|
|
getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetDescriptor); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
@ -188,12 +188,10 @@ public class Indexer extends SpelNodeImpl { |
|
|
|
return (this.children[0] instanceof PropertyOrFieldReference || this.children[0].isCompilable()); |
|
|
|
return (this.children[0] instanceof PropertyOrFieldReference || this.children[0].isCompilable()); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.indexedType == IndexedType.OBJECT) { |
|
|
|
else if (this.indexedType == IndexedType.OBJECT) { |
|
|
|
// If the string name is changing the accessor is clearly going to change (so compilation is not possible)
|
|
|
|
// If the string name is changing the accessor is clearly going to change (so no compilation possible)
|
|
|
|
if (this.cachedReadAccessor != null && |
|
|
|
return (this.cachedReadAccessor != null && |
|
|
|
(this.cachedReadAccessor instanceof ReflectivePropertyAccessor.OptimalPropertyAccessor) && |
|
|
|
this.cachedReadAccessor instanceof ReflectivePropertyAccessor.OptimalPropertyAccessor && |
|
|
|
(getChild(0) instanceof StringLiteral)) { |
|
|
|
getChild(0) instanceof StringLiteral); |
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -271,7 +269,8 @@ public class Indexer extends SpelNodeImpl { |
|
|
|
this.children[0].generateCode(mv, cf); |
|
|
|
this.children[0].generateCode(mv, cf); |
|
|
|
cf.exitCompilationScope(); |
|
|
|
cf.exitCompilationScope(); |
|
|
|
} |
|
|
|
} |
|
|
|
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); |
|
|
|
mv.visitMethodInsn( |
|
|
|
|
|
|
|
INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
else if (this.indexedType == IndexedType.OBJECT) { |
|
|
|
else if (this.indexedType == IndexedType.OBJECT) { |
|
|
|
@ -526,8 +525,9 @@ public class Indexer extends SpelNodeImpl { |
|
|
|
|
|
|
|
|
|
|
|
private final TypeDescriptor targetObjectTypeDescriptor; |
|
|
|
private final TypeDescriptor targetObjectTypeDescriptor; |
|
|
|
|
|
|
|
|
|
|
|
public PropertyIndexingValueRef(Object targetObject, String value, EvaluationContext evaluationContext, |
|
|
|
public PropertyIndexingValueRef(Object targetObject, String value, |
|
|
|
TypeDescriptor targetObjectTypeDescriptor) { |
|
|
|
EvaluationContext evaluationContext, TypeDescriptor targetObjectTypeDescriptor) { |
|
|
|
|
|
|
|
|
|
|
|
this.targetObject = targetObject; |
|
|
|
this.targetObject = targetObject; |
|
|
|
this.name = value; |
|
|
|
this.name = value; |
|
|
|
this.evaluationContext = evaluationContext; |
|
|
|
this.evaluationContext = evaluationContext; |
|
|
|
@ -569,11 +569,11 @@ public class Indexer extends SpelNodeImpl { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
catch (AccessException ex) { |
|
|
|
catch (AccessException ex) { |
|
|
|
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, |
|
|
|
throw new SpelEvaluationException(getStartPosition(), ex, |
|
|
|
this.targetObjectTypeDescriptor.toString()); |
|
|
|
SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, this.targetObjectTypeDescriptor.toString()); |
|
|
|
} |
|
|
|
} |
|
|
|
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, |
|
|
|
throw new SpelEvaluationException(getStartPosition(), |
|
|
|
this.targetObjectTypeDescriptor.toString()); |
|
|
|
SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, this.targetObjectTypeDescriptor.toString()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
@ -629,11 +629,12 @@ public class Indexer extends SpelNodeImpl { |
|
|
|
|
|
|
|
|
|
|
|
private final int maximumSize; |
|
|
|
private final int maximumSize; |
|
|
|
|
|
|
|
|
|
|
|
public CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryTypeDescriptor, |
|
|
|
public CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryDescriptor, |
|
|
|
TypeConverter typeConverter, boolean growCollection, int maximumSize) { |
|
|
|
TypeConverter typeConverter, boolean growCollection, int maximumSize) { |
|
|
|
|
|
|
|
|
|
|
|
this.collection = collection; |
|
|
|
this.collection = collection; |
|
|
|
this.index = index; |
|
|
|
this.index = index; |
|
|
|
this.collectionEntryDescriptor = collectionEntryTypeDescriptor; |
|
|
|
this.collectionEntryDescriptor = collectionEntryDescriptor; |
|
|
|
this.typeConverter = typeConverter; |
|
|
|
this.typeConverter = typeConverter; |
|
|
|
this.growCollection = growCollection; |
|
|
|
this.growCollection = growCollection; |
|
|
|
this.maximumSize = maximumSize; |
|
|
|
this.maximumSize = maximumSize; |
|
|
|
@ -684,13 +685,14 @@ public class Indexer extends SpelNodeImpl { |
|
|
|
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION); |
|
|
|
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION); |
|
|
|
} |
|
|
|
} |
|
|
|
if (this.collectionEntryDescriptor.getElementTypeDescriptor() == null) { |
|
|
|
if (this.collectionEntryDescriptor.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 = this.collectionEntryDescriptor.getElementTypeDescriptor(); |
|
|
|
TypeDescriptor elementType = this.collectionEntryDescriptor.getElementTypeDescriptor(); |
|
|
|
try { |
|
|
|
try { |
|
|
|
int newElements = this.index - this.collection.size(); |
|
|
|
int newElements = this.index - this.collection.size(); |
|
|
|
while (newElements >= 0) { |
|
|
|
while (newElements >= 0) { |
|
|
|
(this.collection).add(elementType.getType().newInstance()); |
|
|
|
this.collection.add(elementType.getType().newInstance()); |
|
|
|
newElements--; |
|
|
|
newElements--; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|