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"),//
|
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(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_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;
|
private Kind kind;
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,16 @@ public class Indexer extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indexing into a Map
|
// 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;
|
Object possiblyConvertedKey = index;
|
||||||
if (targetObjectTypeDescriptor.isMapEntryTypeKnown()) {
|
if (targetObjectTypeDescriptor.isMapEntryTypeKnown()) {
|
||||||
possiblyConvertedKey = state.convertValue(index,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapKeyType()));
|
possiblyConvertedKey = state.convertValue(index,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getMapKeyType()));
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.expression.spel.ast;
|
package org.springframework.expression.spel.ast;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
@ -56,18 +57,50 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||||
@Override
|
@Override
|
||||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||||
TypedValue result = readProperty(state, this.name);
|
TypedValue result = readProperty(state, this.name);
|
||||||
if (result.getValue()==null && state.configuredToCreateCollection() && result.getTypeDescriptor().getType().equals(List.class) && nextChildIs(Indexer.class)) {
|
TypeDescriptor resultDescriptor = result.getTypeDescriptor();
|
||||||
// Create a new list ready for the indexer
|
// Dynamically create the objects if the user has requested that optional behaviour
|
||||||
try {
|
if (result.getValue()==null && state.configuredToCreateCollectionOrMap() && nextChildIs(Indexer.class,PropertyOrFieldReference.class)) {
|
||||||
if (isWritable(state)) {
|
// Creating lists and maps
|
||||||
List newList = ArrayList.class.newInstance();
|
if ((resultDescriptor.getType().equals(List.class) || resultDescriptor.getType().equals(Map.class))) {
|
||||||
writeProperty(state, name, newList);
|
// Create a new collection or map ready for the indexer
|
||||||
result = readProperty(state, this.name);
|
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) {
|
} else {
|
||||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
|
// 'simple' object
|
||||||
} catch (IllegalAccessException e) {
|
try {
|
||||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
|
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;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -62,12 +62,25 @@ public abstract class SpelNodeImpl implements SpelNode, CommonTypeDescriptors {
|
||||||
return result;
|
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) {
|
if (parent!=null) {
|
||||||
SpelNodeImpl[] peers = parent.children;
|
SpelNodeImpl[] peers = parent.children;
|
||||||
for (int i=0,max=peers.length;i<max;i++) {
|
for (int i=0,max=peers.length;i<max;i++) {
|
||||||
if (peers[i]==this) {
|
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 {
|
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;
|
static final int GrowListsOnIndexBeyondSize = 0x0002;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue