Browse Source

Mixed polishing along with recent changes

pull/464/head
Juergen Hoeller 12 years ago
parent
commit
14e5a02870
  1. 6
      spring-context/src/main/java/org/springframework/cache/CacheManager.java
  2. 19
      spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java
  3. 451
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
  4. 18
      spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java
  5. 16
      spring-expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java
  6. 28
      spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserDestinationMessageHandler.java
  7. 12
      spring-tx/src/main/java/org/springframework/transaction/interceptor/RollbackRuleAttribute.java
  8. 13
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java
  9. 13
      spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java

6
spring-context/src/main/java/org/springframework/cache/CacheManager.java vendored

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2014 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.
@ -29,13 +29,13 @@ public interface CacheManager {
/** /**
* Return the cache associated with the given name. * Return the cache associated with the given name.
* @param name cache identifier (must not be {@code null}) * @param name cache identifier (must not be {@code null})
* @return associated cache, or {@code null} if none is found * @return the associated cache, or {@code null} if none is found
*/ */
Cache getCache(String name); Cache getCache(String name);
/** /**
* Return a collection of the caches known by this cache manager. * Return a collection of the caches known by this cache manager.
* @return names of caches known by the cache manager. * @return names of caches known by the cache manager
*/ */
Collection<String> getCacheNames(); Collection<String> getCacheNames();

19
spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 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.
@ -30,9 +30,10 @@ import org.springframework.expression.spel.SpelEvaluationException;
public class CompoundExpression extends SpelNodeImpl { public class CompoundExpression extends SpelNodeImpl {
public CompoundExpression(int pos,SpelNodeImpl... expressionComponents) { public CompoundExpression(int pos,SpelNodeImpl... expressionComponents) {
super(pos,expressionComponents); super(pos, expressionComponents);
if (expressionComponents.length<2) { if (expressionComponents.length < 2) {
throw new IllegalStateException("Dont build compound expression less than one entry: "+expressionComponents.length); throw new IllegalStateException("Do not build compound expression less than one entry: " +
expressionComponents.length);
} }
} }
@ -42,11 +43,9 @@ public class CompoundExpression extends SpelNodeImpl {
if (getChildCount() == 1) { if (getChildCount() == 1) {
return this.children[0].getValueRef(state); return this.children[0].getValueRef(state);
} }
TypedValue result = null; SpelNodeImpl nextNode = this.children[0];
SpelNodeImpl nextNode = null;
try { try {
nextNode = this.children[0]; TypedValue result = nextNode.getValueInternal(state);
result = nextNode.getValueInternal(state);
int cc = getChildCount(); int cc = getChildCount();
for (int i = 1; i < cc - 1; i++) { for (int i = 1; i < cc - 1; i++) {
try { try {
@ -75,8 +74,8 @@ public class CompoundExpression extends SpelNodeImpl {
} }
/** /**
* Evaluates a compound expression. This involves evaluating each piece in turn and the return value from each piece * Evaluates a compound expression. This involves evaluating each piece in turn and the
* is the active context object for the subsequent piece. * return value from each piece is the active context object for the subsequent piece.
* @param state the state in which the expression is being evaluated * @param state the state in which the expression is being evaluated
* @return the final value from the last piece of the compound expression * @return the final value from the last piece of the compound expression
*/ */

451
spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 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.
@ -34,7 +34,7 @@ import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
/** /**
* An Indexer can index into some proceeding structure to access a particular piece of it. * An Indexer can index into some proceeding structure to access a particular piece of it.
* Supported structures are: strings/collections (lists/sets)/arrays * Supported structures are: strings / collections (lists/sets) / arrays.
* *
* @author Andy Clement * @author Andy Clement
* @author Phillip Webb * @author Phillip Webb
@ -88,6 +88,206 @@ public class Indexer extends SpelNodeImpl {
} }
@Override
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
TypedValue context = state.getActiveContextObject();
Object targetObject = context.getValue();
TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor();
TypedValue indexValue = null;
Object index = null;
// This first part of the if clause prevents a 'double dereference' of
// the property (SPR-5847)
if (targetObject instanceof Map && (this.children[0] instanceof PropertyOrFieldReference)) {
PropertyOrFieldReference reference = (PropertyOrFieldReference) this.children[0];
index = reference.getName();
indexValue = new TypedValue(index);
}
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 = this.children[0].getValueInternal(state);
index = indexValue.getValue();
}
finally {
state.popActiveContextObject();
}
}
// Indexing into a Map
if (targetObject instanceof Map) {
Object key = index;
if (targetObjectTypeDescriptor.getMapKeyTypeDescriptor() != null) {
key = state.convertValue(key, targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
}
return new MapIndexingValueRef(state.getTypeConverter(), (Map<?, ?>) targetObject, key,
targetObjectTypeDescriptor);
}
if (targetObject == null) {
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.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(),
state.getConfiguration().getMaximumAutoGrowSize());
}
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
if (indexValue.getTypeDescriptor().getType() == String.class) {
return new PropertyIndexingValueRef(targetObject, (String) indexValue.getValue(),
state.getEvaluationContext(), targetObjectTypeDescriptor);
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
targetObjectTypeDescriptor.toString());
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < getChildCount(); i++) {
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<?> arrayComponentType) throws EvaluationException {
if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Integer) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Integer.class));
}
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Boolean) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Boolean.class));
}
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Character) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Character.class));
}
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Long) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Long.class));
}
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Short) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Short.class));
}
else if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Double) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Double.class));
}
else if (arrayComponentType == Float.TYPE) {
float[] array = (float[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Float) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Float.class));
}
else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Byte) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Byte.class));
}
else {
Object[] array = (Object[]) ctx;
checkAccess(array.length, idx);
array[idx] = converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(arrayComponentType));
}
}
private Object accessArrayElement(Object ctx, int idx) throws SpelEvaluationException {
Class<?> arrayComponentType = ctx.getClass().getComponentType();
if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Float.TYPE) {
float[] array = (float[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else {
Object[] array = (Object[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
}
private void checkAccess(int arrayLength, int index) throws SpelEvaluationException {
if (index > arrayLength) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.ARRAY_INDEX_OUT_OF_BOUNDS,
arrayLength, index);
}
}
private class ArrayIndexingValueRef implements ValueRef { private class ArrayIndexingValueRef implements ValueRef {
private final TypeConverter typeConverter; private final TypeConverter typeConverter;
@ -127,7 +327,7 @@ public class Indexer extends SpelNodeImpl {
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
private class MapIndexingValueRef implements ValueRef { private static class MapIndexingValueRef implements ValueRef {
private final TypeConverter typeConverter; private final TypeConverter typeConverter;
@ -137,16 +337,13 @@ public class Indexer extends SpelNodeImpl {
private final TypeDescriptor mapEntryTypeDescriptor; private final TypeDescriptor mapEntryTypeDescriptor;
public MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryTypeDescriptor) {
MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key,
TypeDescriptor mapEntryTypeDescriptor) {
this.typeConverter = typeConverter; this.typeConverter = typeConverter;
this.map = map; this.map = map;
this.key = key; this.key = key;
this.mapEntryTypeDescriptor = mapEntryTypeDescriptor; this.mapEntryTypeDescriptor = mapEntryTypeDescriptor;
} }
@Override @Override
public TypedValue getValue() { public TypedValue getValue() {
Object value = this.map.get(this.key); Object value = this.map.get(this.key);
@ -180,7 +377,6 @@ 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, EvaluationContext evaluationContext,
TypeDescriptor targetObjectTypeDescriptor) { TypeDescriptor targetObjectTypeDescriptor) {
this.targetObject = targetObject; this.targetObject = targetObject;
@ -194,15 +390,14 @@ public class Indexer extends SpelNodeImpl {
public TypedValue getValue() { public TypedValue getValue() {
Class<?> targetObjectRuntimeClass = getObjectClass(this.targetObject); Class<?> targetObjectRuntimeClass = getObjectClass(this.targetObject);
try { try {
if (Indexer.this.cachedReadName != null && Indexer.this.cachedReadName.equals(this.name) && Indexer.this.cachedReadTargetType != null && if (Indexer.this.cachedReadName != null && Indexer.this.cachedReadName.equals(this.name) &&
Indexer.this.cachedReadTargetType != null &&
Indexer.this.cachedReadTargetType.equals(targetObjectRuntimeClass)) { Indexer.this.cachedReadTargetType.equals(targetObjectRuntimeClass)) {
// it is OK to use the cached accessor // It is OK to use the cached accessor
return Indexer.this.cachedReadAccessor.read(this.evaluationContext, this.targetObject, this.name); return Indexer.this.cachedReadAccessor.read(this.evaluationContext, this.targetObject, this.name);
} }
List<PropertyAccessor> accessorsToTry = AstUtils.getPropertyAccessorsToTry( List<PropertyAccessor> accessorsToTry = AstUtils.getPropertyAccessorsToTry(
targetObjectRuntimeClass, this.evaluationContext.getPropertyAccessors()); targetObjectRuntimeClass, this.evaluationContext.getPropertyAccessors());
if (accessorsToTry != null) { if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) { for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canRead(this.evaluationContext, this.targetObject, this.name)) { if (accessor.canRead(this.evaluationContext, this.targetObject, this.name)) {
@ -230,9 +425,10 @@ public class Indexer extends SpelNodeImpl {
public void setValue(Object newValue) { public void setValue(Object newValue) {
Class<?> contextObjectClass = getObjectClass(this.targetObject); Class<?> contextObjectClass = getObjectClass(this.targetObject);
try { try {
if (Indexer.this.cachedWriteName != null && Indexer.this.cachedWriteName.equals(this.name) && Indexer.this.cachedWriteTargetType != null && if (Indexer.this.cachedWriteName != null && Indexer.this.cachedWriteName.equals(this.name) &&
Indexer.this.cachedWriteTargetType != null &&
Indexer.this.cachedWriteTargetType.equals(contextObjectClass)) { Indexer.this.cachedWriteTargetType.equals(contextObjectClass)) {
// it is OK to use the cached accessor // It is OK to use the cached accessor
Indexer.this.cachedWriteAccessor.write(this.evaluationContext, this.targetObject, this.name, newValue); Indexer.this.cachedWriteAccessor.write(this.evaluationContext, this.targetObject, this.name, newValue);
return; return;
} }
@ -270,7 +466,7 @@ public class Indexer extends SpelNodeImpl {
private final int index; private final int index;
private final TypeDescriptor collectionEntryTypeDescriptor; private final TypeDescriptor collectionEntryDescriptor;
private final TypeConverter typeConverter; private final TypeConverter typeConverter;
@ -278,12 +474,11 @@ public class Indexer extends SpelNodeImpl {
private final int maximumSize; private final int maximumSize;
public CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryTypeDescriptor,
CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryTypeDescriptor,
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.collectionEntryTypeDescriptor = collectionEntryTypeDescriptor; this.collectionEntryDescriptor = collectionEntryTypeDescriptor;
this.typeConverter = typeConverter; this.typeConverter = typeConverter;
this.growCollection = growCollection; this.growCollection = growCollection;
this.maximumSize = maximumSize; this.maximumSize = maximumSize;
@ -295,12 +490,12 @@ public class Indexer extends SpelNodeImpl {
growCollectionIfNecessary(); growCollectionIfNecessary();
if (this.collection instanceof List) { if (this.collection instanceof List) {
Object o = ((List) this.collection).get(this.index); Object o = ((List) this.collection).get(this.index);
return new TypedValue(o, this.collectionEntryTypeDescriptor.elementTypeDescriptor(o)); return new TypedValue(o, this.collectionEntryDescriptor.elementTypeDescriptor(o));
} }
int pos = 0; int pos = 0;
for (Object o : this.collection) { for (Object o : this.collection) {
if (pos == this.index) { if (pos == this.index) {
return new TypedValue(o, this.collectionEntryTypeDescriptor.elementTypeDescriptor(o)); return new TypedValue(o, this.collectionEntryDescriptor.elementTypeDescriptor(o));
} }
pos++; pos++;
} }
@ -312,35 +507,31 @@ public class Indexer extends SpelNodeImpl {
growCollectionIfNecessary(); growCollectionIfNecessary();
if (this.collection instanceof List) { if (this.collection instanceof List) {
List list = (List) this.collection; List list = (List) this.collection;
if (this.collectionEntryTypeDescriptor.getElementTypeDescriptor() != null) { if (this.collectionEntryDescriptor.getElementTypeDescriptor() != null) {
newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue), newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue),
this.collectionEntryTypeDescriptor.getElementTypeDescriptor()); this.collectionEntryDescriptor.getElementTypeDescriptor());
} }
list.set(this.index, newValue); list.set(this.index, newValue);
} }
else { else {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
this.collectionEntryTypeDescriptor.toString()); this.collectionEntryDescriptor.toString());
} }
} }
private void growCollectionIfNecessary() { private void growCollectionIfNecessary() {
if (this.index >= this.collection.size()) { if (this.index >= this.collection.size()) {
if (!this.growCollection) { if (!this.growCollection) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS,
this.collection.size(), this.index); this.collection.size(), this.index);
} }
if(this.index >= this.maximumSize) { if(this.index >= this.maximumSize) {
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.collectionEntryTypeDescriptor.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.collectionEntryTypeDescriptor.getElementTypeDescriptor();
try { try {
int newElements = this.index - this.collection.size(); int newElements = this.index - this.collection.size();
while (newElements >= 0) { while (newElements >= 0) {
@ -369,14 +560,12 @@ public class Indexer extends SpelNodeImpl {
private final TypeDescriptor typeDescriptor; private final TypeDescriptor typeDescriptor;
public StringIndexingLValue(String target, int index, TypeDescriptor typeDescriptor) { public StringIndexingLValue(String target, int index, TypeDescriptor typeDescriptor) {
this.target = target; this.target = target;
this.index = index; this.index = index;
this.typeDescriptor = typeDescriptor; this.typeDescriptor = typeDescriptor;
} }
@Override @Override
public TypedValue getValue() { public TypedValue getValue() {
if (this.index >= this.target.length()) { if (this.index >= this.target.length()) {
@ -398,204 +587,4 @@ public class Indexer extends SpelNodeImpl {
} }
} }
@Override
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
TypedValue context = state.getActiveContextObject();
Object targetObject = context.getValue();
TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor();
TypedValue indexValue = null;
Object index = null;
// This first part of the if clause prevents a 'double dereference' of
// the property (SPR-5847)
if (targetObject instanceof Map && (this.children[0] instanceof PropertyOrFieldReference)) {
PropertyOrFieldReference reference = (PropertyOrFieldReference) this.children[0];
index = reference.getName();
indexValue = new TypedValue(index);
}
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 = this.children[0].getValueInternal(state);
index = indexValue.getValue();
}
finally {
state.popActiveContextObject();
}
}
// Indexing into a Map
if (targetObject instanceof Map) {
Object key = index;
if (targetObjectTypeDescriptor.getMapKeyTypeDescriptor() != null) {
key = state.convertValue(key, targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
}
return new MapIndexingValueRef(state.getTypeConverter(), (Map<?, ?>) targetObject, key,
targetObjectTypeDescriptor);
}
if (targetObject == null) {
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.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(),
state.getConfiguration().getMaximumAutoGrowSize());
}
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
if (indexValue.getTypeDescriptor().getType() == String.class) {
return new PropertyIndexingValueRef(targetObject, (String) indexValue.getValue(),
state.getEvaluationContext(), targetObjectTypeDescriptor);
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
targetObjectTypeDescriptor.toString());
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < getChildCount(); i++) {
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 {
Class<?> arrayComponentType = clazz;
if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Integer) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Integer.class));
}
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Boolean) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Boolean.class));
}
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Character) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Character.class));
}
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Long) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Long.class));
}
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Short) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Short.class));
}
else if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Double) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Double.class));
}
else if (arrayComponentType == Float.TYPE) {
float[] array = (float[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Float) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Float.class));
}
else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx;
checkAccess(array.length, idx);
array[idx] = (Byte) converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(Byte.class));
}
else {
Object[] array = (Object[]) ctx;
checkAccess(array.length, idx);
array[idx] = converter.convertValue(newValue, TypeDescriptor.forObject(newValue),
TypeDescriptor.valueOf(clazz));
}
}
private Object accessArrayElement(Object ctx, int idx) throws SpelEvaluationException {
Class<?> arrayComponentType = ctx.getClass().getComponentType();
if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Float.TYPE) {
float[] array = (float[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
else {
Object[] array = (Object[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
}
private void checkAccess(int arrayLength, int index) throws SpelEvaluationException {
if (index > arrayLength) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.ARRAY_INDEX_OUT_OF_BOUNDS,
arrayLength, index);
}
}
} }

18
spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 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.
@ -37,6 +37,7 @@ public abstract class SpelNodeImpl implements SpelNode {
private static SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0]; private static SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];
protected int pos; // start = top 16bits, end = bottom 16bits protected int pos; // start = top 16bits, end = bottom 16bits
protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN; protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN;
@ -152,22 +153,21 @@ public abstract class SpelNodeImpl implements SpelNode {
return ExpressionUtils.convertTypedValue(state.getEvaluationContext(), getValueInternal(state), desiredReturnType); return ExpressionUtils.convertTypedValue(state.getEvaluationContext(), getValueInternal(state), desiredReturnType);
} }
public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;
@Override
public abstract String toStringAST();
@Override @Override
public int getStartPosition() { public int getStartPosition() {
return (this.pos>>16); return (this.pos >> 16);
} }
@Override @Override
public int getEndPosition() { public int getEndPosition() {
return (this.pos&0xffff); return (this.pos & 0xffff);
} }
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
throw new SpelEvaluationException(this.pos,SpelMessage.NOT_ASSIGNABLE,toStringAST()); throw new SpelEvaluationException(this.pos, SpelMessage.NOT_ASSIGNABLE, toStringAST());
} }
public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;
} }

16
spring-expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java

@ -81,9 +81,19 @@ public class MapAccessTests extends AbstractExpressionTests {
Object bean = new TestBean("name1", new TestBean("name2", null, "Description 2", 15, props1), "description 1", 6, props1); Object bean = new TestBean("name1", new TestBean("name2", null, "Description 2", 15, props1), "description 1", 6, props1);
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("testBean.properties['key2']"); Expression expr = parser.parseExpression("testBean.properties['key2']");
String key = (String) exp.getValue(bean); assertEquals("value2", expr.getValue(bean));
assertNotNull(key); }
@Test
public void testGetValueFromRootMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("key", "value");
EvaluationContext context = new StandardEvaluationContext(map);
ExpressionParser spelExpressionParser = new SpelExpressionParser();
Expression expr = spelExpressionParser.parseExpression("#root['key']");
assertEquals("value", expr.getValue(map));
} }

28
spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserDestinationMessageHandler.java

@ -31,9 +31,7 @@ import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageType; import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.support.MessageBuilder; import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/** /**
* Provides support for messages sent to "user" destinations, translating the * Provides support for messages sent to "user" destinations, translating the
@ -60,7 +58,7 @@ public class UserDestinationMessageHandler implements MessageHandler, SmartLifec
private final UserDestinationResolver userDestinationResolver; private final UserDestinationResolver userDestinationResolver;
private Object lifecycleMonitor = new Object(); private final Object lifecycleMonitor = new Object();
private volatile boolean running = false; private volatile boolean running = false;
@ -85,12 +83,6 @@ public class UserDestinationMessageHandler implements MessageHandler, SmartLifec
this.userDestinationResolver = userDestinationResolver; this.userDestinationResolver = userDestinationResolver;
} }
/**
* Return the configured {@link UserDestinationResolver}.
*/
public UserDestinationResolver getUserDestinationResolver() {
return this.userDestinationResolver;
}
/** /**
* Return the configured messaging template for sending messages with * Return the configured messaging template for sending messages with
@ -100,16 +92,24 @@ public class UserDestinationMessageHandler implements MessageHandler, SmartLifec
return this.brokerMessagingTemplate; return this.brokerMessagingTemplate;
} }
@Override /**
public boolean isAutoStartup() { * Return the configured {@link UserDestinationResolver}.
return true; */
public UserDestinationResolver getUserDestinationResolver() {
return this.userDestinationResolver;
} }
@Override @Override
public int getPhase() { public int getPhase() {
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
} }
@Override
public boolean isAutoStartup() {
return true;
}
@Override @Override
public final boolean isRunning() { public final boolean isRunning() {
synchronized (this.lifecycleMonitor) { synchronized (this.lifecycleMonitor) {
@ -143,9 +143,9 @@ public class UserDestinationMessageHandler implements MessageHandler, SmartLifec
} }
} }
@Override @Override
public void handleMessage(Message<?> message) throws MessagingException { public void handleMessage(Message<?> message) throws MessagingException {
UserDestinationResult result = this.userDestinationResolver.resolveDestination(message); UserDestinationResult result = this.userDestinationResolver.resolveDestination(message);
if (result == null) { if (result == null) {
return; return;
@ -154,13 +154,11 @@ public class UserDestinationMessageHandler implements MessageHandler, SmartLifec
if (destinations.isEmpty()) { if (destinations.isEmpty()) {
return; return;
} }
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message); SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message);
if (SimpMessageType.MESSAGE.equals(headerAccessor.getMessageType())) { if (SimpMessageType.MESSAGE.equals(headerAccessor.getMessageType())) {
headerAccessor.setHeader(SUBSCRIBE_DESTINATION, result.getSubscribeDestination()); headerAccessor.setHeader(SUBSCRIBE_DESTINATION, result.getSubscribeDestination());
message = MessageBuilder.withPayload(message.getPayload()).setHeaders(headerAccessor).build(); message = MessageBuilder.withPayload(message.getPayload()).setHeaders(headerAccessor).build();
} }
for (String targetDestination : destinations) { for (String targetDestination : destinations) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Sending message to resolved destination=" + targetDestination); logger.debug("Sending message to resolved destination=" + targetDestination);

12
spring-tx/src/main/java/org/springframework/transaction/interceptor/RollbackRuleAttribute.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2014 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.
@ -16,10 +16,10 @@
package org.springframework.transaction.interceptor; package org.springframework.transaction.interceptor;
import org.springframework.util.Assert;
import java.io.Serializable; import java.io.Serializable;
import org.springframework.util.Assert;
/** /**
* Rule determining whether or not a given exception (and any subclasses) * Rule determining whether or not a given exception (and any subclasses)
* should cause a rollback. * should cause a rollback.
@ -60,7 +60,7 @@ public class RollbackRuleAttribute implements Serializable{
* not a {@code Throwable} type or is {@code null} * not a {@code Throwable} type or is {@code null}
*/ */
public RollbackRuleAttribute(Class<?> clazz) { public RollbackRuleAttribute(Class<?> clazz) {
Assert.notNull(clazz, "'clazz' cannot be null."); Assert.notNull(clazz, "'clazz' cannot be null");
if (!Throwable.class.isAssignableFrom(clazz)) { if (!Throwable.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Cannot construct rollback rule from [" + clazz.getName() + "]: it's not a Throwable"); "Cannot construct rollback rule from [" + clazz.getName() + "]: it's not a Throwable");
@ -87,7 +87,7 @@ public class RollbackRuleAttribute implements Serializable{
* {@code exceptionName} is {@code null} or empty * {@code exceptionName} is {@code null} or empty
*/ */
public RollbackRuleAttribute(String exceptionName) { public RollbackRuleAttribute(String exceptionName) {
Assert.hasText(exceptionName, "'exceptionName' cannot be null or empty."); Assert.hasText(exceptionName, "'exceptionName' cannot be null or empty");
this.exceptionName = exceptionName; this.exceptionName = exceptionName;
} }
@ -111,7 +111,7 @@ public class RollbackRuleAttribute implements Serializable{
private int getDepth(Class<?> exceptionClass, int depth) { private int getDepth(Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().indexOf(this.exceptionName) != -1) { if (exceptionClass.getName().contains(this.exceptionName)) {
// Found it! // Found it!
return depth; return depth;
} }

13
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java

@ -19,7 +19,6 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.lang.reflect.Method;
import java.security.Principal; import java.security.Principal;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Locale; import java.util.Locale;
@ -101,6 +100,9 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
else if (HttpSession.class.isAssignableFrom(paramType)) { else if (HttpSession.class.isAssignableFrom(paramType)) {
return request.getSession(); return request.getSession();
} }
else if (HttpMethod.class.equals(paramType)) {
return ((ServletWebRequest) webRequest).getHttpMethod();
}
else if (Principal.class.isAssignableFrom(paramType)) { else if (Principal.class.isAssignableFrom(paramType)) {
return request.getUserPrincipal(); return request.getUserPrincipal();
} }
@ -120,13 +122,10 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
else if (Reader.class.isAssignableFrom(paramType)) { else if (Reader.class.isAssignableFrom(paramType)) {
return request.getReader(); return request.getReader();
} }
else if (HttpMethod.class.equals(paramType)) {
return ((ServletWebRequest) webRequest).getHttpMethod();
}
else { else {
// should never happen.. // should never happen...
Method method = parameter.getMethod(); throw new UnsupportedOperationException(
throw new UnsupportedOperationException("Unknown parameter type: " + paramType + " in method: " + method); "Unknown parameter type: " + paramType + " in method: " + parameter.getMethod());
} }
} }

13
spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java

@ -18,7 +18,6 @@ package org.springframework.web.servlet.tags.form;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Map; import java.util.Map;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -424,7 +423,6 @@ public class FormTag extends AbstractHtmlElementTag {
* with the context and servlet paths, and the result is used. Otherwise, the * with the context and servlet paths, and the result is used. Otherwise, the
* {@link org.springframework.web.servlet.support.RequestContext#getRequestUri() * {@link org.springframework.web.servlet.support.RequestContext#getRequestUri()
* originating URI} is used. * originating URI} is used.
*
* @return the value that is to be used for the '{@code action}' attribute * @return the value that is to be used for the '{@code action}' attribute
*/ */
protected String resolveAction() throws JspException { protected String resolveAction() throws JspException {
@ -436,7 +434,8 @@ public class FormTag extends AbstractHtmlElementTag {
} }
else if (StringUtils.hasText(servletRelativeAction)) { else if (StringUtils.hasText(servletRelativeAction)) {
String pathToServlet = getRequestContext().getPathToServlet(); String pathToServlet = getRequestContext().getPathToServlet();
if (servletRelativeAction.startsWith("/") && !servletRelativeAction.startsWith(getRequestContext().getContextPath())) { if (servletRelativeAction.startsWith("/") &&
!servletRelativeAction.startsWith(getRequestContext().getContextPath())) {
servletRelativeAction = pathToServlet + servletRelativeAction; servletRelativeAction = pathToServlet + servletRelativeAction;
} }
servletRelativeAction = getDisplayString(evaluate(ACTION_ATTRIBUTE, servletRelativeAction)); servletRelativeAction = getDisplayString(evaluate(ACTION_ATTRIBUTE, servletRelativeAction));
@ -444,12 +443,12 @@ public class FormTag extends AbstractHtmlElementTag {
} }
else { else {
String requestUri = getRequestContext().getRequestUri(); String requestUri = getRequestContext().getRequestUri();
String encoding = pageContext.getResponse().getCharacterEncoding(); String encoding = this.pageContext.getResponse().getCharacterEncoding();
try { try {
requestUri = UriUtils.encodePath(requestUri, encoding); requestUri = UriUtils.encodePath(requestUri, encoding);
} }
catch (UnsupportedEncodingException e) { catch (UnsupportedEncodingException ex) {
throw new JspException(e); // shouldn't happen - if it does, proceed with requestUri as-is
} }
ServletResponse response = this.pageContext.getResponse(); ServletResponse response = this.pageContext.getResponse();
if (response instanceof HttpServletResponse) { if (response instanceof HttpServletResponse) {
@ -476,7 +475,7 @@ public class FormTag extends AbstractHtmlElementTag {
private String processAction(String action) { private String processAction(String action) {
RequestDataValueProcessor processor = getRequestContext().getRequestDataValueProcessor(); RequestDataValueProcessor processor = getRequestContext().getRequestDataValueProcessor();
ServletRequest request = this.pageContext.getRequest(); ServletRequest request = this.pageContext.getRequest();
if ((processor != null) && (request instanceof HttpServletRequest)) { if (processor != null && request instanceof HttpServletRequest) {
action = processor.processAction((HttpServletRequest) request, action, getHttpMethod()); action = processor.processAction((HttpServletRequest) request, action, getHttpMethod());
} }
return action; return action;

Loading…
Cancel
Save