diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java index 53536ad7434..6dc633c3bc5 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -36,7 +36,7 @@ import org.springframework.util.ObjectUtils; * Represents projection, where a given operation is performed on all elements in some input sequence, returning * a new sequence of the same size. For example: * "{1,2,3,4,5,6,7,8,9,10}.!{#isEven(#this)}" returns "[n, y, n, y, n, y, n, y, n, y]" - * + * * @author Andy Clement * @author Mark Fisher * @since 3.0 @@ -45,8 +45,8 @@ public class Projection extends SpelNodeImpl { private final boolean nullSafe; - public Projection(boolean nullSafe, int pos,SpelNodeImpl expression) { - super(pos,expression); + public Projection(boolean nullSafe, int pos, SpelNodeImpl expression) { + super(pos, expression); this.nullSafe = nullSafe; } @@ -64,34 +64,36 @@ public class Projection extends SpelNodeImpl { // and value, and they can be referenced in the operation // eg. {'a':'y','b':'n'}.!{value=='y'?key:null}" == ['a', null] if (operand instanceof Map) { - Map mapdata = (Map) operand; + Map mapData = (Map) operand; List result = new ArrayList(); - for (Map.Entry entry : mapdata.entrySet()) { + for (Map.Entry entry : mapData.entrySet()) { try { - state.pushActiveContextObject(new TypedValue(entry,TypeDescriptor.valueOf(Map.Entry.class))); - result.add(children[0].getValueInternal(state).getValue()); - } finally { + state.pushActiveContextObject(new TypedValue(entry, TypeDescriptor.valueOf(Map.Entry.class))); + result.add(this.children[0].getValueInternal(state).getValue()); + } + finally { state.popActiveContextObject(); } } return new TypedValue(result,TypeDescriptor.valueOf(List.class)); // TODO unable to build correct type descriptor - } else if (operand instanceof List || operandIsArray) { - List data = new ArrayList(); - Collection c = (operand instanceof List) ? (Collection) operand : Arrays.asList(ObjectUtils.toObjectArray(operand)); - data.addAll(c); + } + else if (operand instanceof Collection || operandIsArray) { + Collection data = (operand instanceof Collection ? (Collection) operand : + Arrays.asList(ObjectUtils.toObjectArray(operand))); List result = new ArrayList(); int idx = 0; Class arrayElementType = null; for (Object element : data) { try { - state.pushActiveContextObject(new TypedValue(element,TypeDescriptor.valueOf(op.getTypeDescriptor().getType()))); + state.pushActiveContextObject(new TypedValue(element ,TypeDescriptor.valueOf(op.getTypeDescriptor().getType()))); state.enterScope("index", idx); Object value = children[0].getValueInternal(state).getValue(); if (value != null && operandIsArray) { - arrayElementType = this.determineCommonType(arrayElementType, value.getClass()); + arrayElementType = determineCommonType(arrayElementType, value.getClass()); } result.add(value); - } finally { + } + finally { state.exitScope(); state.popActiveContextObject(); } @@ -105,16 +107,21 @@ public class Projection extends SpelNodeImpl { System.arraycopy(result.toArray(), 0, resultArray, 0, result.size()); return new TypedValue(resultArray, op.getTypeDescriptor()); } - return new TypedValue(result,op.getTypeDescriptor()); - } else { + return new TypedValue(result, op.getTypeDescriptor()); + } + else { if (operand==null) { - if (nullSafe) { + if (this.nullSafe) { return TypedValue.NULL; - } else { - throw new SpelEvaluationException(getStartPosition(),SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null"); } - } else { - throw new SpelEvaluationException(getStartPosition(),SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName()); + else { + throw new SpelEvaluationException(getStartPosition(), + SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null"); + } + } + else { + throw new SpelEvaluationException(getStartPosition(), + SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName()); } } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java index 8acf52b8151..99931e577b8 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java @@ -16,15 +16,16 @@ package org.springframework.expression.spel; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeMap; +import static org.junit.Assert.*; import org.junit.Test; + import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; @@ -35,8 +36,6 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; /** * @author Mark Fisher * @author Sam Brannen - * - * @since 3.0 */ public class SelectionAndProjectionTests { @@ -73,6 +72,39 @@ public class SelectionAndProjectionTests { assertEquals(4, value); } + @Test + public void selectionWithSet() throws Exception { + Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]"); + EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); + Object value = expression.getValue(context); + assertTrue(value instanceof List); + List list = (List) value; + assertEquals(5, list.size()); + assertEquals(0, list.get(0)); + assertEquals(1, list.get(1)); + assertEquals(2, list.get(2)); + assertEquals(3, list.get(3)); + assertEquals(4, list.get(4)); + } + + @Test + public void selectFirstItemInSet() throws Exception { + Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]"); + EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); + Object value = expression.getValue(context); + assertTrue(value instanceof Integer); + assertEquals(0, value); + } + + @Test + public void selectLastItemInSet() throws Exception { + Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]"); + EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); + Object value = expression.getValue(context); + assertTrue(value instanceof Integer); + assertEquals(4, value); + } + @Test public void selectionWithArray() throws Exception { Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]"); @@ -195,6 +227,20 @@ public class SelectionAndProjectionTests { assertEquals(7, list.get(2)); } + @Test + public void projectionWithSet() throws Exception { + Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]"); + EvaluationContext context = new StandardEvaluationContext(); + context.setVariable("testList", IntegerTestBean.createSet()); + Object value = expression.getValue(context); + assertTrue(value instanceof List); + List list = (List) value; + assertEquals(3, list.size()); + assertEquals(5, list.get(0)); + assertEquals(6, list.get(1)); + assertEquals(7, list.get(2)); + } + @Test public void projectionWithArray() throws Exception { Expression expression = new SpelExpressionParser().parseRaw("#testArray.![wrapper.value]"); @@ -244,6 +290,21 @@ public class SelectionAndProjectionTests { } } + static class SetTestBean { + + private final Set integers = new LinkedHashSet(); + + SetTestBean() { + for (int i = 0; i < 10; i++) { + integers.add(i); + } + } + + public Set getIntegers() { + return integers; + } + } + static class ArrayTestBean { private final int[] ints = new int[10]; @@ -286,6 +347,14 @@ public class SelectionAndProjectionTests { return list; } + static Set createSet() { + Set set = new LinkedHashSet(); + for (int i = 0; i < 3; i++) { + set.add(new IntegerTestBean(i + 5)); + } + return set; + } + static IntegerTestBean[] createArray() { IntegerTestBean[] array = new IntegerTestBean[3]; for (int i = 0; i < 3; i++) {