Skip to content

Commit

Permalink
INT-6051 SpEL selection and projection may now be applied to arrays.
Browse files Browse the repository at this point in the history
  • Loading branch information
markfisher committed Oct 28, 2009
1 parent 65f10cc commit 3346752
Show file tree
Hide file tree
Showing 3 changed files with 327 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package org.springframework.expression.spel.ast;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
Expand All @@ -27,14 +29,17 @@
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.ClassUtils;
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
*/
public class Projection extends SpelNodeImpl {

Expand All @@ -50,6 +55,7 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep
TypedValue op = state.getActiveContextObject();

Object operand = op.getValue();
boolean operandIsArray = ObjectUtils.isArray(operand);
// TypeDescriptor operandTypeDescriptor = op.getTypeDescriptor();

// When the input is a map, we push a special context object on the stack
Expand All @@ -69,22 +75,36 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep
}
}
return new TypedValue(result,TypeDescriptor.valueOf(List.class)); // TODO unable to build correct type descriptor
} else if (operand instanceof List) {
} else if (operand instanceof List || operandIsArray) {
List<Object> data = new ArrayList<Object>();
data.addAll((Collection<?>) operand);
Collection<?> c = (operand instanceof List) ? (Collection<?>) operand : Arrays.asList(ObjectUtils.toObjectArray(operand));
data.addAll(c);
List<Object> result = new ArrayList<Object>();
int idx = 0;
Class<?> arrayElementType = null;
for (Object element : data) {
try {
state.pushActiveContextObject(new TypedValue(element,TypeDescriptor.valueOf(op.getTypeDescriptor().getType())));
state.enterScope("index", idx);
result.add(children[0].getValueInternal(state).getValue());
Object value = children[0].getValueInternal(state).getValue();
if (value != null && operandIsArray) {
arrayElementType = this.determineCommonType(arrayElementType, value.getClass());
}
result.add(value);
} finally {
state.exitScope();
state.popActiveContextObject();
}
idx++;
}
if (operandIsArray) {
if (arrayElementType == null) {
arrayElementType = Object.class;
}
Object resultArray = Array.newInstance(arrayElementType, result.size());
System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
return new TypedValue(resultArray, op.getTypeDescriptor());
}
return new TypedValue(result,op.getTypeDescriptor());
} else {
if (operand==null) {
Expand All @@ -104,5 +124,28 @@ public String toStringAST() {
StringBuilder sb = new StringBuilder();
return sb.append("![").append(getChild(0).toStringAST()).append("]").toString();
}


private Class<?> determineCommonType(Class<?> oldType, Class<?> newType) {
if (oldType == null) {
return newType;
}
if (oldType.isAssignableFrom(newType)) {
return oldType;
}
Class<?> nextType = newType;
while (nextType != Object.class) {
if (nextType.isAssignableFrom(oldType)) {
return nextType;
}
nextType = nextType.getSuperclass();
}
Class<?>[] interfaces = ClassUtils.getAllInterfacesForClass(newType);
for (Class<?> nextInterface : interfaces) {
if (nextInterface.isAssignableFrom(oldType)) {
return nextInterface;
}
}
return Object.class;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package org.springframework.expression.spel.ast;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
Expand All @@ -28,6 +30,8 @@
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

/**
* Represents selection over a map or collection. For example: {1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'} returns
Expand All @@ -37,6 +41,8 @@
* criteria.
*
* @author Andy Clement
* @author Mark Fisher
* @since 3.0
*/
public class Selection extends SpelNodeImpl {

Expand Down Expand Up @@ -97,9 +103,11 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep
return new TypedValue(resultMap,TypeDescriptor.valueOf(Map.class));
}
return new TypedValue(result,op.getTypeDescriptor());
} else if (operand instanceof Collection) {
} else if ((operand instanceof Collection) || ObjectUtils.isArray(operand)) {
List<Object> data = new ArrayList<Object>();
data.addAll((Collection<?>) operand);
Collection<?> c = (operand instanceof Collection) ?
(Collection<?>) operand : Arrays.asList(ObjectUtils.toObjectArray(operand));
data.addAll(c);
List<Object> result = new ArrayList<Object>();
int idx = 0;
for (Object element : data) {
Expand Down Expand Up @@ -130,7 +138,15 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep
if (variant == LAST) {
return new TypedValue(result.get(result.size() - 1),TypeDescriptor.valueOf(op.getTypeDescriptor().getElementType()));
}
return new TypedValue(result,op.getTypeDescriptor());
if (operand instanceof Collection) {
return new TypedValue(result,op.getTypeDescriptor());
}
else {
Class<?> elementType = ClassUtils.resolvePrimitiveIfNecessary(op.getTypeDescriptor().getElementType());
Object resultArray = Array.newInstance(elementType, result.size());
System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
return new TypedValue(resultArray, op.getTypeDescriptor());
}
} else {
if (operand==null) {
if (nullSafe) {
Expand Down
Loading

0 comments on commit 3346752

Please sign in to comment.