Merge pull request #113 from aclement/SPR-9613

* SPR-9613:
  Support case-insensitive null literals in SpEL
This commit is contained in:
Sam Brannen 2012-08-04 01:50:36 +02:00
commit 3fbcebb82e
3 changed files with 134 additions and 120 deletions

View File

@ -513,7 +513,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private boolean maybeEatNullReference() {
if (peekToken(TokenKind.IDENTIFIER)) {
Token nullToken = peekToken();
if (!nullToken.stringValue().equals("null")) {
if (!nullToken.stringValue().toLowerCase().equals("null")) {
return false;
}
nextToken();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2012 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.
@ -16,12 +16,13 @@
package org.springframework.expression.spel;
import static org.junit.Assert.*;
import java.util.List;
import java.util.Map;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
@ -38,6 +39,7 @@ import org.springframework.expression.spel.testresources.TestPerson;
*
* @author Andy Clement
* @author Mark Fisher
* @author Sam Brannen
* @since 3.0
*/
public class EvaluationTests extends ExpressionTestCase {
@ -49,62 +51,53 @@ public class EvaluationTests extends ExpressionTestCase {
TestClass testClass = new TestClass();
Object o = null;
o = expression.getValue(new StandardEvaluationContext(testClass));
Assert.assertEquals("",o);
assertEquals("", o);
o = parser.parseExpression("list[3]").getValue(new StandardEvaluationContext(testClass));
Assert.assertEquals("",o);
Assert.assertEquals(4, testClass.list.size());
assertEquals("", o);
assertEquals(4, testClass.list.size());
try {
o = parser.parseExpression("list2[3]").getValue(new StandardEvaluationContext(testClass));
Assert.fail();
fail();
} catch (EvaluationException ee) {
ee.printStackTrace();
// success!
}
o = parser.parseExpression("foo[3]").getValue(new StandardEvaluationContext(testClass));
Assert.assertEquals("",o);
Assert.assertEquals(4, testClass.getFoo().size());
assertEquals("", o);
assertEquals(4, testClass.getFoo().size());
}
@Test
public void testCreateMapsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
@Test(expected = SpelEvaluationException.class)
public void testCreateMapsOnAttemptToIndexNull01() throws Exception {
TestClass testClass = new TestClass();
StandardEvaluationContext ctx = new StandardEvaluationContext(testClass);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Object o = null;
o = parser.parseExpression("map['a']").getValue(ctx);
Assert.assertNull(o);
assertNull(o);
o = parser.parseExpression("map").getValue(ctx);
Assert.assertNotNull(o);
assertNotNull(o);
try {
o = parser.parseExpression("map2['a']").getValue(ctx);
// fail!
Assert.fail("map2 should be null, there is no setter");
} catch (Exception e) {
// success!
}
// map2 should be null, there is no setter
}
@Test
public void testCreateObjectsOnAttemptToReferenceNull() throws EvaluationException, ParseException {
// wibble2 should be null (cannot be initialized dynamically), there is no setter
@Test(expected = SpelEvaluationException.class)
public void testCreateObjectsOnAttemptToReferenceNull() throws Exception {
TestClass testClass = new TestClass();
StandardEvaluationContext ctx = new StandardEvaluationContext(testClass);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Object o = null;
o = parser.parseExpression("wibble.bar").getValue(ctx);
Assert.assertEquals("hello",o);
assertEquals("hello", o);
o = parser.parseExpression("wibble").getValue(ctx);
Assert.assertNotNull(o);
assertNotNull(o);
try {
o = parser.parseExpression("wibble2.bar").getValue(ctx);
// fail!
Assert.fail("wibble2 should be null (cannot be initialized dynamically), there is no setter");
} catch (Exception e) {
// success!
}
}
static class TestClass {
public Foo wibble;
@ -114,11 +107,10 @@ public class EvaluationTests extends ExpressionTestCase {
public List<String> list;
public List list2;
private Map map2;
private List<String> foo;
public Map getMap2() { return this.map2; }
public Foo getWibble2() { return this.wibble2; }
// public void setMap2(Map m) { this.map2 = m; }
private List<String> foo;
public List<String> getFoo() { return this.foo; }
public void setFoo(List<String> newfoo) { this.foo = newfoo; }
}
@ -128,6 +120,7 @@ public class EvaluationTests extends ExpressionTestCase {
public String bar = "hello";
}
@Test
public void testElvis01() {
evaluate("'Andy'?:'Dave'", "Andy", String.class);
@ -234,16 +227,15 @@ public class EvaluationTests extends ExpressionTestCase {
public void testRogueTrailingDotCausesNPE_SPR6866() {
try {
new SpelExpressionParser().parseExpression("placeOfBirth.foo.");
Assert.fail("Should have failed to parse");
fail("Should have failed to parse");
} catch (ParseException e) {
Assert.assertTrue(e instanceof SpelParseException);
assertTrue(e instanceof SpelParseException);
SpelParseException spe = (SpelParseException) e;
Assert.assertEquals(SpelMessage.OOD,spe.getMessageCode());
Assert.assertEquals(16,spe.getPosition());
assertEquals(SpelMessage.OOD, spe.getMessageCode());
assertEquals(16, spe.getPosition());
}
}
// nested properties
@Test
public void testPropertiesNested01() {
@ -259,10 +251,10 @@ public class EvaluationTests extends ExpressionTestCase {
public void testPropertiesNested03() throws ParseException {
try {
new SpelExpressionParser().parseRaw("placeOfBirth.23");
Assert.fail();
fail();
} catch (SpelParseException spe) {
Assert.assertEquals(spe.getMessageCode(), SpelMessage.UNEXPECTED_DATA_AFTER_DOT);
Assert.assertEquals("23", spe.getInserts()[0]);
assertEquals(spe.getMessageCode(), SpelMessage.UNEXPECTED_DATA_AFTER_DOT);
assertEquals("23", spe.getInserts()[0]);
}
}
@ -293,15 +285,15 @@ public class EvaluationTests extends ExpressionTestCase {
// repeated evaluation to drive use of cached executor
SpelExpression expr = (SpelExpression) parser.parseExpression("new String('wibble')");
String newString = expr.getValue(String.class);
Assert.assertEquals("wibble",newString);
assertEquals("wibble", newString);
newString = expr.getValue(String.class);
Assert.assertEquals("wibble",newString);
assertEquals("wibble", newString);
// not writable
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
assertFalse(expr.isWritable(new StandardEvaluationContext()));
// ast
Assert.assertEquals("new String('wibble')",expr.toStringAST());
assertEquals("new String('wibble')", expr.toStringAST());
}
// unary expressions
@ -368,13 +360,14 @@ public class EvaluationTests extends ExpressionTestCase {
@Test
public void testTernaryOperator03() {
evaluateAndCheckError("'hello'?1:2", SpelMessage.TYPE_CONVERSION_ERROR); // cannot convert String to boolean
// cannot convert String to boolean
evaluateAndCheckError("'hello'?1:2", SpelMessage.TYPE_CONVERSION_ERROR);
}
@Test
public void testTernaryOperator04() throws Exception {
Expression expr = parser.parseExpression("1>2?3:4");
Assert.assertFalse(expr.isWritable(eContext));
assertFalse(expr.isWritable(eContext));
}
@Test
@ -397,7 +390,8 @@ public class EvaluationTests extends ExpressionTestCase {
@Test
public void ctorCallWithRootReferenceThroughParameter() {
evaluate("new org.springframework.expression.spel.testresources.PlaceOfBirth(inventions[0].toString()).city", "Telephone repeater", String.class);
evaluate("new org.springframework.expression.spel.testresources.PlaceOfBirth(inventions[0].toString()).city",
"Telephone repeater", String.class);
}
@Test
@ -415,10 +409,10 @@ public class EvaluationTests extends ExpressionTestCase {
evaluate("'christian'[8]", "n", String.class);
}
@Test
public void testIndexerError() {
evaluateAndCheckError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
evaluateAndCheckError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",
SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
}
@Test
@ -451,12 +445,12 @@ public class EvaluationTests extends ExpressionTestCase {
@Test
public void testTypeReferencesAndQualifiedIdentifierCaching() throws Exception {
SpelExpression expr = (SpelExpression) parser.parseExpression("T(java.lang.String)");
Assert.assertFalse(expr.isWritable(new StandardEvaluationContext()));
Assert.assertEquals("T(java.lang.String)",expr.toStringAST());
Assert.assertEquals(String.class,expr.getValue(Class.class));
assertFalse(expr.isWritable(new StandardEvaluationContext()));
assertEquals("T(java.lang.String)", expr.toStringAST());
assertEquals(String.class, expr.getValue(Class.class));
// use cached QualifiedIdentifier:
Assert.assertEquals("T(java.lang.String)",expr.toStringAST());
Assert.assertEquals(String.class,expr.getValue(Class.class));
assertEquals("T(java.lang.String)", expr.toStringAST());
assertEquals(String.class, expr.getValue(Class.class));
}
@Test
@ -490,49 +484,50 @@ public class EvaluationTests extends ExpressionTestCase {
evaluateAndAskForReturnType("3*4+5", "17", String.class);
}
@Test
public void testAdvancedNumerics() throws Exception {
int twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Integer.class);
Assert.assertEquals(24,twentyFour);
assertEquals(24, twentyFour);
double one = parser.parseExpression("8.0 / 5e0 % 2").getValue(Double.class);
Assert.assertEquals(1.6d,one);
assertEquals(1.6d, one, 0);
int o = parser.parseExpression("8.0 / 5e0 % 2").getValue(Integer.class);
Assert.assertEquals(1,o);
assertEquals(1, o);
int sixteen = parser.parseExpression("-2 ^ 4").getValue(Integer.class);
Assert.assertEquals(16,sixteen);
assertEquals(16, sixteen);
int minusFortyFive = parser.parseExpression("1+2-3*8^2/2/2").getValue(Integer.class);
Assert.assertEquals(-45,minusFortyFive);
assertEquals(-45, minusFortyFive);
}
@Test
public void testComparison() throws Exception {
EvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
boolean trueValue = parser.parseExpression("T(java.util.Date) == Birthdate.Class").getValue(context, Boolean.class);
Assert.assertTrue(trueValue);
boolean trueValue = parser.parseExpression("T(java.util.Date) == Birthdate.Class").getValue(context,
Boolean.class);
assertTrue(trueValue);
}
@Test
public void testResolvingList() throws Exception {
StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
try {
Assert.assertFalse(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class));
Assert.fail("should have failed to find List");
assertFalse(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class));
fail("should have failed to find List");
} catch (EvaluationException ee) {
// success - List not found
}
((StandardTypeLocator) context.getTypeLocator()).registerImport("java.util");
Assert.assertTrue(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class));
assertTrue(parser.parseExpression("T(List)!=null").getValue(context, Boolean.class));
}
@Test
public void testResolvingString() throws Exception {
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
Assert.assertEquals(String.class,stringClass);
Class<?> stringClass = parser.parseExpression("T(String)").getValue(Class.class);
assertEquals(String.class, stringClass);
}
/**
* SPR-6984: attempting to index a collection on write using an index that doesn't currently exist in the collection (address.crossStreets[0] below)
* SPR-6984: attempting to index a collection on write using an index that
* doesn't currently exist in the collection (address.crossStreets[0] below)
*/
@Test
public void initializingCollectionElementsOnWrite() throws Exception {
@ -542,20 +537,38 @@ public class EvaluationTests extends ExpressionTestCase {
ExpressionParser parser = new SpelExpressionParser(config);
Expression expression = parser.parseExpression("name");
expression.setValue(context, "Oleg");
Assert.assertEquals("Oleg",person.getName());
assertEquals("Oleg", person.getName());
expression = parser.parseExpression("address.street");
expression.setValue(context, "123 High St");
Assert.assertEquals("123 High St",person.getAddress().getStreet());
assertEquals("123 High St", person.getAddress().getStreet());
expression = parser.parseExpression("address.crossStreets[0]");
expression.setValue(context, "Blah");
Assert.assertEquals("Blah",person.getAddress().getCrossStreets().get(0));
assertEquals("Blah", person.getAddress().getCrossStreets().get(0));
expression = parser.parseExpression("address.crossStreets[3]");
expression.setValue(context, "Wibble");
Assert.assertEquals("Blah",person.getAddress().getCrossStreets().get(0));
Assert.assertEquals("Wibble",person.getAddress().getCrossStreets().get(3));
assertEquals("Blah", person.getAddress().getCrossStreets().get(0));
assertEquals("Wibble", person.getAddress().getCrossStreets().get(3));
}
/**
* Verifies behavior requested in SPR-9613.
*/
@Test
public void caseInsensitiveNullLiterals() {
ExpressionParser parser = new SpelExpressionParser();
Expression exp;
exp = parser.parseExpression("null");
assertNull(exp.getValue());
exp = parser.parseExpression("NULL");
assertNull(exp.getValue());
exp = parser.parseExpression("NuLl");
assertNull(exp.getValue());
}
}

View File

@ -9,6 +9,7 @@ Changes in version 3.2 M2 (2012-08-xx)
* spring-test module now depends on junit:junit-dep (SPR-6966)
* now inferring return type of parameterized factory methods (SPR-9493)
* SpEL Tokenizer now supports methods on integers (SPR-9612)
* introduced support for case-insensitive null literals in SpEL expressions (SPR-9613)
* now using BufferedInputStream in SimpleMetaDataReader to double performance (SPR-9528)
* introduced "repeatCount" property in Quartz SimpleTriggerFactoryBean (SPR-9521)
* introduced "jtaTransactionManager" property in Hibernate 4 LocalSessionFactoryBean/Builder (SPR-9480)