From 38327100d6ce284bca6cbacc139b705abe4a1cef Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Tue, 7 Jul 2009 17:24:58 +0000 Subject: [PATCH] SPR-5905: support for inner type references in type references 'T(com.foo.A$B)' or in ctor calls 'new com.foo.A$B()' git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1484 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../spel/ast/QualifiedIdentifier.java | 5 ++-- .../spel/standard/SpelExpressionParser.java | 23 +++++++++++---- .../expression/spel/SpringEL300Tests.java | 28 +++++++++++++++++++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java index fc990dcd95a..680224e7177 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java @@ -43,10 +43,11 @@ public class QualifiedIdentifier extends SpelNodeImpl { if (this.value == null) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < getChildCount(); i++) { - if (i > 0) { + Object value = children[i].getValueInternal(state).getValue(); + if (i > 0 && !value.toString().startsWith("$")) { sb.append("."); } - sb.append(children[i].getValueInternal(state).getValue()); + sb.append(value); } this.value = new TypedValue(sb.toString(),STRING_TYPE_DESCRIPTOR); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/SpelExpressionParser.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/SpelExpressionParser.java index c6ddb9ffa6d..786c6cc6815 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/SpelExpressionParser.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/SpelExpressionParser.java @@ -452,7 +452,7 @@ public class SpelExpressionParser extends TemplateAwareExpressionParser { } nextToken(); eatToken(TokenKind.LPAREN); - SpelNodeImpl node = eatPossiblyQualifiedId(); + SpelNodeImpl node = eatPossiblyQualifiedId(true); // dotted qualified id eatToken(TokenKind.RPAREN); constructedNodes.push(new TypeReference(toPos(typeName),node)); @@ -515,13 +515,26 @@ public class SpelExpressionParser extends TemplateAwareExpressionParser { return true; } - private SpelNodeImpl eatPossiblyQualifiedId() { + /** + * Eat an identifier, possibly qualified (meaning that it is dotted). If the dollarAllowed parameter is true then + * it will process any dollar characters found between names, and this allows it to support inner type references + * correctly. For example 'com.foo.bar.Outer$Inner' will produce the identifier sequence com, foo, bar, Outer, $Inner, + * note that the $ has been prefixed onto the Inner identifier. The code in TypeReference which reforms this into + * a typename copes with the $ prefixed identifiers. + * TODO AndyC Could create complete identifiers (a.b.c) here rather than a sequence of them? (a, b, c) + */ + private SpelNodeImpl eatPossiblyQualifiedId(boolean dollarAllowed) { List qualifiedIdPieces = new ArrayList(); Token startnode = eatToken(TokenKind.IDENTIFIER); qualifiedIdPieces.add(new Identifier(startnode.stringValue(),toPos(startnode))); - while (peekToken(TokenKind.DOT,true)) { + boolean dollar = false; + while (peekToken(TokenKind.DOT,true) || (dollarAllowed && (dollar = peekToken(TokenKind.DOLLAR,true)))) { Token node = eatToken(TokenKind.IDENTIFIER); - qualifiedIdPieces.add(new Identifier(node.stringValue(),toPos(node))); + if (dollar) { + qualifiedIdPieces.add(new Identifier("$"+node.stringValue(),((node.startpos-1)<<16)+node.endpos)); + } else { + qualifiedIdPieces.add(new Identifier(node.stringValue(),toPos(node))); + } } return new QualifiedIdentifier(toPos(startnode.startpos,qualifiedIdPieces.get(qualifiedIdPieces.size()-1).getEndPosition()),qualifiedIdPieces.toArray(new SpelNodeImpl[qualifiedIdPieces.size()])); } @@ -549,7 +562,7 @@ public class SpelExpressionParser extends TemplateAwareExpressionParser { private boolean maybeEatConstructorReference() { if (peekIdentifierToken("new")) { Token newToken = nextToken(); - SpelNodeImpl possiblyQualifiedConstructorName = eatPossiblyQualifiedId(); + SpelNodeImpl possiblyQualifiedConstructorName = eatPossiblyQualifiedId(true); List nodes = new ArrayList(); nodes.add(possiblyQualifiedConstructorName); eatConstructorArgs(nodes); diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpringEL300Tests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpringEL300Tests.java index 1524571ea0d..0fa715dfd4a 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpringEL300Tests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpringEL300Tests.java @@ -99,6 +99,9 @@ public class SpringEL300Tests extends ExpressionTestCase { if (typename.equals("Spr5899Class")) { return Spr5899Class.class; } + if (typename.equals("Outer")) { + return Outer.class; + } return super.findType(typename); } } @@ -126,6 +129,31 @@ public class SpringEL300Tests extends ExpressionTestCase { return "instance"; } } + + @Test + public void testSPR5905_InnerTypeReferences() throws Exception { + StandardEvaluationContext eContext = new StandardEvaluationContext(new Spr5899Class()); + Expression expr = new SpelExpressionParser().parse("T(java.util.Map$Entry)"); + Assert.assertEquals(Map.Entry.class,expr.getValue(eContext)); + + expr = new SpelExpressionParser().parse("T(org.springframework.expression.spel.SpringEL300Tests$Outer$Inner).run()"); + Assert.assertEquals(12,expr.getValue(eContext)); + + expr = new SpelExpressionParser().parse("new org.springframework.expression.spel.SpringEL300Tests$Outer$Inner().run2()"); + Assert.assertEquals(13,expr.getValue(eContext)); +} + + static class Outer { + static class Inner { + public Inner() {} + public static int run() { + return 12; + } + public int run2() { + return 13; + } + } + } @SuppressWarnings("unchecked") @Test