for Keith and his binder: fault in support for maps (if attempt made to reference into null map) and for general objects, e.g. foo.bar if foo was null, attempt to dynamically build foo.
This commit is contained in:
parent
85eda27cd4
commit
e3e34b04d4
|
|
@ -90,7 +90,9 @@ public enum SpelMessage {
|
|||
RUN_OUT_OF_ARGUMENTS(Kind.ERROR,1051,"Unexpected ran out of arguments"),//
|
||||
UNABLE_TO_GROW_COLLECTION(Kind.ERROR,1052,"Unable to grow collection"),//
|
||||
UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE(Kind.ERROR,1053,"Unable to grow collection: unable to determine list element type"),//
|
||||
UNABLE_TO_CREATE_LIST_FOR_INDEXING(Kind.ERROR,1054,"Unable to create a List for the following indexer"),//
|
||||
UNABLE_TO_CREATE_LIST_FOR_INDEXING(Kind.ERROR,1054,"Unable to dynamically create a List to replace a null value"),//
|
||||
UNABLE_TO_CREATE_MAP_FOR_INDEXING(Kind.ERROR,1055,"Unable to dynamically create a Map to replace a null value"),//
|
||||
UNABLE_TO_DYNAMICALLY_CREATE_OBJECT(Kind.ERROR,1056,"Unable to dynamically create instance of ''{0}'' to replace a null value"),//
|
||||
;
|
||||
|
||||
private Kind kind;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,16 @@ public class Indexer extends SpelNodeImpl {
|
|||
}
|
||||
|
||||
// Indexing into a Map
|
||||
if (targetObject instanceof Map) {
|
||||
if (targetObjectTypeDescriptor.isMap()) {
|
||||
if (targetObject == null) {
|
||||
// Current decision: attempt to index into null map == exception and does not just return null
|
||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
|
||||
// if (targetObjectTypeDescriptor.isMapEntryTypeKnown()) {
|
||||
// return new TypedValue(null,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapValueType()));
|
||||
// } else {
|
||||
// return new TypedValue(null,TypeDescriptor.NULL);
|
||||
// }
|
||||
}
|
||||
Object possiblyConvertedKey = index;
|
||||
if (targetObjectTypeDescriptor.isMapEntryTypeKnown()) {
|
||||
possiblyConvertedKey = state.convertValue(index,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapKeyType()));
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -56,18 +57,50 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
|||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue result = readProperty(state, this.name);
|
||||
if (result.getValue()==null && state.configuredToCreateCollection() && result.getTypeDescriptor().getType().equals(List.class) && nextChildIs(Indexer.class)) {
|
||||
// Create a new list ready for the indexer
|
||||
try {
|
||||
if (isWritable(state)) {
|
||||
List newList = ArrayList.class.newInstance();
|
||||
writeProperty(state, name, newList);
|
||||
result = readProperty(state, this.name);
|
||||
TypeDescriptor resultDescriptor = result.getTypeDescriptor();
|
||||
// Dynamically create the objects if the user has requested that optional behaviour
|
||||
if (result.getValue()==null && state.configuredToCreateCollectionOrMap() && nextChildIs(Indexer.class,PropertyOrFieldReference.class)) {
|
||||
// Creating lists and maps
|
||||
if ((resultDescriptor.getType().equals(List.class) || resultDescriptor.getType().equals(Map.class))) {
|
||||
// Create a new collection or map ready for the indexer
|
||||
if (resultDescriptor.getType().equals(List.class)) {
|
||||
try {
|
||||
if (isWritable(state)) {
|
||||
List newList = ArrayList.class.newInstance();
|
||||
writeProperty(state, name, newList);
|
||||
result = readProperty(state, this.name);
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (isWritable(state)) {
|
||||
Map newMap = HashMap.class.newInstance();
|
||||
writeProperty(state, name, newMap);
|
||||
result = readProperty(state, this.name);
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_CREATE_MAP_FOR_INDEXING);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_CREATE_MAP_FOR_INDEXING);
|
||||
}
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
|
||||
} else {
|
||||
// 'simple' object
|
||||
try {
|
||||
if (isWritable(state)) {
|
||||
Object newObject = result.getTypeDescriptor().getType().newInstance();
|
||||
writeProperty(state, name, newObject);
|
||||
result = readProperty(state, this.name);
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT,result.getTypeDescriptor().getType());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT,result.getTypeDescriptor().getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -62,12 +62,25 @@ public abstract class SpelNodeImpl implements SpelNode, CommonTypeDescriptors {
|
|||
return result;
|
||||
}
|
||||
|
||||
protected boolean nextChildIs(Class clazz) {
|
||||
/**
|
||||
* @return true if the next child is one of the specified classes
|
||||
*/
|
||||
protected boolean nextChildIs(Class... clazzes) {
|
||||
if (parent!=null) {
|
||||
SpelNodeImpl[] peers = parent.children;
|
||||
for (int i=0,max=peers.length;i<max;i++) {
|
||||
if (peers[i]==this) {
|
||||
return (i+1)<max && peers[i+1].getClass().equals(clazz);
|
||||
if ((i+1)>=max) {
|
||||
return false;
|
||||
} else {
|
||||
Class clazz = peers[i+1].getClass();
|
||||
for (Class desiredClazz: clazzes) {
|
||||
if (clazz.equals(desiredClazz)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,13 @@ package org.springframework.expression.spel.standard;
|
|||
*/
|
||||
public interface SpelExpressionParserConfiguration {
|
||||
|
||||
static final int CreateListsOnAttemptToIndexIntoNull = 0x0001;
|
||||
/**
|
||||
* This option applies to maps/collections and regular objects. If the initial part of an expression evaluates to null and then an
|
||||
* attempt is made to resolve an index '[]' or property against it, and this option is set, then the relevant object will be constructed so that
|
||||
* the index/property resolution can proceed.
|
||||
*/
|
||||
static final int CreateObjectIfAttemptToReferenceNull = 0x0001;
|
||||
|
||||
static final int GrowListsOnIndexBeyondSize = 0x0002;
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue