SPR-6984: auto grow collections on write through indexer
This commit is contained in:
parent
0cb7e4dcb3
commit
d932c043da
|
|
@ -61,7 +61,6 @@ public class Indexer extends SpelNodeImpl {
|
||||||
super(pos, expr);
|
super(pos, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||||
TypedValue context = state.getActiveContextObject();
|
TypedValue context = state.getActiveContextObject();
|
||||||
|
|
@ -119,28 +118,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
} else if (targetObject instanceof Collection) {
|
} else if (targetObject instanceof Collection) {
|
||||||
Collection c = (Collection) targetObject;
|
Collection c = (Collection) targetObject;
|
||||||
if (idx >= c.size()) {
|
if (idx >= c.size()) {
|
||||||
if (state.getConfiguration().isAutoGrowCollections()) {
|
if (!growCollection(state, targetObjectTypeDescriptor.getElementType(), idx, c)) {
|
||||||
// Grow the collection
|
|
||||||
Object newCollectionElement = null;
|
|
||||||
try {
|
|
||||||
int newElements = idx-c.size();
|
|
||||||
Class elementClass = targetObjectTypeDescriptor.getElementType();
|
|
||||||
if (elementClass == null) {
|
|
||||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE);
|
|
||||||
}
|
|
||||||
while (newElements>0) {
|
|
||||||
c.add(elementClass.newInstance());
|
|
||||||
newElements--;
|
|
||||||
}
|
|
||||||
newCollectionElement = targetObjectTypeDescriptor.getElementType().newInstance();
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION);
|
|
||||||
}
|
|
||||||
c.add(newCollectionElement);
|
|
||||||
return new TypedValue(newCollectionElement,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getElementType()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
|
throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -234,8 +212,10 @@ public class Indexer extends SpelNodeImpl {
|
||||||
int idx = (Integer)state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
|
int idx = (Integer)state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
|
||||||
Collection c = (Collection) targetObject;
|
Collection c = (Collection) targetObject;
|
||||||
if (idx >= c.size()) {
|
if (idx >= c.size()) {
|
||||||
|
if (!growCollection(state, targetObjectTypeDescriptor.getElementType(), idx, c)) {
|
||||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
|
throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (targetObject instanceof List) {
|
if (targetObject instanceof List) {
|
||||||
List list = (List)targetObject;
|
List list = (List)targetObject;
|
||||||
Object possiblyConvertedValue = state.convertValue(newValue,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getElementType()));
|
Object possiblyConvertedValue = state.convertValue(newValue,TypeDescriptor.valueOf(targetObjectTypeDescriptor.getElementType()));
|
||||||
|
|
@ -282,6 +262,40 @@ public class Indexer extends SpelNodeImpl {
|
||||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetObjectTypeDescriptor.asString());
|
throw new SpelEvaluationException(getStartPosition(),SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetObjectTypeDescriptor.asString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to grow the specified collection so that the specified index is valid.
|
||||||
|
*
|
||||||
|
* @param state the expression state
|
||||||
|
* @param elementType the type of the elements in the collection
|
||||||
|
* @param index the index into the collection that needs to be valid
|
||||||
|
* @param collection the collection to grow with elements
|
||||||
|
* @return true if collection growing succeeded, otherwise false
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private boolean growCollection(ExpressionState state, Class<?> elementType, int index,
|
||||||
|
Collection collection) {
|
||||||
|
if (state.getConfiguration().isAutoGrowCollections()) {
|
||||||
|
Object newCollectionElement = null;
|
||||||
|
try {
|
||||||
|
int newElements = index-collection.size();
|
||||||
|
if (elementType == null) {
|
||||||
|
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE);
|
||||||
|
}
|
||||||
|
while (newElements>0) {
|
||||||
|
collection.add(elementType.newInstance());
|
||||||
|
newElements--;
|
||||||
|
}
|
||||||
|
newCollectionElement = elementType.newInstance();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION);
|
||||||
|
}
|
||||||
|
collection.add(newCollectionElement);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toStringAST() {
|
public String toStringAST() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import org.springframework.expression.spel.standard.SpelExpression;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||||
|
import org.springframework.expression.spel.testresources.TestPerson;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the evaluation of real expressions in a real context.
|
* Tests the evaluation of real expressions in a real context.
|
||||||
|
|
@ -522,4 +523,31 @@ public class EvaluationTests extends ExpressionTestCase {
|
||||||
Assert.assertEquals(String.class,stringClass);
|
Assert.assertEquals(String.class,stringClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SPR-6984: attempting to index a collection on write using an index that doesn't currently exist in the collection (address.crossStreets[0] below)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void initializingCollectionElementsOnWrite() throws Exception {
|
||||||
|
TestPerson person = new TestPerson();
|
||||||
|
EvaluationContext context = new StandardEvaluationContext(person);
|
||||||
|
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
|
||||||
|
ExpressionParser parser = new SpelExpressionParser(config);
|
||||||
|
Expression expression = parser.parseExpression("name");
|
||||||
|
expression.setValue(context, "Oleg");
|
||||||
|
Assert.assertEquals("Oleg",person.getName());
|
||||||
|
|
||||||
|
expression = parser.parseExpression("address.street");
|
||||||
|
expression.setValue(context, "123 High St");
|
||||||
|
Assert.assertEquals("123 High St",person.getAddress().getStreet());
|
||||||
|
|
||||||
|
expression = parser.parseExpression("address.crossStreets[0]");
|
||||||
|
expression.setValue(context, "Blah");
|
||||||
|
Assert.assertEquals("Blah",person.getAddress().getCrossStreets().get(0));
|
||||||
|
|
||||||
|
expression = parser.parseExpression("address.crossStreets[3]");
|
||||||
|
expression.setValue(context, "Wibble");
|
||||||
|
Assert.assertEquals("Blah",person.getAddress().getCrossStreets().get(0));
|
||||||
|
Assert.assertEquals("Wibble",person.getAddress().getCrossStreets().get(3));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.springframework.expression.spel.testresources;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TestAddress{
|
||||||
|
private String street;
|
||||||
|
private List<String> crossStreets;
|
||||||
|
|
||||||
|
public String getStreet() {
|
||||||
|
return street;
|
||||||
|
}
|
||||||
|
public void setStreet(String street) {
|
||||||
|
this.street = street;
|
||||||
|
}
|
||||||
|
public List<String> getCrossStreets() {
|
||||||
|
return crossStreets;
|
||||||
|
}
|
||||||
|
public void setCrossStreets(List<String> crossStreets) {
|
||||||
|
this.crossStreets = crossStreets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package org.springframework.expression.spel.testresources;
|
||||||
|
|
||||||
|
public class TestPerson {
|
||||||
|
private String name;
|
||||||
|
private TestAddress address;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
public TestAddress getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
public void setAddress(TestAddress address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue