Fri, 04 Nov 2011 12:36:40 +0000
7104201: Refactor DocCommentScanner
Summary: Add new Comment helper class to parse contents of comments in source code
Reviewed-by: jjg
1 /*
2 * Copyright (c) 1999, 2011, 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 input the input, might be modified
112 * @param inputLength the size of the input.
113 * Must be positive and less than or equal to input.length.
114 */
115 protected JavaTokenizer(ScannerFactory fac, CharBuffer buf) {
116 this(fac, new UnicodeReader(fac, buf));
117 }
119 protected JavaTokenizer(ScannerFactory fac, char[] buf, int inputLength) {
120 this(fac, new UnicodeReader(fac, buf, inputLength));
121 }
123 protected JavaTokenizer(ScannerFactory fac, UnicodeReader reader) {
124 this.fac = fac;
125 this.log = fac.log;
126 this.tokens = fac.tokens;
127 this.source = fac.source;
128 this.reader = reader;
129 this.allowBinaryLiterals = source.allowBinaryLiterals();
130 this.allowHexFloats = source.allowHexFloats();
131 this.allowUnderscoresInLiterals = source.allowUnderscoresInLiterals();
132 }
134 /** Report an error at the given position using the provided arguments.
135 */
136 protected void lexError(int pos, String key, Object... args) {
137 log.error(pos, key, args);
138 tk = TokenKind.ERROR;
139 errPos = pos;
140 }
142 /** Read next character in character or string literal and copy into sbuf.
143 */
144 private void scanLitChar(int pos) {
145 if (reader.ch == '\\') {
146 if (reader.peekChar() == '\\' && !reader.isUnicode()) {
147 reader.skipChar();
148 reader.putChar('\\', true);
149 } else {
150 reader.scanChar();
151 switch (reader.ch) {
152 case '0': case '1': case '2': case '3':
153 case '4': case '5': case '6': case '7':
154 char leadch = reader.ch;
155 int oct = reader.digit(pos, 8);
156 reader.scanChar();
157 if ('0' <= reader.ch && reader.ch <= '7') {
158 oct = oct * 8 + reader.digit(pos, 8);
159 reader.scanChar();
160 if (leadch <= '3' && '0' <= reader.ch && reader.ch <= '7') {
161 oct = oct * 8 + reader.digit(pos, 8);
162 reader.scanChar();
163 }
164 }
165 reader.putChar((char)oct);
166 break;
167 case 'b':
168 reader.putChar('\b', true); break;
169 case 't':
170 reader.putChar('\t', true); break;
171 case 'n':
172 reader.putChar('\n', true); break;
173 case 'f':
174 reader.putChar('\f', true); break;
175 case 'r':
176 reader.putChar('\r', true); break;
177 case '\'':
178 reader.putChar('\'', true); break;
179 case '\"':
180 reader.putChar('\"', true); break;
181 case '\\':
182 reader.putChar('\\', true); break;
183 default:
184 lexError(reader.bp, "illegal.esc.char");
185 }
186 }
187 } else if (reader.bp != reader.buflen) {
188 reader.putChar(true);
189 }
190 }
192 private void scanDigits(int pos, int digitRadix) {
193 char saveCh;
194 int savePos;
195 do {
196 if (reader.ch != '_') {
197 reader.putChar(false);
198 } else {
199 if (!allowUnderscoresInLiterals) {
200 lexError(pos, "unsupported.underscore.lit", source.name);
201 allowUnderscoresInLiterals = true;
202 }
203 }
204 saveCh = reader.ch;
205 savePos = reader.bp;
206 reader.scanChar();
207 } while (reader.digit(pos, digitRadix) >= 0 || reader.ch == '_');
208 if (saveCh == '_')
209 lexError(savePos, "illegal.underscore");
210 }
212 /** Read fractional part of hexadecimal floating point number.
213 */
214 private void scanHexExponentAndSuffix(int pos) {
215 if (reader.ch == 'p' || reader.ch == 'P') {
216 reader.putChar(true);
217 skipIllegalUnderscores();
218 if (reader.ch == '+' || reader.ch == '-') {
219 reader.putChar(true);
220 }
221 skipIllegalUnderscores();
222 if ('0' <= reader.ch && reader.ch <= '9') {
223 scanDigits(pos, 10);
224 if (!allowHexFloats) {
225 lexError(pos, "unsupported.fp.lit", source.name);
226 allowHexFloats = true;
227 }
228 else if (!hexFloatsWork)
229 lexError(pos, "unsupported.cross.fp.lit");
230 } else
231 lexError(pos, "malformed.fp.lit");
232 } else {
233 lexError(pos, "malformed.fp.lit");
234 }
235 if (reader.ch == 'f' || reader.ch == 'F') {
236 reader.putChar(true);
237 tk = TokenKind.FLOATLITERAL;
238 radix = 16;
239 } else {
240 if (reader.ch == 'd' || reader.ch == 'D') {
241 reader.putChar(true);
242 }
243 tk = TokenKind.DOUBLELITERAL;
244 radix = 16;
245 }
246 }
248 /** Read fractional part of floating point number.
249 */
250 private void scanFraction(int pos) {
251 skipIllegalUnderscores();
252 if ('0' <= reader.ch && reader.ch <= '9') {
253 scanDigits(pos, 10);
254 }
255 int sp1 = reader.sp;
256 if (reader.ch == 'e' || reader.ch == 'E') {
257 reader.putChar(true);
258 skipIllegalUnderscores();
259 if (reader.ch == '+' || reader.ch == '-') {
260 reader.putChar(true);
261 }
262 skipIllegalUnderscores();
263 if ('0' <= reader.ch && reader.ch <= '9') {
264 scanDigits(pos, 10);
265 return;
266 }
267 lexError(pos, "malformed.fp.lit");
268 reader.sp = sp1;
269 }
270 }
272 /** Read fractional part and 'd' or 'f' suffix of floating point number.
273 */
274 private void scanFractionAndSuffix(int pos) {
275 radix = 10;
276 scanFraction(pos);
277 if (reader.ch == 'f' || reader.ch == 'F') {
278 reader.putChar(true);
279 tk = TokenKind.FLOATLITERAL;
280 } else {
281 if (reader.ch == 'd' || reader.ch == 'D') {
282 reader.putChar(true);
283 }
284 tk = TokenKind.DOUBLELITERAL;
285 }
286 }
288 /** Read fractional part and 'd' or 'f' suffix of floating point number.
289 */
290 private void scanHexFractionAndSuffix(int pos, boolean seendigit) {
291 radix = 16;
292 Assert.check(reader.ch == '.');
293 reader.putChar(true);
294 skipIllegalUnderscores();
295 if (reader.digit(pos, 16) >= 0) {
296 seendigit = true;
297 scanDigits(pos, 16);
298 }
299 if (!seendigit)
300 lexError(pos, "invalid.hex.number");
301 else
302 scanHexExponentAndSuffix(pos);
303 }
305 private void skipIllegalUnderscores() {
306 if (reader.ch == '_') {
307 lexError(reader.bp, "illegal.underscore");
308 while (reader.ch == '_')
309 reader.scanChar();
310 }
311 }
313 /** Read a number.
314 * @param radix The radix of the number; one of 2, j8, 10, 16.
315 */
316 private void scanNumber(int pos, int radix) {
317 // for octal, allow base-10 digit in case it's a float literal
318 this.radix = radix;
319 int digitRadix = (radix == 8 ? 10 : radix);
320 boolean seendigit = false;
321 if (reader.digit(pos, digitRadix) >= 0) {
322 seendigit = true;
323 scanDigits(pos, digitRadix);
324 }
325 if (radix == 16 && reader.ch == '.') {
326 scanHexFractionAndSuffix(pos, seendigit);
327 } else if (seendigit && radix == 16 && (reader.ch == 'p' || reader.ch == 'P')) {
328 scanHexExponentAndSuffix(pos);
329 } else if (digitRadix == 10 && reader.ch == '.') {
330 reader.putChar(true);
331 scanFractionAndSuffix(pos);
332 } else if (digitRadix == 10 &&
333 (reader.ch == 'e' || reader.ch == 'E' ||
334 reader.ch == 'f' || reader.ch == 'F' ||
335 reader.ch == 'd' || reader.ch == 'D')) {
336 scanFractionAndSuffix(pos);
337 } else {
338 if (reader.ch == 'l' || reader.ch == 'L') {
339 reader.scanChar();
340 tk = TokenKind.LONGLITERAL;
341 } else {
342 tk = TokenKind.INTLITERAL;
343 }
344 }
345 }
347 /** Read an identifier.
348 */
349 private void scanIdent() {
350 boolean isJavaIdentifierPart;
351 char high;
352 do {
353 reader.putChar(true);
354 switch (reader.ch) {
355 case 'A': case 'B': case 'C': case 'D': case 'E':
356 case 'F': case 'G': case 'H': case 'I': case 'J':
357 case 'K': case 'L': case 'M': case 'N': case 'O':
358 case 'P': case 'Q': case 'R': case 'S': case 'T':
359 case 'U': case 'V': case 'W': case 'X': case 'Y':
360 case 'Z':
361 case 'a': case 'b': case 'c': case 'd': case 'e':
362 case 'f': case 'g': case 'h': case 'i': case 'j':
363 case 'k': case 'l': case 'm': case 'n': case 'o':
364 case 'p': case 'q': case 'r': case 's': case 't':
365 case 'u': case 'v': case 'w': case 'x': case 'y':
366 case 'z':
367 case '$': case '_':
368 case '0': case '1': case '2': case '3': case '4':
369 case '5': case '6': case '7': case '8': case '9':
370 case '\u0000': case '\u0001': case '\u0002': case '\u0003':
371 case '\u0004': case '\u0005': case '\u0006': case '\u0007':
372 case '\u0008': case '\u000E': case '\u000F': case '\u0010':
373 case '\u0011': case '\u0012': case '\u0013': case '\u0014':
374 case '\u0015': case '\u0016': case '\u0017':
375 case '\u0018': case '\u0019': case '\u001B':
376 case '\u007F':
377 break;
378 case '\u001A': // EOI is also a legal identifier part
379 if (reader.bp >= reader.buflen) {
380 name = reader.name();
381 tk = tokens.lookupKind(name);
382 return;
383 }
384 break;
385 default:
386 if (reader.ch < '\u0080') {
387 // all ASCII range chars already handled, above
388 isJavaIdentifierPart = false;
389 } else {
390 high = reader.scanSurrogates();
391 if (high != 0) {
392 reader.putChar(high);
393 isJavaIdentifierPart = Character.isJavaIdentifierPart(
394 Character.toCodePoint(high, reader.ch));
395 } else {
396 isJavaIdentifierPart = Character.isJavaIdentifierPart(reader.ch);
397 }
398 }
399 if (!isJavaIdentifierPart) {
400 name = reader.name();
401 tk = tokens.lookupKind(name);
402 return;
403 }
404 }
405 } while (true);
406 }
408 /** Return true if reader.ch can be part of an operator.
409 */
410 private boolean isSpecial(char ch) {
411 switch (ch) {
412 case '!': case '%': case '&': case '*': case '?':
413 case '+': case '-': case ':': case '<': case '=':
414 case '>': case '^': case '|': case '~':
415 case '@':
416 return true;
417 default:
418 return false;
419 }
420 }
422 /** Read longest possible sequence of special characters and convert
423 * to token.
424 */
425 private void scanOperator() {
426 while (true) {
427 reader.putChar(false);
428 Name newname = reader.name();
429 TokenKind tk1 = tokens.lookupKind(newname);
430 if (tk1 == TokenKind.IDENTIFIER) {
431 reader.sp--;
432 break;
433 }
434 tk = tk1;
435 reader.scanChar();
436 if (!isSpecial(reader.ch)) break;
437 }
438 }
440 /** Read token.
441 */
442 public Token readToken() {
444 reader.sp = 0;
445 name = null;
446 radix = 0;
448 int pos = 0;
449 int endPos = 0;
450 List<Comment> comments = null;
452 try {
453 loop: while (true) {
454 pos = reader.bp;
455 switch (reader.ch) {
456 case ' ': // (Spec 3.6)
457 case '\t': // (Spec 3.6)
458 case FF: // (Spec 3.6)
459 do {
460 reader.scanChar();
461 } while (reader.ch == ' ' || reader.ch == '\t' || reader.ch == FF);
462 processWhiteSpace(pos, reader.bp);
463 break;
464 case LF: // (Spec 3.4)
465 reader.scanChar();
466 processLineTerminator(pos, reader.bp);
467 break;
468 case CR: // (Spec 3.4)
469 reader.scanChar();
470 if (reader.ch == LF) {
471 reader.scanChar();
472 }
473 processLineTerminator(pos, reader.bp);
474 break;
475 case 'A': case 'B': case 'C': case 'D': case 'E':
476 case 'F': case 'G': case 'H': case 'I': case 'J':
477 case 'K': case 'L': case 'M': case 'N': case 'O':
478 case 'P': case 'Q': case 'R': case 'S': case 'T':
479 case 'U': case 'V': case 'W': case 'X': case 'Y':
480 case 'Z':
481 case 'a': case 'b': case 'c': case 'd': case 'e':
482 case 'f': case 'g': case 'h': case 'i': case 'j':
483 case 'k': case 'l': case 'm': case 'n': case 'o':
484 case 'p': case 'q': case 'r': case 's': case 't':
485 case 'u': case 'v': case 'w': case 'x': case 'y':
486 case 'z':
487 case '$': case '_':
488 scanIdent();
489 break loop;
490 case '0':
491 reader.scanChar();
492 if (reader.ch == 'x' || reader.ch == 'X') {
493 reader.scanChar();
494 skipIllegalUnderscores();
495 if (reader.ch == '.') {
496 scanHexFractionAndSuffix(pos, false);
497 } else if (reader.digit(pos, 16) < 0) {
498 lexError(pos, "invalid.hex.number");
499 } else {
500 scanNumber(pos, 16);
501 }
502 } else if (reader.ch == 'b' || reader.ch == 'B') {
503 if (!allowBinaryLiterals) {
504 lexError(pos, "unsupported.binary.lit", source.name);
505 allowBinaryLiterals = true;
506 }
507 reader.scanChar();
508 skipIllegalUnderscores();
509 if (reader.digit(pos, 2) < 0) {
510 lexError(pos, "invalid.binary.number");
511 } else {
512 scanNumber(pos, 2);
513 }
514 } else {
515 reader.putChar('0');
516 if (reader.ch == '_') {
517 int savePos = reader.bp;
518 do {
519 reader.scanChar();
520 } while (reader.ch == '_');
521 if (reader.digit(pos, 10) < 0) {
522 lexError(savePos, "illegal.underscore");
523 }
524 }
525 scanNumber(pos, 8);
526 }
527 break loop;
528 case '1': case '2': case '3': case '4':
529 case '5': case '6': case '7': case '8': case '9':
530 scanNumber(pos, 10);
531 break loop;
532 case '.':
533 reader.scanChar();
534 if ('0' <= reader.ch && reader.ch <= '9') {
535 reader.putChar('.');
536 scanFractionAndSuffix(pos);
537 } else if (reader.ch == '.') {
538 reader.putChar('.'); reader.putChar('.', true);
539 if (reader.ch == '.') {
540 reader.scanChar();
541 reader.putChar('.');
542 tk = TokenKind.ELLIPSIS;
543 } else {
544 lexError(pos, "malformed.fp.lit");
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 = addDocReader(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 = addDocReader(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> addDocReader(List<Comment> docReaders, Comment docReader) {
692 return docReaders == null ?
693 List.of(docReader) :
694 docReaders.prepend(docReader);
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 CommentStyle getStyle() {
779 return cs;
780 }
782 public boolean isDeprecated() {
783 if (!scanned && cs == CommentStyle.JAVADOC) {
784 scanDocComment();
785 }
786 return deprecatedFlag;
787 }
789 @SuppressWarnings("fallthrough")
790 protected void scanDocComment() {
791 try {
792 boolean deprecatedPrefix = false;
794 comment_reader.bp += 3; // '/**'
795 comment_reader.ch = comment_reader.buf[comment_reader.bp];
797 forEachLine:
798 while (comment_reader.bp < comment_reader.buflen) {
800 // Skip optional WhiteSpace at beginning of line
801 while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
802 comment_reader.scanCommentChar();
803 }
805 // Skip optional consecutive Stars
806 while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == '*') {
807 comment_reader.scanCommentChar();
808 if (comment_reader.ch == '/') {
809 return;
810 }
811 }
813 // Skip optional WhiteSpace after Stars
814 while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
815 comment_reader.scanCommentChar();
816 }
818 deprecatedPrefix = false;
819 // At beginning of line in the JavaDoc sense.
820 if (!deprecatedFlag) {
821 String deprecated = "@deprecated";
822 int i = 0;
823 while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == deprecated.charAt(i)) {
824 comment_reader.scanCommentChar();
825 i++;
826 if (i == deprecated.length()) {
827 deprecatedPrefix = true;
828 break;
829 }
830 }
831 }
833 if (deprecatedPrefix && comment_reader.bp < comment_reader.buflen) {
834 if (Character.isWhitespace(comment_reader.ch)) {
835 deprecatedFlag = true;
836 } else if (comment_reader.ch == '*') {
837 comment_reader.scanCommentChar();
838 if (comment_reader.ch == '/') {
839 deprecatedFlag = true;
840 return;
841 }
842 }
843 }
845 // Skip rest of line
846 while (comment_reader.bp < comment_reader.buflen) {
847 switch (comment_reader.ch) {
848 case '*':
849 comment_reader.scanCommentChar();
850 if (comment_reader.ch == '/') {
851 return;
852 }
853 break;
854 case CR: // (Spec 3.4)
855 comment_reader.scanCommentChar();
856 if (comment_reader.ch != LF) {
857 continue forEachLine;
858 }
859 /* fall through to LF case */
860 case LF: // (Spec 3.4)
861 comment_reader.scanCommentChar();
862 continue forEachLine;
863 default:
864 comment_reader.scanCommentChar();
865 }
866 } // rest of line
867 } // forEachLine
868 return;
869 } finally {
870 scanned = true;
871 }
872 }
873 }
874 }