Support [] array ref syntax in SpEL T() construct

Prior to this change, SpEL would not allow the use of '[]' in
expressions like the following:

    T(foo.Bar[])

This commit updates TypeReference and InternalSpelExpressionParser to
support this syntax, avoiding the need for workarounds like:

    new foo.bar[0].class

Issue: SPR-9203
This commit is contained in:
Andy Clement 2012-03-05 15:53:14 -08:00 committed by Chris Beams
parent 0e8f5d877a
commit 916e9d6efa
3 changed files with 69 additions and 8 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2012 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,6 +16,8 @@
package org.springframework.expression.spel.ast; package org.springframework.expression.spel.ast;
import java.lang.reflect.Array;
import org.springframework.expression.EvaluationException; import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypedValue; import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ExpressionState;
@ -27,8 +29,15 @@ import org.springframework.expression.spel.ExpressionState;
*/ */
public class TypeReference extends SpelNodeImpl { public class TypeReference extends SpelNodeImpl {
private int dimensions;
public TypeReference(int pos,SpelNodeImpl qualifiedId) { public TypeReference(int pos,SpelNodeImpl qualifiedId) {
this(pos,qualifiedId,0);
}
public TypeReference(int pos,SpelNodeImpl qualifiedId,int dims) {
super(pos,qualifiedId); super(pos,qualifiedId);
this.dimensions = dims;
} }
@Override @Override
@ -39,10 +48,24 @@ public class TypeReference extends SpelNodeImpl {
TypeCode tc = TypeCode.valueOf(typename.toUpperCase()); TypeCode tc = TypeCode.valueOf(typename.toUpperCase());
if (tc != TypeCode.OBJECT) { if (tc != TypeCode.OBJECT) {
// it is a primitive type // it is a primitive type
return new TypedValue(tc.getType()); Class<?> clazz = tc.getType();
clazz = makeArrayIfNecessary(clazz);
return new TypedValue(clazz);
} }
} }
return new TypedValue(state.findType(typename)); Class<?> clazz = state.findType(typename);
clazz = makeArrayIfNecessary(clazz);
return new TypedValue(clazz);
}
private Class makeArrayIfNecessary(Class clazz) {
if (dimensions!=0) {
for (int i=0;i<dimensions;i++) {
Object o = Array.newInstance(clazz, 0);
clazz = o.getClass();
}
}
return clazz;
} }
@Override @Override
@ -50,6 +73,9 @@ public class TypeReference extends SpelNodeImpl {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("T("); sb.append("T(");
sb.append(getChild(0).toStringAST()); sb.append(getChild(0).toStringAST());
for (int d=0;d<dimensions;d++) {
sb.append("[]");
}
sb.append(")"); sb.append(")");
return sb.toString(); return sb.toString();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2012 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.
@ -497,8 +497,14 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
eatToken(TokenKind.LPAREN); eatToken(TokenKind.LPAREN);
SpelNodeImpl node = eatPossiblyQualifiedId(); SpelNodeImpl node = eatPossiblyQualifiedId();
// dotted qualified id // dotted qualified id
// Are there array dimensions?
int dims = 0;
while (peekToken(TokenKind.LSQUARE,true)) {
eatToken(TokenKind.RSQUARE);
dims++;
}
eatToken(TokenKind.RPAREN); eatToken(TokenKind.RPAREN);
constructedNodes.push(new TypeReference(toPos(typeName),node)); constructedNodes.push(new TypeReference(toPos(typeName),node,dims));
return true; return true;
} }
return false; return false;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2012 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.
@ -1175,6 +1175,35 @@ public class SpringEL300Tests extends ExpressionTestCase {
} }
} }
@Test
public void testArray() {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
Expression expression = null;
Object result = null;
expression = parser.parseExpression("new java.lang.Long[0].class");
result = expression.getValue(context, "");
assertEquals("Equal assertion failed: ", "class [Ljava.lang.Long;", result.toString());
expression = parser.parseExpression("T(java.lang.Long[])");
result = expression.getValue(context, "");
assertEquals("Equal assertion failed: ", "class [Ljava.lang.Long;", result.toString());
expression = parser.parseExpression("T(java.lang.String[][][])");
result = expression.getValue(context, "");
assertEquals("Equal assertion failed: ", "class [[[Ljava.lang.String;", result.toString());
assertEquals("T(java.lang.String[][][])",((SpelExpression)expression).toStringAST());
expression = parser.parseExpression("new int[0].class");
result = expression.getValue(context, "");
assertEquals("Equal assertion failed: ", "class [I", result.toString());
expression = parser.parseExpression("T(int[][])");
result = expression.getValue(context, "");
assertEquals("Equal assertion failed: ", "class [[I", result.toString());
}
} }