Support letters other than A-Z in identifiers in SpEL

Prior to this commit, only the letters 'A' - 'Z' (ignoring case) were
supported in identifiers (i.e., property, field, and variable names).

This known (yet undocumented) limitation prevented the use of characters
such as 'ü', 'ñ', 'é' as well as letters from other character sets such
as Chinese, Japanese, Cyrillic, etc.

This commit lifts that restriction by delegating to Character.isLetter()
to determine if a character in a SpEL expression is a letter.

Closes gh-30580
This commit is contained in:
Sam Brannen
2023-06-06 14:14:15 +02:00
parent cd610b3ed1
commit c8c8f5722b
2 changed files with 16 additions and 30 deletions

View File

@@ -30,6 +30,7 @@ import org.springframework.expression.spel.SpelParseException;
* @author Andy Clement
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
*/
class Tokenizer {
@@ -44,8 +45,6 @@ class Tokenizer {
private static final byte IS_HEXDIGIT = 0x02;
private static final byte IS_ALPHA = 0x04;
static {
for (int ch = '0'; ch <= '9'; ch++) {
FLAGS[ch] |= IS_DIGIT | IS_HEXDIGIT;
@@ -56,12 +55,6 @@ class Tokenizer {
for (int ch = 'a'; ch <= 'f'; ch++) {
FLAGS[ch] |= IS_HEXDIGIT;
}
for (int ch = 'A'; ch <= 'Z'; ch++) {
FLAGS[ch] |= IS_ALPHA;
}
for (int ch = 'a'; ch <= 'z'; ch++) {
FLAGS[ch] |= IS_ALPHA;
}
}
@@ -569,10 +562,7 @@ class Tokenizer {
}
private boolean isAlphabetic(char ch) {
if (ch > 255) {
return false;
}
return (FLAGS[ch] & IS_ALPHA) != 0;
return Character.isLetter(ch);
}
private boolean isHexadecimalDigit(char ch) {

View File

@@ -67,6 +67,20 @@ class ParsingTests {
parseCheck("person_1.Age");
parseCheck("person_1.__age");
parseCheck("Person_1.get__age()");
// German characters
parseCheck("begrüssung");
parseCheck("#begrüssung");
parseCheck("begrüssung[1]", "begrüssung.[1]"); // extra "." is needed due to erroneous logic in CompoundExpression.toStringAST()
parseCheck("service.begrüssung");
parseCheck("service.getBegrüssung()");
parseCheck("Spaß");
// Spanish characters
parseCheck("buenos_sueños");
// Chinese characters
parseCheck("have乐趣()");
}
@Test
@@ -75,24 +89,6 @@ class ParsingTests {
assertThatIllegalStateException()
.isThrownBy(() -> parser.parseRaw("apple~banana"))
.withMessage("Unsupported character '~' (126) encountered at position 6 in expression.");
// German characters
assertThatIllegalStateException()
.isThrownBy(() -> parser.parseRaw("begrüssung"))
.withMessage("Unsupported character 'ü' (252) encountered at position 5 in expression.");
assertThatIllegalStateException()
.isThrownBy(() -> parser.parseRaw("Spaß"))
.withMessage("Unsupported character 'ß' (223) encountered at position 4 in expression.");
// Spanish characters
assertThatIllegalStateException()
.isThrownBy(() -> parser.parseRaw("buenos_sueños"))
.withMessage("Unsupported character 'ñ' (241) encountered at position 11 in expression.");
// Chinese characters
assertThatIllegalStateException()
.isThrownBy(() -> parser.parseRaw("have乐趣()"))
.withMessage("Unsupported character '乐' (20048) encountered at position 5 in expression.");
}
@Test