diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index 14f0bfb9b9..23d616e1bc 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -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"); * you may not use this file except in compliance with the License. @@ -104,6 +104,7 @@ public enum SpelMessage { MISSING_ARRAY_DIMENSION(Kind.ERROR, 1063, "A required array dimension has not been specified"), // INITIALIZER_LENGTH_INCORRECT( Kind.ERROR, 1064, "array initializer size does not match array dimensions"), // + UNEXPECTED_ESCAPE_CHAR(Kind.ERROR,1065,"unexpected escape character."); ; private Kind kind; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java index 2e3ff12dc4..efcd829726 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java @@ -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"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ public class StringLiteral extends Literal { super(payload,pos); // TODO should these have been skipped being created by the parser rules? or not? value = value.substring(1, value.length() - 1); - this.value = new TypedValue(value.replaceAll("''", "'")); + this.value = new TypedValue(value.replaceAll("''", "'").replaceAll("\"\"", "\"")); } @Override diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java index 5471e4c5fc..0654fbe3eb 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java @@ -199,6 +199,8 @@ class Tokenizer { // hit sentinel at end of value pos++; // will take us to the end break; + case '\\': + throw new InternalParseException(new SpelParseException(expressionString,pos,SpelMessage.UNEXPECTED_ESCAPE_CHAR)); default: throw new IllegalStateException("Cannot handle ("+Integer.valueOf(ch)+") '"+ch+"'"); } @@ -241,7 +243,12 @@ class Tokenizer { pos++; char ch = toProcess[pos]; if (ch=='"') { - terminated = true; + // may not be the end if the char after is also a " + if (toProcess[pos+1]=='"') { + pos++; // skip over that too, and continue + } else { + terminated = true; + } } if (ch==0) { throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING)); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java index 3ee78dbd81..66552f4ad4 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java @@ -261,6 +261,25 @@ public class SpelParserTests { assertEquals("ho", expr.getValue()); } + @Test + public void testStringLiterals_DoubleQuotes_spr9620() throws Exception { + SpelExpression expr = new SpelExpressionParser().parseRaw("\"double quote: \"\".\""); + assertEquals("double quote: \".", expr.getValue()); + expr = new SpelExpressionParser().parseRaw("\"hello \"\" world\""); + assertEquals("hello \" world", expr.getValue()); + } + + @Test + public void testStringLiterals_DoubleQuotes_spr9620_2() throws Exception { + try { + new SpelExpressionParser().parseRaw("\"double quote: \\\"\\\".\""); + fail("Should have failed"); + } catch (SpelParseException spe) { + assertEquals(17, spe.getPosition()); + assertEquals(SpelMessage.UNEXPECTED_ESCAPE_CHAR, spe.getMessageCode()); + } + } + @Test public void positionalInformation() throws EvaluationException, ParseException { SpelExpression expr = new SpelExpressionParser().parseRaw("true and true or false"); diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 69c7f0decc..fcee310f59 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -10,6 +10,7 @@ Changes in version 3.2 M2 (2012-08-xx) * now inferring return type of generic factory methods (SPR-9493) * SpEL now supports method invocations on integers (SPR-9612) * SpEL now supports symbolic boolean operators for OR and AND (SPR-9614) +* SpEL now supports nested double quotes in expressions (SPR-9620) * 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)