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.
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1513 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
85a197f5b8
commit
4b94f3b381
|
|
@ -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