From 14e5a02870f9ef90df6171c63ee23d0db5c29809 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 14 Feb 2014 21:39:40 +0100 Subject: [PATCH] Mixed polishing along with recent changes --- .../springframework/cache/CacheManager.java | 6 +- .../spel/ast/CompoundExpression.java | 19 +- .../expression/spel/ast/Indexer.java | 623 +++++++++--------- .../expression/spel/ast/SpelNodeImpl.java | 18 +- .../expression/spel/MapAccessTests.java | 16 +- .../user/UserDestinationMessageHandler.java | 28 +- .../interceptor/RollbackRuleAttribute.java | 12 +- .../ServletRequestMethodArgumentResolver.java | 13 +- .../web/servlet/tags/form/FormTag.java | 13 +- 9 files changed, 371 insertions(+), 377 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/CacheManager.java b/spring-context/src/main/java/org/springframework/cache/CacheManager.java index 86228014eac..2f1c82ec45f 100644 --- a/spring-context/src/main/java/org/springframework/cache/CacheManager.java +++ b/spring-context/src/main/java/org/springframework/cache/CacheManager.java @@ -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"); * 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. * @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); /** * 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 getCacheNames(); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java index 688670c1428..1ea345b582d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java +++ b/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"); * 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 CompoundExpression(int pos,SpelNodeImpl... expressionComponents) { - super(pos,expressionComponents); - if (expressionComponents.length<2) { - throw new IllegalStateException("Dont build compound expression less than one entry: "+expressionComponents.length); + super(pos, expressionComponents); + if (expressionComponents.length < 2) { + 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) { return this.children[0].getValueRef(state); } - TypedValue result = null; - SpelNodeImpl nextNode = null; + SpelNodeImpl nextNode = this.children[0]; try { - nextNode = this.children[0]; - result = nextNode.getValueInternal(state); + TypedValue result = nextNode.getValueInternal(state); int cc = getChildCount(); for (int i = 1; i < cc - 1; i++) { 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 - * is the active context object for the subsequent piece. + * Evaluates a compound expression. This involves evaluating each piece in turn and the + * 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 * @return the final value from the last piece of the compound expression */ diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java index 82e7db4259f..a65f3d5e02f 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java +++ b/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"); * 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. - * Supported structures are: strings/collections (lists/sets)/arrays + * Supported structures are: strings / collections (lists/sets) / arrays. * * @author Andy Clement * @author Phillip Webb @@ -88,319 +88,8 @@ public class Indexer extends SpelNodeImpl { } - private class ArrayIndexingValueRef implements ValueRef { - - private final TypeConverter typeConverter; - - private final Object array; - - private final int index; - - private final TypeDescriptor typeDescriptor; - - - ArrayIndexingValueRef(TypeConverter typeConverter, Object array, int index, TypeDescriptor typeDescriptor) { - this.typeConverter = typeConverter; - this.array = array; - this.index = index; - this.typeDescriptor = typeDescriptor; - } - - - @Override - public TypedValue getValue() { - Object arrayElement = accessArrayElement(this.array, this.index); - return new TypedValue(arrayElement, this.typeDescriptor.elementTypeDescriptor(arrayElement)); - } - - @Override - public void setValue(Object newValue) { - setArrayElement(this.typeConverter, this.array, this.index, newValue, - this.typeDescriptor.getElementTypeDescriptor().getType()); - } - - @Override - public boolean isWritable() { - return true; - } - } - - - @SuppressWarnings({"rawtypes", "unchecked"}) - private class MapIndexingValueRef implements ValueRef { - - private final TypeConverter typeConverter; - - private final Map map; - - private final Object key; - - private final TypeDescriptor mapEntryTypeDescriptor; - - - MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, - TypeDescriptor mapEntryTypeDescriptor) { - this.typeConverter = typeConverter; - this.map = map; - this.key = key; - this.mapEntryTypeDescriptor = mapEntryTypeDescriptor; - } - - - @Override - public TypedValue getValue() { - Object value = this.map.get(this.key); - return new TypedValue(value, - this.mapEntryTypeDescriptor.getMapValueTypeDescriptor(value)); - } - - @Override - public void setValue(Object newValue) { - if (this.mapEntryTypeDescriptor.getMapValueTypeDescriptor() != null) { - newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue), - this.mapEntryTypeDescriptor.getMapValueTypeDescriptor()); - } - this.map.put(this.key, newValue); - } - - @Override - public boolean isWritable() { - return true; - } - } - - - private class PropertyIndexingValueRef implements ValueRef { - - private final Object targetObject; - - private final String name; - - private final EvaluationContext evaluationContext; - - private final TypeDescriptor targetObjectTypeDescriptor; - - - public PropertyIndexingValueRef(Object targetObject, String value, EvaluationContext evaluationContext, - TypeDescriptor targetObjectTypeDescriptor) { - this.targetObject = targetObject; - this.name = value; - this.evaluationContext = evaluationContext; - this.targetObjectTypeDescriptor = targetObjectTypeDescriptor; - } - - - @Override - public TypedValue getValue() { - Class targetObjectRuntimeClass = getObjectClass(this.targetObject); - try { - if (Indexer.this.cachedReadName != null && Indexer.this.cachedReadName.equals(this.name) && Indexer.this.cachedReadTargetType != null && - Indexer.this.cachedReadTargetType.equals(targetObjectRuntimeClass)) { - // it is OK to use the cached accessor - return Indexer.this.cachedReadAccessor.read(this.evaluationContext, this.targetObject, this.name); - } - - List accessorsToTry = AstUtils.getPropertyAccessorsToTry( - targetObjectRuntimeClass, this.evaluationContext.getPropertyAccessors()); - - if (accessorsToTry != null) { - for (PropertyAccessor accessor : accessorsToTry) { - if (accessor.canRead(this.evaluationContext, this.targetObject, this.name)) { - if (accessor instanceof ReflectivePropertyAccessor) { - accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor( - this.evaluationContext, this.targetObject, this.name); - } - Indexer.this.cachedReadAccessor = accessor; - Indexer.this.cachedReadName = this.name; - Indexer.this.cachedReadTargetType = targetObjectRuntimeClass; - return accessor.read(this.evaluationContext, this.targetObject, this.name); - } - } - } - } - catch (AccessException ex) { - throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, - this.targetObjectTypeDescriptor.toString()); - } - throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, - this.targetObjectTypeDescriptor.toString()); - } - - @Override - public void setValue(Object newValue) { - Class contextObjectClass = getObjectClass(this.targetObject); - try { - if (Indexer.this.cachedWriteName != null && Indexer.this.cachedWriteName.equals(this.name) && Indexer.this.cachedWriteTargetType != null && - Indexer.this.cachedWriteTargetType.equals(contextObjectClass)) { - // it is OK to use the cached accessor - Indexer.this.cachedWriteAccessor.write(this.evaluationContext, this.targetObject, this.name, newValue); - return; - } - List accessorsToTry = - AstUtils.getPropertyAccessorsToTry(contextObjectClass, this.evaluationContext.getPropertyAccessors()); - if (accessorsToTry != null) { - for (PropertyAccessor accessor : accessorsToTry) { - if (accessor.canWrite(this.evaluationContext, this.targetObject, this.name)) { - Indexer.this.cachedWriteName = this.name; - Indexer.this.cachedWriteTargetType = contextObjectClass; - Indexer.this.cachedWriteAccessor = accessor; - accessor.write(this.evaluationContext, this.targetObject, this.name, newValue); - return; - } - } - } - } - catch (AccessException ex) { - throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE, - this.name, ex.getMessage()); - } - } - - @Override - public boolean isWritable() { - return true; - } - } - - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private class CollectionIndexingValueRef implements ValueRef { - - private final Collection collection; - - private final int index; - - private final TypeDescriptor collectionEntryTypeDescriptor; - - private final TypeConverter typeConverter; - - private final boolean growCollection; - - private final int maximumSize; - - - CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryTypeDescriptor, - TypeConverter typeConverter, boolean growCollection, int maximumSize) { - this.collection = collection; - this.index = index; - this.collectionEntryTypeDescriptor = collectionEntryTypeDescriptor; - this.typeConverter = typeConverter; - this.growCollection = growCollection; - this.maximumSize = maximumSize; - } - - - @Override - public TypedValue getValue() { - growCollectionIfNecessary(); - if (this.collection instanceof List) { - Object o = ((List) this.collection).get(this.index); - return new TypedValue(o, this.collectionEntryTypeDescriptor.elementTypeDescriptor(o)); - } - int pos = 0; - for (Object o : this.collection) { - if (pos == this.index) { - return new TypedValue(o, this.collectionEntryTypeDescriptor.elementTypeDescriptor(o)); - } - pos++; - } - throw new IllegalStateException("Failed to find indexed element " + this.index + ": " + this.collection); - } - - @Override - public void setValue(Object newValue) { - growCollectionIfNecessary(); - if (this.collection instanceof List) { - List list = (List) this.collection; - if (this.collectionEntryTypeDescriptor.getElementTypeDescriptor() != null) { - newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue), - this.collectionEntryTypeDescriptor.getElementTypeDescriptor()); - } - list.set(this.index, newValue); - } - else { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, - this.collectionEntryTypeDescriptor.toString()); - } - } - - private void growCollectionIfNecessary() { - if (this.index >= this.collection.size()) { - - if (!this.growCollection) { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, - this.collection.size(), this.index); - } - - if(this.index >= this.maximumSize) { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION); - } - - if (this.collectionEntryTypeDescriptor.getElementTypeDescriptor() == null) { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE); - } - - TypeDescriptor elementType = this.collectionEntryTypeDescriptor.getElementTypeDescriptor(); - try { - int newElements = this.index - this.collection.size(); - while (newElements >= 0) { - (this.collection).add(elementType.getType().newInstance()); - newElements--; - } - } - catch (Exception ex) { - throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION); - } - } - } - - @Override - public boolean isWritable() { - return true; - } - } - - - private class StringIndexingLValue implements ValueRef { - - private final String target; - - private final int index; - - private final TypeDescriptor typeDescriptor; - - - public StringIndexingLValue(String target, int index, TypeDescriptor typeDescriptor) { - this.target = target; - this.index = index; - this.typeDescriptor = typeDescriptor; - } - - - @Override - public TypedValue getValue() { - if (this.index >= this.target.length()) { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.STRING_INDEX_OUT_OF_BOUNDS, - this.target.length(), this.index); - } - return new TypedValue(String.valueOf(this.target.charAt(this.index))); - } - - @Override - public void setValue(Object newValue) { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, - this.typeDescriptor.toString()); - } - - @Override - public boolean isWritable() { - return true; - } - } - @Override protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { - TypedValue context = state.getActiveContextObject(); Object targetObject = context.getValue(); TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor(); @@ -483,9 +172,9 @@ public class Indexer extends SpelNodeImpl { return sb.toString(); } - private void setArrayElement(TypeConverter converter, Object ctx, int idx, Object newValue, Class clazz) - throws EvaluationException { - Class arrayComponentType = clazz; + 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); @@ -538,7 +227,7 @@ public class Indexer extends SpelNodeImpl { Object[] array = (Object[]) ctx; checkAccess(array.length, idx); array[idx] = converter.convertValue(newValue, TypeDescriptor.forObject(newValue), - TypeDescriptor.valueOf(clazz)); + TypeDescriptor.valueOf(arrayComponentType)); } } @@ -598,4 +287,304 @@ public class Indexer extends SpelNodeImpl { } } + + private class ArrayIndexingValueRef implements ValueRef { + + private final TypeConverter typeConverter; + + private final Object array; + + private final int index; + + private final TypeDescriptor typeDescriptor; + + + ArrayIndexingValueRef(TypeConverter typeConverter, Object array, int index, TypeDescriptor typeDescriptor) { + this.typeConverter = typeConverter; + this.array = array; + this.index = index; + this.typeDescriptor = typeDescriptor; + } + + + @Override + public TypedValue getValue() { + Object arrayElement = accessArrayElement(this.array, this.index); + return new TypedValue(arrayElement, this.typeDescriptor.elementTypeDescriptor(arrayElement)); + } + + @Override + public void setValue(Object newValue) { + setArrayElement(this.typeConverter, this.array, this.index, newValue, + this.typeDescriptor.getElementTypeDescriptor().getType()); + } + + @Override + public boolean isWritable() { + return true; + } + } + + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static class MapIndexingValueRef implements ValueRef { + + private final TypeConverter typeConverter; + + private final Map map; + + private final Object key; + + private final TypeDescriptor mapEntryTypeDescriptor; + + public MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryTypeDescriptor) { + this.typeConverter = typeConverter; + this.map = map; + this.key = key; + this.mapEntryTypeDescriptor = mapEntryTypeDescriptor; + } + + @Override + public TypedValue getValue() { + Object value = this.map.get(this.key); + return new TypedValue(value, + this.mapEntryTypeDescriptor.getMapValueTypeDescriptor(value)); + } + + @Override + public void setValue(Object newValue) { + if (this.mapEntryTypeDescriptor.getMapValueTypeDescriptor() != null) { + newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue), + this.mapEntryTypeDescriptor.getMapValueTypeDescriptor()); + } + this.map.put(this.key, newValue); + } + + @Override + public boolean isWritable() { + return true; + } + } + + + private class PropertyIndexingValueRef implements ValueRef { + + private final Object targetObject; + + private final String name; + + private final EvaluationContext evaluationContext; + + private final TypeDescriptor targetObjectTypeDescriptor; + + public PropertyIndexingValueRef(Object targetObject, String value, EvaluationContext evaluationContext, + TypeDescriptor targetObjectTypeDescriptor) { + this.targetObject = targetObject; + this.name = value; + this.evaluationContext = evaluationContext; + this.targetObjectTypeDescriptor = targetObjectTypeDescriptor; + } + + + @Override + public TypedValue getValue() { + Class targetObjectRuntimeClass = getObjectClass(this.targetObject); + try { + if (Indexer.this.cachedReadName != null && Indexer.this.cachedReadName.equals(this.name) && + Indexer.this.cachedReadTargetType != null && + Indexer.this.cachedReadTargetType.equals(targetObjectRuntimeClass)) { + // It is OK to use the cached accessor + return Indexer.this.cachedReadAccessor.read(this.evaluationContext, this.targetObject, this.name); + } + List accessorsToTry = AstUtils.getPropertyAccessorsToTry( + targetObjectRuntimeClass, this.evaluationContext.getPropertyAccessors()); + if (accessorsToTry != null) { + for (PropertyAccessor accessor : accessorsToTry) { + if (accessor.canRead(this.evaluationContext, this.targetObject, this.name)) { + if (accessor instanceof ReflectivePropertyAccessor) { + accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor( + this.evaluationContext, this.targetObject, this.name); + } + Indexer.this.cachedReadAccessor = accessor; + Indexer.this.cachedReadName = this.name; + Indexer.this.cachedReadTargetType = targetObjectRuntimeClass; + return accessor.read(this.evaluationContext, this.targetObject, this.name); + } + } + } + } + catch (AccessException ex) { + throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, + this.targetObjectTypeDescriptor.toString()); + } + throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, + this.targetObjectTypeDescriptor.toString()); + } + + @Override + public void setValue(Object newValue) { + Class contextObjectClass = getObjectClass(this.targetObject); + try { + if (Indexer.this.cachedWriteName != null && Indexer.this.cachedWriteName.equals(this.name) && + Indexer.this.cachedWriteTargetType != null && + Indexer.this.cachedWriteTargetType.equals(contextObjectClass)) { + // It is OK to use the cached accessor + Indexer.this.cachedWriteAccessor.write(this.evaluationContext, this.targetObject, this.name, newValue); + return; + } + List accessorsToTry = + AstUtils.getPropertyAccessorsToTry(contextObjectClass, this.evaluationContext.getPropertyAccessors()); + if (accessorsToTry != null) { + for (PropertyAccessor accessor : accessorsToTry) { + if (accessor.canWrite(this.evaluationContext, this.targetObject, this.name)) { + Indexer.this.cachedWriteName = this.name; + Indexer.this.cachedWriteTargetType = contextObjectClass; + Indexer.this.cachedWriteAccessor = accessor; + accessor.write(this.evaluationContext, this.targetObject, this.name, newValue); + return; + } + } + } + } + catch (AccessException ex) { + throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE, + this.name, ex.getMessage()); + } + } + + @Override + public boolean isWritable() { + return true; + } + } + + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private class CollectionIndexingValueRef implements ValueRef { + + private final Collection collection; + + private final int index; + + private final TypeDescriptor collectionEntryDescriptor; + + private final TypeConverter typeConverter; + + private final boolean growCollection; + + private final int maximumSize; + + public CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryTypeDescriptor, + TypeConverter typeConverter, boolean growCollection, int maximumSize) { + this.collection = collection; + this.index = index; + this.collectionEntryDescriptor = collectionEntryTypeDescriptor; + this.typeConverter = typeConverter; + this.growCollection = growCollection; + this.maximumSize = maximumSize; + } + + + @Override + public TypedValue getValue() { + growCollectionIfNecessary(); + if (this.collection instanceof List) { + Object o = ((List) this.collection).get(this.index); + return new TypedValue(o, this.collectionEntryDescriptor.elementTypeDescriptor(o)); + } + int pos = 0; + for (Object o : this.collection) { + if (pos == this.index) { + return new TypedValue(o, this.collectionEntryDescriptor.elementTypeDescriptor(o)); + } + pos++; + } + throw new IllegalStateException("Failed to find indexed element " + this.index + ": " + this.collection); + } + + @Override + public void setValue(Object newValue) { + growCollectionIfNecessary(); + if (this.collection instanceof List) { + List list = (List) this.collection; + if (this.collectionEntryDescriptor.getElementTypeDescriptor() != null) { + newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue), + this.collectionEntryDescriptor.getElementTypeDescriptor()); + } + list.set(this.index, newValue); + } + else { + throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, + this.collectionEntryDescriptor.toString()); + } + } + + private void growCollectionIfNecessary() { + if (this.index >= this.collection.size()) { + if (!this.growCollection) { + throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, + this.collection.size(), this.index); + } + if(this.index >= this.maximumSize) { + throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION); + } + if (this.collectionEntryDescriptor.getElementTypeDescriptor() == null) { + throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE); + } + TypeDescriptor elementType = this.collectionEntryDescriptor.getElementTypeDescriptor(); + try { + int newElements = this.index - this.collection.size(); + while (newElements >= 0) { + (this.collection).add(elementType.getType().newInstance()); + newElements--; + } + } + catch (Exception ex) { + throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION); + } + } + } + + @Override + public boolean isWritable() { + return true; + } + } + + + private class StringIndexingLValue implements ValueRef { + + private final String target; + + private final int index; + + private final TypeDescriptor typeDescriptor; + + public StringIndexingLValue(String target, int index, TypeDescriptor typeDescriptor) { + this.target = target; + this.index = index; + this.typeDescriptor = typeDescriptor; + } + + @Override + public TypedValue getValue() { + if (this.index >= this.target.length()) { + throw new SpelEvaluationException(getStartPosition(), SpelMessage.STRING_INDEX_OUT_OF_BOUNDS, + this.target.length(), this.index); + } + return new TypedValue(String.valueOf(this.target.charAt(this.index))); + } + + @Override + public void setValue(Object newValue) { + throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, + this.typeDescriptor.toString()); + } + + @Override + public boolean isWritable() { + return true; + } + } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java index 1047e7729f9..5b0e2138c16 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java +++ b/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"); * 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]; + protected int pos; // start = top 16bits, end = bottom 16bits protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN; @@ -152,22 +153,21 @@ public abstract class SpelNodeImpl implements SpelNode { return ExpressionUtils.convertTypedValue(state.getEvaluationContext(), getValueInternal(state), desiredReturnType); } - public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException; - - @Override - public abstract String toStringAST(); - @Override public int getStartPosition() { - return (this.pos>>16); + return (this.pos >> 16); } @Override public int getEndPosition() { - return (this.pos&0xffff); + return (this.pos & 0xffff); } 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; + } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java index 17703a20f8c..5cec0e95034 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java +++ b/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); ExpressionParser parser = new SpelExpressionParser(); - Expression exp = parser.parseExpression("testBean.properties['key2']"); - String key = (String) exp.getValue(bean); - assertNotNull(key); + Expression expr = parser.parseExpression("testBean.properties['key2']"); + assertEquals("value2", expr.getValue(bean)); + } + + @Test + public void testGetValueFromRootMap() { + Map map = new HashMap(); + map.put("key", "value"); + EvaluationContext context = new StandardEvaluationContext(map); + + ExpressionParser spelExpressionParser = new SpelExpressionParser(); + Expression expr = spelExpressionParser.parseExpression("#root['key']"); + assertEquals("value", expr.getValue(map)); } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserDestinationMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserDestinationMessageHandler.java index 5f0d1b19741..bda6e87a4b8 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserDestinationMessageHandler.java +++ b/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.SimpMessagingTemplate; import org.springframework.messaging.support.MessageBuilder; -import org.springframework.messaging.support.MessageHeaderAccessor; import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; /** * 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 Object lifecycleMonitor = new Object(); + private final Object lifecycleMonitor = new Object(); private volatile boolean running = false; @@ -85,12 +83,6 @@ public class UserDestinationMessageHandler implements MessageHandler, SmartLifec this.userDestinationResolver = userDestinationResolver; } - /** - * Return the configured {@link UserDestinationResolver}. - */ - public UserDestinationResolver getUserDestinationResolver() { - return this.userDestinationResolver; - } /** * Return the configured messaging template for sending messages with @@ -100,16 +92,24 @@ public class UserDestinationMessageHandler implements MessageHandler, SmartLifec return this.brokerMessagingTemplate; } - @Override - public boolean isAutoStartup() { - return true; + /** + * Return the configured {@link UserDestinationResolver}. + */ + public UserDestinationResolver getUserDestinationResolver() { + return this.userDestinationResolver; } + @Override public int getPhase() { return Integer.MAX_VALUE; } + @Override + public boolean isAutoStartup() { + return true; + } + @Override public final boolean isRunning() { synchronized (this.lifecycleMonitor) { @@ -143,9 +143,9 @@ public class UserDestinationMessageHandler implements MessageHandler, SmartLifec } } + @Override public void handleMessage(Message message) throws MessagingException { - UserDestinationResult result = this.userDestinationResolver.resolveDestination(message); if (result == null) { return; @@ -154,13 +154,11 @@ public class UserDestinationMessageHandler implements MessageHandler, SmartLifec if (destinations.isEmpty()) { return; } - SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message); if (SimpMessageType.MESSAGE.equals(headerAccessor.getMessageType())) { headerAccessor.setHeader(SUBSCRIBE_DESTINATION, result.getSubscribeDestination()); message = MessageBuilder.withPayload(message.getPayload()).setHeaders(headerAccessor).build(); } - for (String targetDestination : destinations) { if (logger.isDebugEnabled()) { logger.debug("Sending message to resolved destination=" + targetDestination); diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/RollbackRuleAttribute.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/RollbackRuleAttribute.java index 14c4817e7ed..c7267cd9d78 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/RollbackRuleAttribute.java +++ b/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"); * you may not use this file except in compliance with the License. @@ -16,10 +16,10 @@ package org.springframework.transaction.interceptor; -import org.springframework.util.Assert; - import java.io.Serializable; +import org.springframework.util.Assert; + /** * Rule determining whether or not a given exception (and any subclasses) * should cause a rollback. @@ -60,7 +60,7 @@ public class RollbackRuleAttribute implements Serializable{ * not a {@code Throwable} type or is {@code null} */ public RollbackRuleAttribute(Class clazz) { - Assert.notNull(clazz, "'clazz' cannot be null."); + Assert.notNull(clazz, "'clazz' cannot be null"); if (!Throwable.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException( "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 */ 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; } @@ -111,7 +111,7 @@ public class RollbackRuleAttribute implements Serializable{ private int getDepth(Class exceptionClass, int depth) { - if (exceptionClass.getName().indexOf(this.exceptionName) != -1) { + if (exceptionClass.getName().contains(this.exceptionName)) { // Found it! return depth; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java index 011704fda53..fefc115c2e1 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java +++ b/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.InputStream; import java.io.Reader; -import java.lang.reflect.Method; import java.security.Principal; import java.time.ZoneId; import java.util.Locale; @@ -101,6 +100,9 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume else if (HttpSession.class.isAssignableFrom(paramType)) { return request.getSession(); } + else if (HttpMethod.class.equals(paramType)) { + return ((ServletWebRequest) webRequest).getHttpMethod(); + } else if (Principal.class.isAssignableFrom(paramType)) { return request.getUserPrincipal(); } @@ -120,13 +122,10 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume else if (Reader.class.isAssignableFrom(paramType)) { return request.getReader(); } - else if (HttpMethod.class.equals(paramType)) { - return ((ServletWebRequest) webRequest).getHttpMethod(); - } else { - // should never happen.. - Method method = parameter.getMethod(); - throw new UnsupportedOperationException("Unknown parameter type: " + paramType + " in method: " + method); + // should never happen... + throw new UnsupportedOperationException( + "Unknown parameter type: " + paramType + " in method: " + parameter.getMethod()); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java index 8c50bde21d4..8ccf7428d61 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java +++ b/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.util.Map; - import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; 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 * {@link org.springframework.web.servlet.support.RequestContext#getRequestUri() * originating URI} is used. - * * @return the value that is to be used for the '{@code action}' attribute */ protected String resolveAction() throws JspException { @@ -436,7 +434,8 @@ public class FormTag extends AbstractHtmlElementTag { } else if (StringUtils.hasText(servletRelativeAction)) { String pathToServlet = getRequestContext().getPathToServlet(); - if (servletRelativeAction.startsWith("/") && !servletRelativeAction.startsWith(getRequestContext().getContextPath())) { + if (servletRelativeAction.startsWith("/") && + !servletRelativeAction.startsWith(getRequestContext().getContextPath())) { servletRelativeAction = pathToServlet + servletRelativeAction; } servletRelativeAction = getDisplayString(evaluate(ACTION_ATTRIBUTE, servletRelativeAction)); @@ -444,12 +443,12 @@ public class FormTag extends AbstractHtmlElementTag { } else { String requestUri = getRequestContext().getRequestUri(); - String encoding = pageContext.getResponse().getCharacterEncoding(); + String encoding = this.pageContext.getResponse().getCharacterEncoding(); try { requestUri = UriUtils.encodePath(requestUri, encoding); } - catch (UnsupportedEncodingException e) { - throw new JspException(e); + catch (UnsupportedEncodingException ex) { + // shouldn't happen - if it does, proceed with requestUri as-is } ServletResponse response = this.pageContext.getResponse(); if (response instanceof HttpServletResponse) { @@ -476,7 +475,7 @@ public class FormTag extends AbstractHtmlElementTag { private String processAction(String action) { RequestDataValueProcessor processor = getRequestContext().getRequestDataValueProcessor(); ServletRequest request = this.pageContext.getRequest(); - if ((processor != null) && (request instanceof HttpServletRequest)) { + if (processor != null && request instanceof HttpServletRequest) { action = processor.processAction((HttpServletRequest) request, action, getHttpMethod()); } return action;