SPR-7173, SPR-7100
This commit is contained in:
parent
42cdfbcd89
commit
f53621a86f
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A bean resolver can be registered with the evaluation context and will
|
||||||
|
* @author Andy Clement
|
||||||
|
* @since 3.0.3
|
||||||
|
*/
|
||||||
|
public interface BeanResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup the named bean and return it.
|
||||||
|
* @param context the current evaluation context
|
||||||
|
* @param beanname the name of the bean to lookup
|
||||||
|
* @return a object representing the bean
|
||||||
|
* @throws AccessException if there is an unexpected problem resolving the named bean
|
||||||
|
*/
|
||||||
|
Object resolve(EvaluationContext context, String beanname) throws AccessException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -73,6 +73,11 @@ public interface EvaluationContext {
|
||||||
*/
|
*/
|
||||||
OperatorOverloader getOperatorOverloader();
|
OperatorOverloader getOperatorOverloader();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a bean resolver that can lookup named beans
|
||||||
|
*/
|
||||||
|
BeanResolver getBeanResolver();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a named variable within this evaluation context to a specified value.
|
* Set a named variable within this evaluation context to a specified value.
|
||||||
* @param name variable to set
|
* @param name variable to set
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,9 @@ public enum SpelMessage {
|
||||||
UNABLE_TO_CREATE_LIST_FOR_INDEXING(Kind.ERROR,1054,"Unable to dynamically create a List to replace a null value"),//
|
UNABLE_TO_CREATE_LIST_FOR_INDEXING(Kind.ERROR,1054,"Unable to dynamically create a List to replace a null value"),//
|
||||||
UNABLE_TO_CREATE_MAP_FOR_INDEXING(Kind.ERROR,1055,"Unable to dynamically create a Map to replace a null value"),//
|
UNABLE_TO_CREATE_MAP_FOR_INDEXING(Kind.ERROR,1055,"Unable to dynamically create a Map to replace a null value"),//
|
||||||
UNABLE_TO_DYNAMICALLY_CREATE_OBJECT(Kind.ERROR,1056,"Unable to dynamically create instance of ''{0}'' to replace a null value"),//
|
UNABLE_TO_DYNAMICALLY_CREATE_OBJECT(Kind.ERROR,1056,"Unable to dynamically create instance of ''{0}'' to replace a null value"),//
|
||||||
|
NO_BEAN_RESOLVER_REGISTERED(Kind.ERROR,1057,"No bean resolver registered in the context to resolve access to bean ''{0}''"),//
|
||||||
|
EXCEPTION_DURING_BEAN_RESOLUTION(Kind.ERROR, 1058, "A problem occurred when trying to resolve bean ''{0}'':''{1}''"), //
|
||||||
|
INVALID_BEAN_REFERENCE(Kind.ERROR,1059,"@ can only be followed by an identifier or a quoted name"),//
|
||||||
;
|
;
|
||||||
|
|
||||||
private Kind kind;
|
private Kind kind;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.expression.spel.ast;
|
||||||
|
|
||||||
|
import org.springframework.expression.AccessException;
|
||||||
|
import org.springframework.expression.BeanResolver;
|
||||||
|
import org.springframework.expression.EvaluationException;
|
||||||
|
import org.springframework.expression.TypedValue;
|
||||||
|
import org.springframework.expression.spel.ExpressionState;
|
||||||
|
import org.springframework.expression.spel.SpelEvaluationException;
|
||||||
|
import org.springframework.expression.spel.SpelMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a bean reference to a type, for example "@foo" or "@'foo.bar'"
|
||||||
|
*
|
||||||
|
* @author Andy Clement
|
||||||
|
*/
|
||||||
|
public class BeanReference extends SpelNodeImpl {
|
||||||
|
|
||||||
|
private String beanname;
|
||||||
|
|
||||||
|
public BeanReference(int pos,String beanname) {
|
||||||
|
super(pos);
|
||||||
|
this.beanname = beanname;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||||
|
BeanResolver beanResolver = state.getEvaluationContext().getBeanResolver();
|
||||||
|
if (beanResolver==null) {
|
||||||
|
throw new SpelEvaluationException(getStartPosition(),SpelMessage.NO_BEAN_RESOLVER_REGISTERED, beanname);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
TypedValue bean = new TypedValue(beanResolver.resolve(state.getEvaluationContext(),beanname));
|
||||||
|
return bean;
|
||||||
|
} catch (AccessException ae) {
|
||||||
|
throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION,
|
||||||
|
beanname, ae.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toStringAST() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("@");
|
||||||
|
if (beanname.indexOf('.')==-1) {
|
||||||
|
sb.append(beanname);
|
||||||
|
} else {
|
||||||
|
sb.append("'").append(beanname).append("'");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ import org.springframework.expression.spel.SpelMessage;
|
||||||
import org.springframework.expression.spel.SpelParseException;
|
import org.springframework.expression.spel.SpelParseException;
|
||||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||||
import org.springframework.expression.spel.ast.Assign;
|
import org.springframework.expression.spel.ast.Assign;
|
||||||
|
import org.springframework.expression.spel.ast.BeanReference;
|
||||||
import org.springframework.expression.spel.ast.BooleanLiteral;
|
import org.springframework.expression.spel.ast.BooleanLiteral;
|
||||||
import org.springframework.expression.spel.ast.CompoundExpression;
|
import org.springframework.expression.spel.ast.CompoundExpression;
|
||||||
import org.springframework.expression.spel.ast.ConstructorReference;
|
import org.springframework.expression.spel.ast.ConstructorReference;
|
||||||
|
|
@ -437,6 +438,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
return pop();
|
return pop();
|
||||||
} else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) {
|
} else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) {
|
||||||
return pop();
|
return pop();
|
||||||
|
} else if (maybeEatBeanReference()) {
|
||||||
|
return pop();
|
||||||
} else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) {
|
} else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) {
|
||||||
return pop();
|
return pop();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -444,7 +447,30 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse: @beanname @'bean.name'
|
||||||
|
// quoted if dotted
|
||||||
|
private boolean maybeEatBeanReference() {
|
||||||
|
if (peekToken(TokenKind.BEAN_REF)) {
|
||||||
|
Token beanRefToken = nextToken();
|
||||||
|
Token beanNameToken = null;
|
||||||
|
String beanname = null;
|
||||||
|
if (peekToken(TokenKind.IDENTIFIER)) {
|
||||||
|
beanNameToken = eatToken(TokenKind.IDENTIFIER);
|
||||||
|
beanname = beanNameToken.data;
|
||||||
|
} else if (peekToken(TokenKind.LITERAL_STRING)) {
|
||||||
|
beanNameToken = eatToken(TokenKind.LITERAL_STRING);
|
||||||
|
beanname = beanNameToken.stringValue();
|
||||||
|
beanname = beanname.substring(1, beanname.length() - 1);
|
||||||
|
} else {
|
||||||
|
raiseInternalException(beanRefToken.startpos,SpelMessage.INVALID_BEAN_REFERENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
BeanReference beanReference = new BeanReference(toPos(beanNameToken),beanname);
|
||||||
|
constructedNodes.push(beanReference);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean maybeEatTypeReference() {
|
private boolean maybeEatTypeReference() {
|
||||||
if (peekToken(TokenKind.IDENTIFIER)) {
|
if (peekToken(TokenKind.IDENTIFIER)) {
|
||||||
|
|
@ -454,7 +480,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
}
|
}
|
||||||
nextToken();
|
nextToken();
|
||||||
eatToken(TokenKind.LPAREN);
|
eatToken(TokenKind.LPAREN);
|
||||||
SpelNodeImpl node = eatPossiblyQualifiedId(true);
|
SpelNodeImpl node = eatPossiblyQualifiedId();
|
||||||
// dotted qualified id
|
// dotted qualified id
|
||||||
eatToken(TokenKind.RPAREN);
|
eatToken(TokenKind.RPAREN);
|
||||||
constructedNodes.push(new TypeReference(toPos(typeName),node));
|
constructedNodes.push(new TypeReference(toPos(typeName),node));
|
||||||
|
|
@ -518,29 +544,22 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eat an identifier, possibly qualified (meaning that it is dotted). If the dollarAllowed parameter is true then
|
* Eat an identifier, possibly qualified (meaning that it is dotted).
|
||||||
* 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)
|
* TODO AndyC Could create complete identifiers (a.b.c) here rather than a sequence of them? (a, b, c)
|
||||||
*/
|
*/
|
||||||
private SpelNodeImpl eatPossiblyQualifiedId(boolean dollarAllowed) {
|
private SpelNodeImpl eatPossiblyQualifiedId() {
|
||||||
List<SpelNodeImpl> qualifiedIdPieces = new ArrayList<SpelNodeImpl>();
|
List<SpelNodeImpl> qualifiedIdPieces = new ArrayList<SpelNodeImpl>();
|
||||||
Token startnode = eatToken(TokenKind.IDENTIFIER);
|
Token startnode = eatToken(TokenKind.IDENTIFIER);
|
||||||
qualifiedIdPieces.add(new Identifier(startnode.stringValue(),toPos(startnode)));
|
qualifiedIdPieces.add(new Identifier(startnode.stringValue(),toPos(startnode)));
|
||||||
boolean dollar = false;
|
while (peekToken(TokenKind.DOT,true)) {
|
||||||
while (peekToken(TokenKind.DOT,true) || (dollarAllowed && (dollar = peekToken(TokenKind.DOLLAR,true)))) {
|
|
||||||
Token node = eatToken(TokenKind.IDENTIFIER);
|
Token node = eatToken(TokenKind.IDENTIFIER);
|
||||||
if (dollar) {
|
|
||||||
qualifiedIdPieces.add(new Identifier("$"+node.stringValue(),((node.startpos-1)<<16)+node.endpos));
|
|
||||||
} else {
|
|
||||||
qualifiedIdPieces.add(new Identifier(node.stringValue(),toPos(node)));
|
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()]));
|
return new QualifiedIdentifier(toPos(startnode.startpos,qualifiedIdPieces.get(qualifiedIdPieces.size()-1).getEndPosition()),qualifiedIdPieces.toArray(new SpelNodeImpl[qualifiedIdPieces.size()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but
|
||||||
|
// there we want to combine a series of identifiers and dollars into a single identifier
|
||||||
private boolean maybeEatMethodOrProperty(boolean nullSafeNavigation) {
|
private boolean maybeEatMethodOrProperty(boolean nullSafeNavigation) {
|
||||||
if (peekToken(TokenKind.IDENTIFIER)) {
|
if (peekToken(TokenKind.IDENTIFIER)) {
|
||||||
Token methodOrPropertyName = nextToken();
|
Token methodOrPropertyName = nextToken();
|
||||||
|
|
@ -557,6 +576,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//constructor
|
//constructor
|
||||||
|
|
@ -564,7 +584,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||||
private boolean maybeEatConstructorReference() {
|
private boolean maybeEatConstructorReference() {
|
||||||
if (peekIdentifierToken("new")) {
|
if (peekIdentifierToken("new")) {
|
||||||
Token newToken = nextToken();
|
Token newToken = nextToken();
|
||||||
SpelNodeImpl possiblyQualifiedConstructorName = eatPossiblyQualifiedId(true);
|
SpelNodeImpl possiblyQualifiedConstructorName = eatPossiblyQualifiedId();
|
||||||
List<SpelNodeImpl> nodes = new ArrayList<SpelNodeImpl>();
|
List<SpelNodeImpl> nodes = new ArrayList<SpelNodeImpl>();
|
||||||
nodes.add(possiblyQualifiedConstructorName);
|
nodes.add(possiblyQualifiedConstructorName);
|
||||||
eatConstructorArgs(nodes);
|
eatConstructorArgs(nodes);
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ enum TokenKind {
|
||||||
COLON(":"),HASH("#"),RSQUARE("]"), LSQUARE("["),
|
COLON(":"),HASH("#"),RSQUARE("]"), LSQUARE("["),
|
||||||
DOT("."), PLUS("+"), STAR("*"), DIV("/"), NOT("!"), MINUS("-"), SELECT_FIRST("^["), SELECT_LAST("$["), QMARK("?"), PROJECT("!["),
|
DOT("."), PLUS("+"), STAR("*"), DIV("/"), NOT("!"), MINUS("-"), SELECT_FIRST("^["), SELECT_LAST("$["), QMARK("?"), PROJECT("!["),
|
||||||
GE(">="),GT(">"),LE("<="),LT("<"),EQ("=="),NE("!="),ASSIGN("="), INSTANCEOF("instanceof"), MATCHES("matches"), BETWEEN("between"),
|
GE(">="),GT(">"),LE("<="),LT("<"),EQ("=="),NE("!="),ASSIGN("="), INSTANCEOF("instanceof"), MATCHES("matches"), BETWEEN("between"),
|
||||||
SELECT("?["), MOD("%"), POWER("^"), DOLLAR("$"),
|
SELECT("?["), MOD("%"), POWER("^"),
|
||||||
ELVIS("?:"), SAFE_NAVI("?.");
|
ELVIS("?:"), SAFE_NAVI("?."), BEAN_REF("@")
|
||||||
;
|
;
|
||||||
|
|
||||||
char[] tokenChars;
|
char[] tokenChars;
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,9 @@ class Tokenizer {
|
||||||
case ']':
|
case ']':
|
||||||
pushCharToken(TokenKind.RSQUARE);
|
pushCharToken(TokenKind.RSQUARE);
|
||||||
break;
|
break;
|
||||||
|
case '@':
|
||||||
|
pushCharToken(TokenKind.BEAN_REF);
|
||||||
|
break;
|
||||||
case '^':
|
case '^':
|
||||||
if (isTwoCharToken(TokenKind.SELECT_FIRST)) {
|
if (isTwoCharToken(TokenKind.SELECT_FIRST)) {
|
||||||
pushPairToken(TokenKind.SELECT_FIRST);
|
pushPairToken(TokenKind.SELECT_FIRST);
|
||||||
|
|
@ -134,7 +137,7 @@ class Tokenizer {
|
||||||
if (isTwoCharToken(TokenKind.SELECT_LAST)) {
|
if (isTwoCharToken(TokenKind.SELECT_LAST)) {
|
||||||
pushPairToken(TokenKind.SELECT_LAST);
|
pushPairToken(TokenKind.SELECT_LAST);
|
||||||
} else {
|
} else {
|
||||||
pushCharToken(TokenKind.DOLLAR);
|
lexIdentifier();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
|
|
@ -424,9 +427,9 @@ class Tokenizer {
|
||||||
tokens.add(new Token(kind,pos,pos+kind.getLength()));
|
tokens.add(new Token(kind,pos,pos+kind.getLength()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|DOT_ESCAPED)*;
|
// ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*;
|
||||||
private boolean isIdentifier(char ch) {
|
private boolean isIdentifier(char ch) {
|
||||||
return isAlphabetic(ch) || isDigit(ch) || ch=='_';
|
return isAlphabetic(ch) || isDigit(ch) || ch=='_' || ch=='$';
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isChar(char a,char b) {
|
private boolean isChar(char a,char b) {
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
import org.springframework.expression.BeanResolver;
|
||||||
import org.springframework.expression.ConstructorResolver;
|
import org.springframework.expression.ConstructorResolver;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.MethodFilter;
|
import org.springframework.expression.MethodFilter;
|
||||||
|
|
@ -66,6 +67,8 @@ public class StandardEvaluationContext implements EvaluationContext {
|
||||||
|
|
||||||
private final Map<String, Object> variables = new HashMap<String, Object>();
|
private final Map<String, Object> variables = new HashMap<String, Object>();
|
||||||
|
|
||||||
|
private BeanResolver beanResolver;
|
||||||
|
|
||||||
|
|
||||||
public StandardEvaluationContext() {
|
public StandardEvaluationContext() {
|
||||||
setRootObject(null);
|
setRootObject(null);
|
||||||
|
|
@ -134,6 +137,14 @@ public class StandardEvaluationContext implements EvaluationContext {
|
||||||
return this.methodResolvers;
|
return this.methodResolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBeanResolver(BeanResolver beanResolver) {
|
||||||
|
this.beanResolver = beanResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeanResolver getBeanResolver() {
|
||||||
|
return this.beanResolver;
|
||||||
|
}
|
||||||
|
|
||||||
public void setMethodResolvers(List<MethodResolver> methodResolvers) {
|
public void setMethodResolvers(List<MethodResolver> methodResolvers) {
|
||||||
this.methodResolvers = methodResolvers;
|
this.methodResolvers = methodResolvers;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ import junit.framework.Assert;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.expression.ParseException;
|
import org.springframework.expression.ParseException;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
|
||||||
import org.springframework.expression.spel.standard.SpelExpression;
|
import org.springframework.expression.spel.standard.SpelExpression;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse some expressions and check we get the AST we expect. Rather than inspecting each node in the AST, we ask it to
|
* Parse some expressions and check we get the AST we expect. Rather than inspecting each node in the AST, we ask it to
|
||||||
|
|
@ -267,18 +267,18 @@ public class ParsingTests {
|
||||||
// parseCheck("{'a','b','a','d','e'}.distinct()");
|
// parseCheck("{'a','b','a','d','e'}.distinct()");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // references
|
// references
|
||||||
// public void testReferences01() {
|
@Test
|
||||||
// parseCheck("@(foo)");
|
public void testReferences01() {
|
||||||
// }
|
parseCheck("@foo");
|
||||||
//
|
parseCheck("@'foo.bar'");
|
||||||
// public void testReferences02() {
|
parseCheck("@\"foo.bar.goo\"","@'foo.bar.goo'");
|
||||||
// parseCheck("@(p:foo)");
|
}
|
||||||
// }
|
|
||||||
//
|
@Test
|
||||||
// public void testReferences04() {
|
public void testReferences03() {
|
||||||
// parseCheck("@(a/b/c:foo)", "@(a.b.c:foo)");
|
parseCheck("@$$foo");
|
||||||
// }// normalized to '.' for separator in QualifiedIdentifier
|
}
|
||||||
|
|
||||||
// properties
|
// properties
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import junit.framework.Assert;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.expression.AccessException;
|
import org.springframework.expression.AccessException;
|
||||||
|
import org.springframework.expression.BeanResolver;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.EvaluationException;
|
import org.springframework.expression.EvaluationException;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
|
|
@ -350,6 +351,47 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
Assert.assertEquals("hello",name);
|
Assert.assertEquals("hello",name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** $ related identifiers */
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Test
|
||||||
|
public void testDollarPrefixedIdentifier_SPR7100() {
|
||||||
|
Holder h = new Holder();
|
||||||
|
StandardEvaluationContext eContext = new StandardEvaluationContext(h);
|
||||||
|
eContext.addPropertyAccessor(new MapAccessor());
|
||||||
|
h.map.put("$foo","wibble");
|
||||||
|
h.map.put("foo$bar","wobble");
|
||||||
|
h.map.put("foobar$$","wabble");
|
||||||
|
h.map.put("$","wubble");
|
||||||
|
h.map.put("$$","webble");
|
||||||
|
h.map.put("$_$","tribble");
|
||||||
|
String name = null;
|
||||||
|
Expression expr = null;
|
||||||
|
|
||||||
|
expr = new SpelExpressionParser().parseRaw("map.$foo");
|
||||||
|
name = expr.getValue(eContext,String.class);
|
||||||
|
Assert.assertEquals("wibble",name);
|
||||||
|
|
||||||
|
expr = new SpelExpressionParser().parseRaw("map.foo$bar");
|
||||||
|
name = expr.getValue(eContext,String.class);
|
||||||
|
Assert.assertEquals("wobble",name);
|
||||||
|
|
||||||
|
expr = new SpelExpressionParser().parseRaw("map.foobar$$");
|
||||||
|
name = expr.getValue(eContext,String.class);
|
||||||
|
Assert.assertEquals("wabble",name);
|
||||||
|
|
||||||
|
expr = new SpelExpressionParser().parseRaw("map.$");
|
||||||
|
name = expr.getValue(eContext,String.class);
|
||||||
|
Assert.assertEquals("wubble",name);
|
||||||
|
|
||||||
|
expr = new SpelExpressionParser().parseRaw("map.$$");
|
||||||
|
name = expr.getValue(eContext,String.class);
|
||||||
|
Assert.assertEquals("webble",name);
|
||||||
|
|
||||||
|
expr = new SpelExpressionParser().parseRaw("map.$_$");
|
||||||
|
name = expr.getValue(eContext,String.class);
|
||||||
|
Assert.assertEquals("tribble",name);
|
||||||
|
}
|
||||||
|
|
||||||
/** Should be accessing Goo.wibble field because 'bar' variable evaluates to "wibble" */
|
/** Should be accessing Goo.wibble field because 'bar' variable evaluates to "wibble" */
|
||||||
@Test
|
@Test
|
||||||
public void testIndexingAsAPropertyAccess_SPR6968_3() {
|
public void testIndexingAsAPropertyAccess_SPR6968_3() {
|
||||||
|
|
@ -393,6 +435,36 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
Assert.assertEquals("world",g.value);
|
Assert.assertEquals("world",g.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDollars() {
|
||||||
|
StandardEvaluationContext eContext = new StandardEvaluationContext(new XX());
|
||||||
|
Expression expr = null;
|
||||||
|
expr = new SpelExpressionParser().parseRaw("m['$foo']");
|
||||||
|
eContext.setVariable("file_name","$foo");
|
||||||
|
Assert.assertEquals("wibble",expr.getValue(eContext,String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDollars2() {
|
||||||
|
StandardEvaluationContext eContext = new StandardEvaluationContext(new XX());
|
||||||
|
Expression expr = null;
|
||||||
|
expr = new SpelExpressionParser().parseRaw("m[$foo]");
|
||||||
|
eContext.setVariable("file_name","$foo");
|
||||||
|
Assert.assertEquals("wibble",expr.getValue(eContext,String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
static class XX {
|
||||||
|
public Map<String,String> m;
|
||||||
|
|
||||||
|
public String floo ="bar";
|
||||||
|
|
||||||
|
public XX() {
|
||||||
|
m = new HashMap<String,String>();
|
||||||
|
m.put("$foo","wibble");
|
||||||
|
m.put("bar","siddle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static class Goo {
|
static class Goo {
|
||||||
|
|
||||||
public static Goo instance = new Goo();
|
public static Goo instance = new Goo();
|
||||||
|
|
@ -411,6 +483,11 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class Holder {
|
||||||
|
|
||||||
|
public Map map = new HashMap();
|
||||||
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
private void checkTemplateParsing(String expression, String expectedValue) throws Exception {
|
private void checkTemplateParsing(String expression, String expectedValue) throws Exception {
|
||||||
|
|
@ -452,5 +529,102 @@ public class SpringEL300Tests extends ExpressionTestCase {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// public void testFails() {
|
||||||
|
//
|
||||||
|
// StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
|
||||||
|
// evaluationContext.setVariable("target", new Foo2());
|
||||||
|
// for (int i = 0; i < 300000; i++) {
|
||||||
|
// evaluationContext.addPropertyAccessor(new MapAccessor());
|
||||||
|
// ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
// Expression expression = parser.parseExpression("#target.execute(payload)");
|
||||||
|
// Message message = new Message();
|
||||||
|
// message.setPayload(i+"");
|
||||||
|
// expression.getValue(evaluationContext, message);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
static class Foo2 {
|
||||||
|
public void execute(String str){
|
||||||
|
System.out.println("Value: " + str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Message{
|
||||||
|
private String payload;
|
||||||
|
|
||||||
|
public String getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayload(String payload) {
|
||||||
|
this.payload = payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bean resolver tests
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void beanResolution() {
|
||||||
|
StandardEvaluationContext eContext = new StandardEvaluationContext(new XX());
|
||||||
|
Expression expr = null;
|
||||||
|
|
||||||
|
// no resolver registered == exception
|
||||||
|
try {
|
||||||
|
expr = new SpelExpressionParser().parseRaw("@foo");
|
||||||
|
Assert.assertEquals("custard",expr.getValue(eContext,String.class));
|
||||||
|
} catch (SpelEvaluationException see) {
|
||||||
|
Assert.assertEquals(SpelMessage.NO_BEAN_RESOLVER_REGISTERED,see.getMessageCode());
|
||||||
|
Assert.assertEquals("foo",see.getInserts()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
eContext.setBeanResolver(new MyBeanResolver());
|
||||||
|
|
||||||
|
// bean exists
|
||||||
|
expr = new SpelExpressionParser().parseRaw("@foo");
|
||||||
|
Assert.assertEquals("custard",expr.getValue(eContext,String.class));
|
||||||
|
|
||||||
|
// bean does not exist
|
||||||
|
expr = new SpelExpressionParser().parseRaw("@bar");
|
||||||
|
Assert.assertEquals(null,expr.getValue(eContext,String.class));
|
||||||
|
|
||||||
|
// bean name will cause AccessException
|
||||||
|
expr = new SpelExpressionParser().parseRaw("@goo");
|
||||||
|
try {
|
||||||
|
Assert.assertEquals(null,expr.getValue(eContext,String.class));
|
||||||
|
} catch (SpelEvaluationException see) {
|
||||||
|
Assert.assertEquals(SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION,see.getMessageCode());
|
||||||
|
Assert.assertEquals("goo",see.getInserts()[0]);
|
||||||
|
Assert.assertTrue(see.getCause() instanceof AccessException);
|
||||||
|
Assert.assertTrue(((AccessException)see.getCause()).getMessage().startsWith("DONT"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// bean exists
|
||||||
|
expr = new SpelExpressionParser().parseRaw("@'foo.bar'");
|
||||||
|
Assert.assertEquals("trouble",expr.getValue(eContext,String.class));
|
||||||
|
|
||||||
|
// bean exists
|
||||||
|
try {
|
||||||
|
expr = new SpelExpressionParser().parseRaw("@378");
|
||||||
|
Assert.assertEquals("trouble",expr.getValue(eContext,String.class));
|
||||||
|
} catch (SpelParseException spe) {
|
||||||
|
Assert.assertEquals(SpelMessage.INVALID_BEAN_REFERENCE,spe.getMessageCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MyBeanResolver implements BeanResolver {
|
||||||
|
public Object resolve(EvaluationContext context, String beanname) throws AccessException {
|
||||||
|
if (beanname.equals("foo")) {
|
||||||
|
return "custard";
|
||||||
|
} else if (beanname.equals("foo.bar")) {
|
||||||
|
return "trouble";
|
||||||
|
} else if (beanname.equals("goo")) {
|
||||||
|
throw new AccessException("DONT ASK ME ABOUT GOO");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// end bean resolver tests
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue