Wed, 10 Oct 2012 18:44:21 -0700
8000310: Clean up use of StringBuffer in langtools
Reviewed-by: bpatel
1 /*
2 * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.tools.javac.parser;
28 import com.sun.tools.javac.code.Source;
29 import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
30 import com.sun.tools.javac.util.*;
32 import java.nio.CharBuffer;
34 import static com.sun.tools.javac.parser.Tokens.*;
35 import static com.sun.tools.javac.util.LayoutCharacters.*;
37 /** The lexical analyzer maps an input stream consisting of
38 * ASCII characters and Unicode escapes into a token sequence.
39 *
40 * <p><b>This is NOT part of any supported API.
41 * If you write code that depends on this, you do so at your own risk.
42 * This code and its internal interfaces are subject to change or
43 * deletion without notice.</b>
44 */
45 public class JavaTokenizer {
47 private static boolean scannerDebug = false;
49 /** Allow hex floating-point literals.
50 */
51 private boolean allowHexFloats;
53 /** Allow binary literals.
54 */
55 private boolean allowBinaryLiterals;
57 /** Allow underscores in literals.
58 */
59 private boolean allowUnderscoresInLiterals;
61 /** The source language setting.
62 */
63 private Source source;
65 /** The log to be used for error reporting.
66 */
67 private final Log log;
69 /** The token factory. */
70 private final Tokens tokens;
72 /** The token kind, set by nextToken().
73 */
74 protected TokenKind tk;
76 /** The token's radix, set by nextToken().
77 */
78 protected int radix;
80 /** The token's name, set by nextToken().
81 */
82 protected Name name;
84 /** The position where a lexical error occurred;
85 */
86 protected int errPos = Position.NOPOS;
88 /** The Unicode reader (low-level stream reader).
89 */
90 protected UnicodeReader reader;
92 protected ScannerFactory fac;
94 private static final boolean hexFloatsWork = hexFloatsWork();
95 private static boolean hexFloatsWork() {
96 try {
97 Float.valueOf("0x1.0p1");
98 return true;
99 } catch (NumberFormatException ex) {
100 return false;
101 }
102 }
104 /**
105 * Create a scanner from the input array. This method might
106 * modify the array. To avoid copying the input array, ensure
107 * that {@code inputLength < input.length} or
108 * {@code input[input.length -1]} is a white space character.
109 *
110 * @param fac the factory which created this Scanner
111 * @param buf the input, might be modified
112 * Must be positive and less than or equal to input.length.
113 */
114 protected JavaTokenizer(ScannerFactory fac, CharBuffer buf) {
115 this(fac, new UnicodeReader(fac, buf));
116 }
118 protected JavaTokenizer(ScannerFactory fac, char[] buf, int inputLength) {
119 this(fac, new UnicodeReader(fac, buf, inputLength));
120 }
122 protected JavaTokenizer(ScannerFactory fac, UnicodeReader reader) {
123 this.fac = fac;
124 this.log = fac.log;
125 this.tokens = fac.tokens;
126 this.source = fac.source;
127 this.reader = reader;
128 this.allowBinaryLiterals = source.allowBinaryLiterals();
129 this.allowHexFloats = source.allowHexFloats();
130 this.allowUnderscoresInLiterals = source.allowUnderscoresInLiterals();
131 }
133 /** Report an error at the given position using the provided arguments.
134 */
135 protected void lexError(int pos, String key, Object... args) {
136 log.error(pos, key, args);
137 tk = TokenKind.ERROR;
138 errPos = pos;
139 }
141 /** Read next character in character or string literal and copy into sbuf.
142 */
143 private void scanLitChar(int pos) {
144 if (reader.ch == '\\') {
145 if (reader.peekChar() == '\\' && !reader.isUnicode()) {
146 reader.skipChar();
147 reader.putChar('\\', true);
148 } else {
149 reader.scanChar();
150 switch (reader.ch) {
151 case '0': case '1': case '2': case '3':
152 case '4': case '5': case '6': case '7':
153 char leadch = reader.ch;
154 int oct = reader.digit(pos, 8);
155 reader.scanChar();
156 if ('0' <= reader.ch && reader.ch <= '7') {
157 oct = oct * 8 + reader.digit(pos, 8);
158 reader.scanChar();
159 if (leadch <= '3' && '0' <= reader.ch && reader.ch <= '7') {
160 oct = oct * 8 + reader.digit(pos, 8);
161 reader.scanChar();
162 }
163 }
164 reader.putChar((char)oct);
165 break;
166 case 'b':
167 reader.putChar('\b', true); break;
168 case 't':
169 reader.putChar('\t', true); break;
170 case 'n':
171 reader.putChar('\n', true); break;
172 case 'f':
173 reader.putChar('\f', true); break;
174 case 'r':
175 reader.putChar('\r', true); break;
176 case '\'':
177 reader.putChar('\'', true); break;
178 case '\"':
179 reader.putChar('\"', true); break;
180 case '\\':
181 reader.putChar('\\', true); break;
182 default:
183 lexError(reader.bp, "illegal.esc.char");
184 }
185 }
186 } else if (reader.bp != reader.buflen) {
187 reader.putChar(true);
188 }
189 }
191 private void scanDigits(int pos, int digitRadix) {
192 char saveCh;
193 int savePos;
194 do {
195 if (reader.ch != '_') {
196 reader.putChar(false);
197 } else {
198 if (!allowUnderscoresInLiterals) {
199 lexError(pos, "unsupported.underscore.lit", source.name);
200 allowUnderscoresInLiterals = true;
201 }
202 }
203 saveCh = reader.ch;
204 savePos = reader.bp;
205 reader.scanChar();
206 } while (reader.digit(pos, digitRadix) >= 0 || reader.ch == '_');
207 if (saveCh == '_')
208 lexError(savePos, "illegal.underscore");
209 }
211 /** Read fractional part of hexadecimal floating point number.
212 */
213 private void scanHexExponentAndSuffix(int pos) {
214 if (reader.ch == 'p' || reader.ch == 'P') {
215 reader.putChar(true);
216 skipIllegalUnderscores();
217 if (reader.ch == '+' || reader.ch == '-') {
218 reader.putChar(true);
219 }
220 skipIllegalUnderscores();
221 if ('0' <= reader.ch && reader.ch <= '9') {
222 scanDigits(pos, 10);
223 if (!allowHexFloats) {
224 lexError(pos, "unsupported.fp.lit", source.name);
225 allowHexFloats = true;
226 }
227 else if (!hexFloatsWork)
228 lexError(pos, "unsupported.cross.fp.lit");
229 } else
230 lexError(pos, "malformed.fp.lit");
231 } else {
232 lexError(pos, "malformed.fp.lit");
233 }
234 if (reader.ch == 'f' || reader.ch == 'F') {
235 reader.putChar(true);
236 tk = TokenKind.FLOATLITERAL;
237 radix = 16;
238 } else {
239 if (reader.ch == 'd' || reader.ch == 'D') {
240 reader.putChar(true);
241 }
242 tk = TokenKind.DOUBLELITERAL;
243 radix = 16;
244 }
245 }
247 /** Read fractional part of floating point number.
248 */
249 private void scanFraction(int pos) {
250 skipIllegalUnderscores();
251 if ('0' <= reader.ch && reader.ch <= '9') {
252 scanDigits(pos, 10);
253 }
254 int sp1 = reader.sp;
255 if (reader.ch == 'e' || reader.ch == 'E') {
256 reader.putChar(true);
257 skipIllegalUnderscores();
258 if (reader.ch == '+' || reader.ch == '-') {
259 reader.putChar(true);
260 }
261 skipIllegalUnderscores();
262 if ('0' <= reader.ch && reader.ch <= '9') {
263 scanDigits(pos, 10);
264 return;
265 }
266 lexError(pos, "malformed.fp.lit");
267 reader.sp = sp1;
268 }
269 }
271 /** Read fractional part and 'd' or 'f' suffix of floating point number.
272 */
273 private void scanFractionAndSuffix(int pos) {
274 radix = 10;
275 scanFraction(pos);
276 if (reader.ch == 'f' || reader.ch == 'F') {
277 reader.putChar(true);
278 tk = TokenKind.FLOATLITERAL;
279 } else {
280 if (reader.ch == 'd' || reader.ch == 'D') {
281 reader.putChar(true);
282 }
283 tk = TokenKind.DOUBLELITERAL;
284 }
285 }
287 /** Read fractional part and 'd' or 'f' suffix of floating point number.
288 */
289 private void scanHexFractionAndSuffix(int pos, boolean seendigit) {
290 radix = 16;
291 Assert.check(reader.ch == '.');
292 reader.putChar(true);
293 skipIllegalUnderscores();
294 if (reader.digit(pos, 16) >= 0) {
295 seendigit = true;
296 scanDigits(pos, 16);
297 }
298 if (!seendigit)
299 lexError(pos, "invalid.hex.number");
300 else
301 scanHexExponentAndSuffix(pos);
302 }
304 private void skipIllegalUnderscores() {
305 if (reader.ch == '_') {
306 lexError(reader.bp, "illegal.underscore");
307 while (reader.ch == '_')
308 reader.scanChar();
309 }
310 }
312 /** Read a number.
313 * @param radix The radix of the number; one of 2, j8, 10, 16.
314 */
315 private void scanNumber(int pos, int radix) {
316 // for octal, allow base-10 digit in case it's a float literal
317 this.radix = radix;
318 int digitRadix = (radix == 8 ? 10 : radix);
319 boolean seendigit = false;
320 if (reader.digit(pos, digitRadix) >= 0) {
321 seendigit = true;
322 scanDigits(pos, digitRadix);
323 }
324 if (radix == 16 && reader.ch == '.') {
325 scanHexFractionAndSuffix(pos, seendigit);
326 } else if (seendigit && radix == 16 && (reader.ch == 'p' || reader.ch == 'P')) {
327 scanHexExponentAndSuffix(pos);
328 } else if (digitRadix == 10 && reader.ch == '.') {
329 reader.putChar(true);
330 scanFractionAndSuffix(pos);
331 } else if (digitRadix == 10 &&
332 (reader.ch == 'e' || reader.ch == 'E' ||
333 reader.ch == 'f' || reader.ch == 'F' ||
334 reader.ch == 'd' || reader.ch == 'D')) {
335 scanFractionAndSuffix(pos);
336 } else {
337 if (reader.ch == 'l' || reader.ch == 'L') {
338 reader.scanChar();
339 tk = TokenKind.LONGLITERAL;
340 } else {
341 tk = TokenKind.INTLITERAL;
342 }
343 }
344 }
346 /** Read an identifier.
347 */
348 private void scanIdent() {
349 boolean isJavaIdentifierPart;
350 char high;
351 do {
352 reader.putChar(true);
353 switch (reader.ch) {
354 case 'A': case 'B': case 'C': case 'D': case 'E':
355 case 'F': case 'G': case 'H': case 'I': case 'J':
356 case 'K': case 'L': case 'M': case 'N': case 'O':
357 case 'P': case 'Q': case 'R': case 'S': case 'T':
358 case 'U': case 'V': case 'W': case 'X': case 'Y':
359 case 'Z':
360 case 'a': case 'b': case 'c': case 'd': case 'e':
361 case 'f': case 'g': case 'h': case 'i': case 'j':
362 case 'k': case 'l': case 'm': case 'n': case 'o':
363 case 'p': case 'q': case 'r': case 's': case 't':
364 case 'u': case 'v': case 'w': case 'x': case 'y':
365 case 'z':
366 case '$': case '_':
367 case '0': case '1': case '2': case '3': case '4':
368 case '5': case '6': case '7': case '8': case '9':
369 case '\u0000': case '\u0001': case '\u0002': case '\u0003':
370 case '\u0004': case '\u0005': case '\u0006': case '\u0007':
371 case '\u0008': case '\u000E': case '\u000F': case '\u0010':
372 case '\u0011': case '\u0012': case '\u0013': case '\u0014':
373 case '\u0015': case '\u0016': case '\u0017':
374 case '\u0018': case '\u0019': case '\u001B':
375 case '\u007F':
376 break;
377 case '\u001A': // EOI is also a legal identifier part
378 if (reader.bp >= reader.buflen) {
379 name = reader.name();
380 tk = tokens.lookupKind(name);
381 return;
382 }
383 break;
384 default:
385 if (reader.ch < '\u0080') {
386 // all ASCII range chars already handled, above
387 isJavaIdentifierPart = false;
388 } else {
389 high = reader.scanSurrogates();
390 if (high != 0) {
391 reader.putChar(high);
392 isJavaIdentifierPart = Character.isJavaIdentifierPart(
393 Character.toCodePoint(high, reader.ch));
394 } else {
395 isJavaIdentifierPart = Character.isJavaIdentifierPart(reader.ch);
396 }
397 }
398 if (!isJavaIdentifierPart) {
399 name = reader.name();
400 tk = tokens.lookupKind(name);
401 return;
402 }
403 }
404 } while (true);
405 }
407 /** Return true if reader.ch can be part of an operator.
408 */
409 private boolean isSpecial(char ch) {
410 switch (ch) {
411 case '!': case '%': case '&': case '*': case '?':
412 case '+': case '-': case ':': case '<': case '=':
413 case '>': case '^': case '|': case '~':
414 case '@':
415 return true;
416 default:
417 return false;
418 }
419 }
421 /** Read longest possible sequence of special characters and convert
422 * to token.
423 */
424 private void scanOperator() {
425 while (true) {
426 reader.putChar(false);
427 Name newname = reader.name();
428 TokenKind tk1 = tokens.lookupKind(newname);
429 if (tk1 == TokenKind.IDENTIFIER) {
430 reader.sp--;
431 break;
432 }
433 tk = tk1;
434 reader.scanChar();
435 if (!isSpecial(reader.ch)) break;
436 }
437 }
439 /** Read token.
440 */
441 public Token readToken() {
443 reader.sp = 0;
444 name = null;
445 radix = 0;
447 int pos = 0;
448 int endPos = 0;
449 List<Comment> comments = null;
451 try {
452 loop: while (true) {
453 pos = reader.bp;
454 switch (reader.ch) {
455 case ' ': // (Spec 3.6)
456 case '\t': // (Spec 3.6)
457 case FF: // (Spec 3.6)
458 do {
459 reader.scanChar();
460 } while (reader.ch == ' ' || reader.ch == '\t' || reader.ch == FF);
461 processWhiteSpace(pos, reader.bp);
462 break;
463 case LF: // (Spec 3.4)
464 reader.scanChar();
465 processLineTerminator(pos, reader.bp);
466 break;
467 case CR: // (Spec 3.4)
468 reader.scanChar();
469 if (reader.ch == LF) {
470 reader.scanChar();
471 }
472 processLineTerminator(pos, reader.bp);
473 break;
474 case 'A': case 'B': case 'C': case 'D': case 'E':
475 case 'F': case 'G': case 'H': case 'I': case 'J':
476 case 'K': case 'L': case 'M': case 'N': case 'O':
477 case 'P': case 'Q': case 'R': case 'S': case 'T':
478 case 'U': case 'V': case 'W': case 'X': case 'Y':
479 case 'Z':
480 case 'a': case 'b': case 'c': case 'd': case 'e':
481 case 'f': case 'g': case 'h': case 'i': case 'j':
482 case 'k': case 'l': case 'm': case 'n': case 'o':
483 case 'p': case 'q': case 'r': case 's': case 't':
484 case 'u': case 'v': case 'w': case 'x': case 'y':
485 case 'z':
486 case '$': case '_':
487 scanIdent();
488 break loop;
489 case '0':
490 reader.scanChar();
491 if (reader.ch == 'x' || reader.ch == 'X') {
492 reader.scanChar();
493 skipIllegalUnderscores();
494 if (reader.ch == '.') {
495 scanHexFractionAndSuffix(pos, false);
496 } else if (reader.digit(pos, 16) < 0) {
497 lexError(pos, "invalid.hex.number");
498 } else {
499 scanNumber(pos, 16);
500 }
501 } else if (reader.ch == 'b' || reader.ch == 'B') {
502 if (!allowBinaryLiterals) {
503 lexError(pos, "unsupported.binary.lit", source.name);
504 allowBinaryLiterals = true;
505 }
506 reader.scanChar();
507 skipIllegalUnderscores();
508 if (reader.digit(pos, 2) < 0) {
509 lexError(pos, "invalid.binary.number");
510 } else {
511 scanNumber(pos, 2);
512 }
513 } else {
514 reader.putChar('0');
515 if (reader.ch == '_') {
516 int savePos = reader.bp;
517 do {
518 reader.scanChar();
519 } while (reader.ch == '_');
520 if (reader.digit(pos, 10) < 0) {
521 lexError(savePos, "illegal.underscore");
522 }
523 }
524 scanNumber(pos, 8);
525 }
526 break loop;
527 case '1': case '2': case '3': case '4':
528 case '5': case '6': case '7': case '8': case '9':
529 scanNumber(pos, 10);
530 break loop;
531 case '.':
532 reader.scanChar();
533 if ('0' <= reader.ch && reader.ch <= '9') {
534 reader.putChar('.');
535 scanFractionAndSuffix(pos);
536 } else if (reader.ch == '.') {
537 int savePos = reader.bp;
538 reader.putChar('.'); reader.putChar('.', true);
539 if (reader.ch == '.') {
540 reader.scanChar();
541 reader.putChar('.');
542 tk = TokenKind.ELLIPSIS;
543 } else {
544 lexError(savePos, "illegal.dot");
545 }
546 } else {
547 tk = TokenKind.DOT;
548 }
549 break loop;
550 case ',':
551 reader.scanChar(); tk = TokenKind.COMMA; break loop;
552 case ';':
553 reader.scanChar(); tk = TokenKind.SEMI; break loop;
554 case '(':
555 reader.scanChar(); tk = TokenKind.LPAREN; break loop;
556 case ')':
557 reader.scanChar(); tk = TokenKind.RPAREN; break loop;
558 case '[':
559 reader.scanChar(); tk = TokenKind.LBRACKET; break loop;
560 case ']':
561 reader.scanChar(); tk = TokenKind.RBRACKET; break loop;
562 case '{':
563 reader.scanChar(); tk = TokenKind.LBRACE; break loop;
564 case '}':
565 reader.scanChar(); tk = TokenKind.RBRACE; break loop;
566 case '/':
567 reader.scanChar();
568 if (reader.ch == '/') {
569 do {
570 reader.scanCommentChar();
571 } while (reader.ch != CR && reader.ch != LF && reader.bp < reader.buflen);
572 if (reader.bp < reader.buflen) {
573 comments = addComment(comments, processComment(pos, reader.bp, CommentStyle.LINE));
574 }
575 break;
576 } else if (reader.ch == '*') {
577 boolean isEmpty = false;
578 reader.scanChar();
579 CommentStyle style;
580 if (reader.ch == '*') {
581 style = CommentStyle.JAVADOC;
582 reader.scanCommentChar();
583 if (reader.ch == '/') {
584 isEmpty = true;
585 }
586 } else {
587 style = CommentStyle.BLOCK;
588 }
589 while (!isEmpty && reader.bp < reader.buflen) {
590 if (reader.ch == '*') {
591 reader.scanChar();
592 if (reader.ch == '/') break;
593 } else {
594 reader.scanCommentChar();
595 }
596 }
597 if (reader.ch == '/') {
598 reader.scanChar();
599 comments = addComment(comments, processComment(pos, reader.bp, style));
600 break;
601 } else {
602 lexError(pos, "unclosed.comment");
603 break loop;
604 }
605 } else if (reader.ch == '=') {
606 tk = TokenKind.SLASHEQ;
607 reader.scanChar();
608 } else {
609 tk = TokenKind.SLASH;
610 }
611 break loop;
612 case '\'':
613 reader.scanChar();
614 if (reader.ch == '\'') {
615 lexError(pos, "empty.char.lit");
616 } else {
617 if (reader.ch == CR || reader.ch == LF)
618 lexError(pos, "illegal.line.end.in.char.lit");
619 scanLitChar(pos);
620 char ch2 = reader.ch;
621 if (reader.ch == '\'') {
622 reader.scanChar();
623 tk = TokenKind.CHARLITERAL;
624 } else {
625 lexError(pos, "unclosed.char.lit");
626 }
627 }
628 break loop;
629 case '\"':
630 reader.scanChar();
631 while (reader.ch != '\"' && reader.ch != CR && reader.ch != LF && reader.bp < reader.buflen)
632 scanLitChar(pos);
633 if (reader.ch == '\"') {
634 tk = TokenKind.STRINGLITERAL;
635 reader.scanChar();
636 } else {
637 lexError(pos, "unclosed.str.lit");
638 }
639 break loop;
640 default:
641 if (isSpecial(reader.ch)) {
642 scanOperator();
643 } else {
644 boolean isJavaIdentifierStart;
645 if (reader.ch < '\u0080') {
646 // all ASCII range chars already handled, above
647 isJavaIdentifierStart = false;
648 } else {
649 char high = reader.scanSurrogates();
650 if (high != 0) {
651 reader.putChar(high);
653 isJavaIdentifierStart = Character.isJavaIdentifierStart(
654 Character.toCodePoint(high, reader.ch));
655 } else {
656 isJavaIdentifierStart = Character.isJavaIdentifierStart(reader.ch);
657 }
658 }
659 if (isJavaIdentifierStart) {
660 scanIdent();
661 } else if (reader.bp == reader.buflen || reader.ch == EOI && reader.bp + 1 == reader.buflen) { // JLS 3.5
662 tk = TokenKind.EOF;
663 pos = reader.buflen;
664 } else {
665 lexError(pos, "illegal.char", String.valueOf((int)reader.ch));
666 reader.scanChar();
667 }
668 }
669 break loop;
670 }
671 }
672 endPos = reader.bp;
673 switch (tk.tag) {
674 case DEFAULT: return new Token(tk, pos, endPos, comments);
675 case NAMED: return new NamedToken(tk, pos, endPos, name, comments);
676 case STRING: return new StringToken(tk, pos, endPos, reader.chars(), comments);
677 case NUMERIC: return new NumericToken(tk, pos, endPos, reader.chars(), radix, comments);
678 default: throw new AssertionError();
679 }
680 }
681 finally {
682 if (scannerDebug) {
683 System.out.println("nextToken(" + pos
684 + "," + endPos + ")=|" +
685 new String(reader.getRawCharacters(pos, endPos))
686 + "|");
687 }
688 }
689 }
690 //where
691 List<Comment> addComment(List<Comment> comments, Comment comment) {
692 return comments == null ?
693 List.of(comment) :
694 comments.prepend(comment);
695 }
697 /** Return the position where a lexical error occurred;
698 */
699 public int errPos() {
700 return errPos;
701 }
703 /** Set the position where a lexical error occurred;
704 */
705 public void errPos(int pos) {
706 errPos = pos;
707 }
709 /**
710 * Called when a complete comment has been scanned. pos and endPos
711 * will mark the comment boundary.
712 */
713 protected Tokens.Comment processComment(int pos, int endPos, CommentStyle style) {
714 if (scannerDebug)
715 System.out.println("processComment(" + pos
716 + "," + endPos + "," + style + ")=|"
717 + new String(reader.getRawCharacters(pos, endPos))
718 + "|");
719 char[] buf = reader.getRawCharacters(pos, endPos);
720 return new BasicComment<UnicodeReader>(new UnicodeReader(fac, buf, buf.length), style);
721 }
723 /**
724 * Called when a complete whitespace run has been scanned. pos and endPos
725 * will mark the whitespace boundary.
726 */
727 protected void processWhiteSpace(int pos, int endPos) {
728 if (scannerDebug)
729 System.out.println("processWhitespace(" + pos
730 + "," + endPos + ")=|" +
731 new String(reader.getRawCharacters(pos, endPos))
732 + "|");
733 }
735 /**
736 * Called when a line terminator has been processed.
737 */
738 protected void processLineTerminator(int pos, int endPos) {
739 if (scannerDebug)
740 System.out.println("processTerminator(" + pos
741 + "," + endPos + ")=|" +
742 new String(reader.getRawCharacters(pos, endPos))
743 + "|");
744 }
746 /** Build a map for translating between line numbers and
747 * positions in the input.
748 *
749 * @return a LineMap */
750 public Position.LineMap getLineMap() {
751 return Position.makeLineMap(reader.getRawCharacters(), reader.buflen, false);
752 }
755 /**
756 * Scan a documentation comment; determine if a deprecated tag is present.
757 * Called once the initial /, * have been skipped, positioned at the second *
758 * (which is treated as the beginning of the first line).
759 * Stops positioned at the closing '/'.
760 */
761 protected class BasicComment<U extends UnicodeReader> implements Comment {
763 CommentStyle cs;
764 U comment_reader;
766 protected boolean deprecatedFlag = false;
767 protected boolean scanned = false;
769 protected BasicComment(U comment_reader, CommentStyle cs) {
770 this.comment_reader = comment_reader;
771 this.cs = cs;
772 }
774 public String getText() {
775 return null;
776 }
778 public int getSourcePos(int pos) {
779 return -1;
780 }
782 public CommentStyle getStyle() {
783 return cs;
784 }
786 public boolean isDeprecated() {
787 if (!scanned && cs == CommentStyle.JAVADOC) {
788 scanDocComment();
789 }
790 return deprecatedFlag;
791 }
793 @SuppressWarnings("fallthrough")
794 protected void scanDocComment() {
795 try {
796 boolean deprecatedPrefix = false;
798 comment_reader.bp += 3; // '/**'
799 comment_reader.ch = comment_reader.buf[comment_reader.bp];
801 forEachLine:
802 while (comment_reader.bp < comment_reader.buflen) {
804 // Skip optional WhiteSpace at beginning of line
805 while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
806 comment_reader.scanCommentChar();
807 }
809 // Skip optional consecutive Stars
810 while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == '*') {
811 comment_reader.scanCommentChar();
812 if (comment_reader.ch == '/') {
813 return;
814 }
815 }
817 // Skip optional WhiteSpace after Stars
818 while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
819 comment_reader.scanCommentChar();
820 }
822 deprecatedPrefix = false;
823 // At beginning of line in the JavaDoc sense.
824 if (!deprecatedFlag) {
825 String deprecated = "@deprecated";
826 int i = 0;
827 while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == deprecated.charAt(i)) {
828 comment_reader.scanCommentChar();
829 i++;
830 if (i == deprecated.length()) {
831 deprecatedPrefix = true;
832 break;
833 }
834 }
835 }
837 if (deprecatedPrefix && comment_reader.bp < comment_reader.buflen) {
838 if (Character.isWhitespace(comment_reader.ch)) {
839 deprecatedFlag = true;
840 } else if (comment_reader.ch == '*') {
841 comment_reader.scanCommentChar();
842 if (comment_reader.ch == '/') {
843 deprecatedFlag = true;
844 return;
845 }
846 }
847 }
849 // Skip rest of line
850 while (comment_reader.bp < comment_reader.buflen) {
851 switch (comment_reader.ch) {
852 case '*':
853 comment_reader.scanCommentChar();
854 if (comment_reader.ch == '/') {
855 return;
856 }
857 break;
858 case CR: // (Spec 3.4)
859 comment_reader.scanCommentChar();
860 if (comment_reader.ch != LF) {
861 continue forEachLine;
862 }
863 /* fall through to LF case */
864 case LF: // (Spec 3.4)
865 comment_reader.scanCommentChar();
866 continue forEachLine;
867 default:
868 comment_reader.scanCommentChar();
869 }
870 } // rest of line
871 } // forEachLine
872 return;
873 } finally {
874 scanned = true;
875 }
876 }
877 }
878 }