Skip to content

Commit

Permalink
Support letters other than A-Z in identifiers in SpEL
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sbrannen committed Jun 6, 2023
1 parent cd610b3 commit c8c8f57
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* @author Andy Clement
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
*/
class Tokenizer {
Expand All @@ -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;
Expand All @@ -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;
}
}


Expand Down Expand Up @@ -569,10 +562,7 @@ private boolean isDigit(char ch) {
}

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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ void supportedCharactersInIdentifiers() {
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
Expand All @@ -75,24 +89,6 @@ void unsupportedCharactersInIdentifiers() {
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
Expand Down

0 comments on commit c8c8f57

Please sign in to comment.