2828 * Lex some input data into a stream of tokens that can then be parsed.
2929 *
3030 * @author Andy Clement
31+ * @author Juergen Hoeller
3132 * @author Phillip Webb
3233 * @since 3.0
3334 */
3435class Tokenizer {
3536
36- // if this is changed, it must remain sorted
37- private static final String [] ALTERNATIVE_OPERATOR_NAMES = { "DIV" , "EQ" , "GE" , "GT" ,
38- " LE" , "LT" , "MOD" , "NE" , "NOT" };
37+ // If this gets changed, it must remain sorted...
38+ private static final String [] ALTERNATIVE_OPERATOR_NAMES =
39+ { "DIV" , "EQ" , "GE" , "GT" , " LE" , "LT" , "MOD" , "NE" , "NOT" };
3940
4041 private static final byte FLAGS [] = new byte [256 ];
4142
@@ -64,29 +65,28 @@ class Tokenizer {
6465 }
6566
6667
67- String expressionString ;
68+ private String expressionString ;
6869
69- char [] toProcess ;
70+ private char [] charsToProcess ;
7071
71- int pos ;
72+ private int pos ;
7273
73- int max ;
74+ private int max ;
7475
75- List <Token > tokens = new ArrayList <>();
76+ private List <Token > tokens = new ArrayList <>();
7677
7778
7879 public Tokenizer (String inputData ) {
7980 this .expressionString = inputData ;
80- this .toProcess = (inputData + "\0 " ).toCharArray ();
81- this .max = this .toProcess .length ;
81+ this .charsToProcess = (inputData + "\0 " ).toCharArray ();
82+ this .max = this .charsToProcess .length ;
8283 this .pos = 0 ;
83- process ();
8484 }
8585
8686
87- public void process () {
87+ public List < Token > process () {
8888 while (this .pos < this .max ) {
89- char ch = this .toProcess [this .pos ];
89+ char ch = this .charsToProcess [this .pos ];
9090 if (isAlphabetic (ch )) {
9191 lexIdentifier ();
9292 }
@@ -190,9 +190,7 @@ else if (isTwoCharToken(TokenKind.PROJECT)) {
190190 break ;
191191 case '|' :
192192 if (!isTwoCharToken (TokenKind .SYMBOLIC_OR )) {
193- throw new InternalParseException (new SpelParseException (
194- this .expressionString , this .pos , SpelMessage .MISSING_CHARACTER ,
195- "|" ));
193+ raiseParseException (this .pos , SpelMessage .MISSING_CHARACTER , "|" );
196194 }
197195 pushPairToken (TokenKind .SYMBOLIC_OR );
198196 break ;
@@ -261,41 +259,38 @@ else if (isTwoCharToken(TokenKind.SAFE_NAVI)) {
261259 break ;
262260 case 0 :
263261 // hit sentinel at end of value
264- this .pos ++; // will take us to the end
262+ this .pos ++; // will take us to the end
265263 break ;
266264 case '\\' :
267- throw new InternalParseException (
268- new SpelParseException ( this . expressionString , this . pos , SpelMessage . UNEXPECTED_ESCAPE_CHAR )) ;
265+ raiseParseException ( this . pos , SpelMessage . UNEXPECTED_ESCAPE_CHAR );
266+ break ;
269267 default :
270268 throw new IllegalStateException ("Cannot handle (" + Integer .valueOf (ch ) + ") '" + ch + "'" );
271269 }
272270 }
273271 }
274- }
275-
276- public List <Token > getTokens () {
277272 return this .tokens ;
278273 }
279274
275+
280276 // STRING_LITERAL: '\''! (APOS|~'\'')* '\''!;
281277 private void lexQuotedStringLiteral () {
282278 int start = this .pos ;
283279 boolean terminated = false ;
284280 while (!terminated ) {
285281 this .pos ++;
286- char ch = this .toProcess [this .pos ];
282+ char ch = this .charsToProcess [this .pos ];
287283 if (ch == '\'' ) {
288284 // may not be the end if the char after is also a '
289- if (this .toProcess [this .pos + 1 ] == '\'' ) {
290- this .pos ++; // skip over that too, and continue
285+ if (this .charsToProcess [this .pos + 1 ] == '\'' ) {
286+ this .pos ++; // skip over that too, and continue
291287 }
292288 else {
293289 terminated = true ;
294290 }
295291 }
296- if (ch == 0 ) {
297- throw new InternalParseException (new SpelParseException (this .expressionString , start ,
298- SpelMessage .NON_TERMINATING_QUOTED_STRING ));
292+ if (isExhausted ()) {
293+ raiseParseException (start , SpelMessage .NON_TERMINATING_QUOTED_STRING );
299294 }
300295 }
301296 this .pos ++;
@@ -308,19 +303,18 @@ private void lexDoubleQuotedStringLiteral() {
308303 boolean terminated = false ;
309304 while (!terminated ) {
310305 this .pos ++;
311- char ch = this .toProcess [this .pos ];
306+ char ch = this .charsToProcess [this .pos ];
312307 if (ch == '"' ) {
313308 // may not be the end if the char after is also a "
314- if (this .toProcess [this .pos + 1 ] == '"' ) {
315- this .pos ++; // skip over that too, and continue
309+ if (this .charsToProcess [this .pos + 1 ] == '"' ) {
310+ this .pos ++; // skip over that too, and continue
316311 }
317312 else {
318313 terminated = true ;
319314 }
320315 }
321- if (ch == 0 ) {
322- throw new InternalParseException (new SpelParseException (this .expressionString ,
323- start , SpelMessage .NON_TERMINATING_DOUBLE_QUOTED_STRING ));
316+ if (isExhausted ()) {
317+ raiseParseException (start , SpelMessage .NON_TERMINATING_DOUBLE_QUOTED_STRING );
324318 }
325319 }
326320 this .pos ++;
@@ -346,7 +340,7 @@ private void lexDoubleQuotedStringLiteral() {
346340 private void lexNumericLiteral (boolean firstCharIsZero ) {
347341 boolean isReal = false ;
348342 int start = this .pos ;
349- char ch = this .toProcess [this .pos + 1 ];
343+ char ch = this .charsToProcess [this .pos + 1 ];
350344 boolean isHex = ch == 'x' || ch == 'X' ;
351345
352346 // deal with hexadecimal
@@ -355,7 +349,7 @@ private void lexNumericLiteral(boolean firstCharIsZero) {
355349 do {
356350 this .pos ++;
357351 }
358- while (isHexadecimalDigit (this .toProcess [this .pos ]));
352+ while (isHexadecimalDigit (this .charsToProcess [this .pos ]));
359353 if (isChar ('L' , 'l' )) {
360354 pushHexIntToken (subarray (start + 2 , this .pos ), true , start , this .pos );
361355 this .pos ++;
@@ -372,18 +366,18 @@ private void lexNumericLiteral(boolean firstCharIsZero) {
372366 do {
373367 this .pos ++;
374368 }
375- while (isDigit (this .toProcess [this .pos ]));
369+ while (isDigit (this .charsToProcess [this .pos ]));
376370
377371 // a '.' indicates this number is a real
378- ch = this .toProcess [this .pos ];
372+ ch = this .charsToProcess [this .pos ];
379373 if (ch == '.' ) {
380374 isReal = true ;
381375 int dotpos = this .pos ;
382376 // carry on consuming digits
383377 do {
384378 this .pos ++;
385379 }
386- while (isDigit (this .toProcess [this .pos ]));
380+ while (isDigit (this .charsToProcess [this .pos ]));
387381 if (this .pos == dotpos + 1 ) {
388382 // the number is something like '3.'. It is really an int but may be
389383 // part of something like '3.toString()'. In this case process it as
@@ -398,19 +392,18 @@ private void lexNumericLiteral(boolean firstCharIsZero) {
398392
399393 // Now there may or may not be an exponent
400394
401- // is it a long ?
395+ // Is it a long ?
402396 if (isChar ('L' , 'l' )) {
403- if (isReal ) { // 3.4L - not allowed
404- throw new InternalParseException (new SpelParseException (this .expressionString ,
405- start , SpelMessage .REAL_CANNOT_BE_LONG ));
397+ if (isReal ) { // 3.4L - not allowed
398+ raiseParseException (start , SpelMessage .REAL_CANNOT_BE_LONG );
406399 }
407400 pushIntToken (subarray (start , endOfNumber ), true , start , endOfNumber );
408401 this .pos ++;
409402 }
410- else if (isExponentChar (this .toProcess [this .pos ])) {
411- isReal = true ; // if it wasn't before, it is now
403+ else if (isExponentChar (this .charsToProcess [this .pos ])) {
404+ isReal = true ; // if it wasn't before, it is now
412405 this .pos ++;
413- char possibleSign = this .toProcess [this .pos ];
406+ char possibleSign = this .charsToProcess [this .pos ];
414407 if (isSign (possibleSign )) {
415408 this .pos ++;
416409 }
@@ -419,19 +412,19 @@ else if (isExponentChar(this.toProcess[this.pos])) {
419412 do {
420413 this .pos ++;
421414 }
422- while (isDigit (this .toProcess [this .pos ]));
415+ while (isDigit (this .charsToProcess [this .pos ]));
423416 boolean isFloat = false ;
424- if (isFloatSuffix (this .toProcess [this .pos ])) {
417+ if (isFloatSuffix (this .charsToProcess [this .pos ])) {
425418 isFloat = true ;
426419 endOfNumber = ++this .pos ;
427420 }
428- else if (isDoubleSuffix (this .toProcess [this .pos ])) {
421+ else if (isDoubleSuffix (this .charsToProcess [this .pos ])) {
429422 endOfNumber = ++this .pos ;
430423 }
431424 pushRealToken (subarray (start , this .pos ), isFloat , start , this .pos );
432425 }
433426 else {
434- ch = this .toProcess [this .pos ];
427+ ch = this .charsToProcess [this .pos ];
435428 boolean isFloat = false ;
436429 if (isFloatSuffix (ch )) {
437430 isReal = true ;
@@ -456,7 +449,7 @@ private void lexIdentifier() {
456449 do {
457450 this .pos ++;
458451 }
459- while (isIdentifier (this .toProcess [this .pos ]));
452+ while (isIdentifier (this .charsToProcess [this .pos ]));
460453 char [] subarray = subarray (start , this .pos );
461454
462455 // Check if this is the alternative (textual) representation of an operator (see
@@ -484,14 +477,10 @@ private void pushIntToken(char[] data, boolean isLong, int start, int end) {
484477 private void pushHexIntToken (char [] data , boolean isLong , int start , int end ) {
485478 if (data .length == 0 ) {
486479 if (isLong ) {
487- throw new InternalParseException (new SpelParseException (this .expressionString ,
488- start , SpelMessage .NOT_A_LONG , this .expressionString .substring (start ,
489- end + 1 )));
480+ raiseParseException (start , SpelMessage .NOT_A_LONG , this .expressionString .substring (start , end + 1 ));
490481 }
491482 else {
492- throw new InternalParseException (new SpelParseException (this .expressionString ,
493- start , SpelMessage .NOT_AN_INTEGER , this .expressionString .substring (
494- start , end )));
483+ raiseParseException (start , SpelMessage .NOT_AN_INTEGER , this .expressionString .substring (start , end ));
495484 }
496485 }
497486 if (isLong ) {
@@ -513,7 +502,7 @@ private void pushRealToken(char[] data, boolean isFloat, int start, int end) {
513502
514503 private char [] subarray (int start , int end ) {
515504 char [] result = new char [end - start ];
516- System .arraycopy (this .toProcess , start , result , 0 , end - start );
505+ System .arraycopy (this .charsToProcess , start , result , 0 , end - start );
517506 return result ;
518507 }
519508
@@ -522,8 +511,8 @@ private char[] subarray(int start, int end) {
522511 */
523512 private boolean isTwoCharToken (TokenKind kind ) {
524513 return (kind .tokenChars .length == 2 &&
525- this .toProcess [this .pos ] == kind .tokenChars [0 ] &&
526- this .toProcess [this .pos + 1 ] == kind .tokenChars [1 ]);
514+ this .charsToProcess [this .pos ] == kind .tokenChars [0 ] &&
515+ this .charsToProcess [this .pos + 1 ] == kind .tokenChars [1 ]);
527516 }
528517
529518 /**
@@ -552,7 +541,7 @@ private boolean isIdentifier(char ch) {
552541 }
553542
554543 private boolean isChar (char a , char b ) {
555- char ch = this .toProcess [this .pos ];
544+ char ch = this .charsToProcess [this .pos ];
556545 return ch == a || ch == b ;
557546 }
558547
@@ -593,4 +582,12 @@ private boolean isHexadecimalDigit(char ch) {
593582 return (FLAGS [ch ] & IS_HEXDIGIT ) != 0 ;
594583 }
595584
585+ private boolean isExhausted () {
586+ return (this .pos == this .max - 1 );
587+ }
588+
589+ private void raiseParseException (int start , SpelMessage msg , Object ... inserts ) {
590+ throw new InternalParseException (new SpelParseException (this .expressionString , start , msg , inserts ));
591+ }
592+
596593}
0 commit comments