7104201: Refactor DocCommentScanner

Add new Comment helper class to parse contents of comments in source code

Reviewed-by: jjg
This commit is contained in:
Maurizio Cimadamore 2011-11-04 12:36:40 +00:00
parent dae561e3ea
commit be5a83c8ce
7 changed files with 638 additions and 588 deletions

View file

@ -25,10 +25,11 @@
package com.sun.tools.javac.parser; package com.sun.tools.javac.parser;
import java.nio.CharBuffer;
import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.*;
import java.nio.CharBuffer;
import static com.sun.tools.javac.parser.Tokens.*; import static com.sun.tools.javac.parser.Tokens.*;
import static com.sun.tools.javac.util.LayoutCharacters.*; import static com.sun.tools.javac.util.LayoutCharacters.*;
@ -65,9 +66,6 @@ public class JavaTokenizer {
*/ */
private final Log log; private final Log log;
/** The name table. */
private final Names names;
/** The token factory. */ /** The token factory. */
private final Tokens tokens; private final Tokens tokens;
@ -87,18 +85,12 @@ public class JavaTokenizer {
*/ */
protected int errPos = Position.NOPOS; protected int errPos = Position.NOPOS;
/** Has a @deprecated been encountered in last doc comment? /** The Unicode reader (low-level stream reader).
* this needs to be reset by client.
*/ */
protected boolean deprecatedFlag = false;
/** A character buffer for saved chars.
*/
protected char[] sbuf = new char[128];
protected int sp;
protected UnicodeReader reader; protected UnicodeReader reader;
protected ScannerFactory fac;
private static final boolean hexFloatsWork = hexFloatsWork(); private static final boolean hexFloatsWork = hexFloatsWork();
private static boolean hexFloatsWork() { private static boolean hexFloatsWork() {
try { try {
@ -129,14 +121,14 @@ public class JavaTokenizer {
} }
protected JavaTokenizer(ScannerFactory fac, UnicodeReader reader) { protected JavaTokenizer(ScannerFactory fac, UnicodeReader reader) {
log = fac.log; this.fac = fac;
names = fac.names; this.log = fac.log;
tokens = fac.tokens; this.tokens = fac.tokens;
source = fac.source; this.source = fac.source;
this.reader = reader; this.reader = reader;
allowBinaryLiterals = source.allowBinaryLiterals(); this.allowBinaryLiterals = source.allowBinaryLiterals();
allowHexFloats = source.allowHexFloats(); this.allowHexFloats = source.allowHexFloats();
allowUnderscoresInLiterals = source.allowUnderscoresInLiterals(); this.allowUnderscoresInLiterals = source.allowUnderscoresInLiterals();
} }
/** Report an error at the given position using the provided arguments. /** Report an error at the given position using the provided arguments.
@ -147,38 +139,13 @@ public class JavaTokenizer {
errPos = pos; errPos = pos;
} }
/** Read next character in comment, skipping over double '\' characters.
*/
protected void scanCommentChar() {
reader.scanChar();
if (reader.ch == '\\') {
if (reader.peekChar() == '\\' && !reader.isUnicode()) {
reader.skipChar();
} else {
reader.convertUnicode();
}
}
}
/** Append a character to sbuf.
*/
private void putChar(char ch) {
if (sp == sbuf.length) {
char[] newsbuf = new char[sbuf.length * 2];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
sbuf[sp++] = ch;
}
/** Read next character in character or string literal and copy into sbuf. /** Read next character in character or string literal and copy into sbuf.
*/ */
private void scanLitChar(int pos) { private void scanLitChar(int pos) {
if (reader.ch == '\\') { if (reader.ch == '\\') {
if (reader.peekChar() == '\\' && !reader.isUnicode()) { if (reader.peekChar() == '\\' && !reader.isUnicode()) {
reader.skipChar(); reader.skipChar();
putChar('\\'); reader.putChar('\\', true);
reader.scanChar();
} else { } else {
reader.scanChar(); reader.scanChar();
switch (reader.ch) { switch (reader.ch) {
@ -195,30 +162,30 @@ public class JavaTokenizer {
reader.scanChar(); reader.scanChar();
} }
} }
putChar((char)oct); reader.putChar((char)oct);
break; break;
case 'b': case 'b':
putChar('\b'); reader.scanChar(); break; reader.putChar('\b', true); break;
case 't': case 't':
putChar('\t'); reader.scanChar(); break; reader.putChar('\t', true); break;
case 'n': case 'n':
putChar('\n'); reader.scanChar(); break; reader.putChar('\n', true); break;
case 'f': case 'f':
putChar('\f'); reader.scanChar(); break; reader.putChar('\f', true); break;
case 'r': case 'r':
putChar('\r'); reader.scanChar(); break; reader.putChar('\r', true); break;
case '\'': case '\'':
putChar('\''); reader.scanChar(); break; reader.putChar('\'', true); break;
case '\"': case '\"':
putChar('\"'); reader.scanChar(); break; reader.putChar('\"', true); break;
case '\\': case '\\':
putChar('\\'); reader.scanChar(); break; reader.putChar('\\', true); break;
default: default:
lexError(reader.bp, "illegal.esc.char"); lexError(reader.bp, "illegal.esc.char");
} }
} }
} else if (reader.bp != reader.buflen) { } else if (reader.bp != reader.buflen) {
putChar(reader.ch); reader.scanChar(); reader.putChar(true);
} }
} }
@ -227,7 +194,7 @@ public class JavaTokenizer {
int savePos; int savePos;
do { do {
if (reader.ch != '_') { if (reader.ch != '_') {
putChar(reader.ch); reader.putChar(false);
} else { } else {
if (!allowUnderscoresInLiterals) { if (!allowUnderscoresInLiterals) {
lexError(pos, "unsupported.underscore.lit", source.name); lexError(pos, "unsupported.underscore.lit", source.name);
@ -246,12 +213,10 @@ public class JavaTokenizer {
*/ */
private void scanHexExponentAndSuffix(int pos) { private void scanHexExponentAndSuffix(int pos) {
if (reader.ch == 'p' || reader.ch == 'P') { if (reader.ch == 'p' || reader.ch == 'P') {
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
skipIllegalUnderscores(); skipIllegalUnderscores();
if (reader.ch == '+' || reader.ch == '-') { if (reader.ch == '+' || reader.ch == '-') {
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
} }
skipIllegalUnderscores(); skipIllegalUnderscores();
if ('0' <= reader.ch && reader.ch <= '9') { if ('0' <= reader.ch && reader.ch <= '9') {
@ -268,14 +233,12 @@ public class JavaTokenizer {
lexError(pos, "malformed.fp.lit"); lexError(pos, "malformed.fp.lit");
} }
if (reader.ch == 'f' || reader.ch == 'F') { if (reader.ch == 'f' || reader.ch == 'F') {
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
tk = TokenKind.FLOATLITERAL; tk = TokenKind.FLOATLITERAL;
radix = 16; radix = 16;
} else { } else {
if (reader.ch == 'd' || reader.ch == 'D') { if (reader.ch == 'd' || reader.ch == 'D') {
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
} }
tk = TokenKind.DOUBLELITERAL; tk = TokenKind.DOUBLELITERAL;
radix = 16; radix = 16;
@ -289,14 +252,12 @@ public class JavaTokenizer {
if ('0' <= reader.ch && reader.ch <= '9') { if ('0' <= reader.ch && reader.ch <= '9') {
scanDigits(pos, 10); scanDigits(pos, 10);
} }
int sp1 = sp; int sp1 = reader.sp;
if (reader.ch == 'e' || reader.ch == 'E') { if (reader.ch == 'e' || reader.ch == 'E') {
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
skipIllegalUnderscores(); skipIllegalUnderscores();
if (reader.ch == '+' || reader.ch == '-') { if (reader.ch == '+' || reader.ch == '-') {
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
} }
skipIllegalUnderscores(); skipIllegalUnderscores();
if ('0' <= reader.ch && reader.ch <= '9') { if ('0' <= reader.ch && reader.ch <= '9') {
@ -304,7 +265,7 @@ public class JavaTokenizer {
return; return;
} }
lexError(pos, "malformed.fp.lit"); lexError(pos, "malformed.fp.lit");
sp = sp1; reader.sp = sp1;
} }
} }
@ -314,13 +275,11 @@ public class JavaTokenizer {
radix = 10; radix = 10;
scanFraction(pos); scanFraction(pos);
if (reader.ch == 'f' || reader.ch == 'F') { if (reader.ch == 'f' || reader.ch == 'F') {
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
tk = TokenKind.FLOATLITERAL; tk = TokenKind.FLOATLITERAL;
} else { } else {
if (reader.ch == 'd' || reader.ch == 'D') { if (reader.ch == 'd' || reader.ch == 'D') {
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
} }
tk = TokenKind.DOUBLELITERAL; tk = TokenKind.DOUBLELITERAL;
} }
@ -331,8 +290,7 @@ public class JavaTokenizer {
private void scanHexFractionAndSuffix(int pos, boolean seendigit) { private void scanHexFractionAndSuffix(int pos, boolean seendigit) {
radix = 16; radix = 16;
Assert.check(reader.ch == '.'); Assert.check(reader.ch == '.');
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
skipIllegalUnderscores(); skipIllegalUnderscores();
if (reader.digit(pos, 16) >= 0) { if (reader.digit(pos, 16) >= 0) {
seendigit = true; seendigit = true;
@ -369,8 +327,7 @@ public class JavaTokenizer {
} else if (seendigit && radix == 16 && (reader.ch == 'p' || reader.ch == 'P')) { } else if (seendigit && radix == 16 && (reader.ch == 'p' || reader.ch == 'P')) {
scanHexExponentAndSuffix(pos); scanHexExponentAndSuffix(pos);
} else if (digitRadix == 10 && reader.ch == '.') { } else if (digitRadix == 10 && reader.ch == '.') {
putChar(reader.ch); reader.putChar(true);
reader.scanChar();
scanFractionAndSuffix(pos); scanFractionAndSuffix(pos);
} else if (digitRadix == 10 && } else if (digitRadix == 10 &&
(reader.ch == 'e' || reader.ch == 'E' || (reader.ch == 'e' || reader.ch == 'E' ||
@ -393,10 +350,7 @@ public class JavaTokenizer {
boolean isJavaIdentifierPart; boolean isJavaIdentifierPart;
char high; char high;
do { do {
if (sp == sbuf.length) putChar(reader.ch); else sbuf[sp++] = reader.ch; reader.putChar(true);
// optimization, was: putChar(reader.ch);
reader.scanChar();
switch (reader.ch) { switch (reader.ch) {
case 'A': case 'B': case 'C': case 'D': case 'E': case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F': case 'G': case 'H': case 'I': case 'J': case 'F': case 'G': case 'H': case 'I': case 'J':
@ -423,7 +377,7 @@ public class JavaTokenizer {
break; break;
case '\u001A': // EOI is also a legal identifier part case '\u001A': // EOI is also a legal identifier part
if (reader.bp >= reader.buflen) { if (reader.bp >= reader.buflen) {
name = names.fromChars(sbuf, 0, sp); name = reader.name();
tk = tokens.lookupKind(name); tk = tokens.lookupKind(name);
return; return;
} }
@ -435,11 +389,7 @@ public class JavaTokenizer {
} else { } else {
high = reader.scanSurrogates(); high = reader.scanSurrogates();
if (high != 0) { if (high != 0) {
if (sp == sbuf.length) { reader.putChar(high);
putChar(high);
} else {
sbuf[sp++] = high;
}
isJavaIdentifierPart = Character.isJavaIdentifierPart( isJavaIdentifierPart = Character.isJavaIdentifierPart(
Character.toCodePoint(high, reader.ch)); Character.toCodePoint(high, reader.ch));
} else { } else {
@ -447,7 +397,7 @@ public class JavaTokenizer {
} }
} }
if (!isJavaIdentifierPart) { if (!isJavaIdentifierPart) {
name = names.fromChars(sbuf, 0, sp); name = reader.name();
tk = tokens.lookupKind(name); tk = tokens.lookupKind(name);
return; return;
} }
@ -474,11 +424,11 @@ public class JavaTokenizer {
*/ */
private void scanOperator() { private void scanOperator() {
while (true) { while (true) {
putChar(reader.ch); reader.putChar(false);
Name newname = names.fromChars(sbuf, 0, sp); Name newname = reader.name();
TokenKind tk1 = tokens.lookupKind(newname); TokenKind tk1 = tokens.lookupKind(newname);
if (tk1 == TokenKind.IDENTIFIER) { if (tk1 == TokenKind.IDENTIFIER) {
sp--; reader.sp--;
break; break;
} }
tk = tk1; tk = tk1;
@ -487,111 +437,17 @@ public class JavaTokenizer {
} }
} }
/**
* Scan a documentation comment; determine if a deprecated tag is present.
* Called once the initial /, * have been skipped, positioned at the second *
* (which is treated as the beginning of the first line).
* Stops positioned at the closing '/'.
*/
@SuppressWarnings("fallthrough")
private void scanDocComment() {
boolean deprecatedPrefix = false;
forEachLine:
while (reader.bp < reader.buflen) {
// Skip optional WhiteSpace at beginning of line
while (reader.bp < reader.buflen && (reader.ch == ' ' || reader.ch == '\t' || reader.ch == FF)) {
scanCommentChar();
}
// Skip optional consecutive Stars
while (reader.bp < reader.buflen && reader.ch == '*') {
scanCommentChar();
if (reader.ch == '/') {
return;
}
}
// Skip optional WhiteSpace after Stars
while (reader.bp < reader.buflen && (reader.ch == ' ' || reader.ch == '\t' || reader.ch == FF)) {
scanCommentChar();
}
deprecatedPrefix = false;
// At beginning of line in the JavaDoc sense.
if (reader.bp < reader.buflen && reader.ch == '@' && !deprecatedFlag) {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 'd') {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 'e') {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 'p') {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 'r') {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 'e') {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 'c') {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 'a') {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 't') {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 'e') {
scanCommentChar();
if (reader.bp < reader.buflen && reader.ch == 'd') {
deprecatedPrefix = true;
scanCommentChar();
}}}}}}}}}}}
if (deprecatedPrefix && reader.bp < reader.buflen) {
if (Character.isWhitespace(reader.ch)) {
deprecatedFlag = true;
} else if (reader.ch == '*') {
scanCommentChar();
if (reader.ch == '/') {
deprecatedFlag = true;
return;
}
}
}
// Skip rest of line
while (reader.bp < reader.buflen) {
switch (reader.ch) {
case '*':
scanCommentChar();
if (reader.ch == '/') {
return;
}
break;
case CR: // (Spec 3.4)
scanCommentChar();
if (reader.ch != LF) {
continue forEachLine;
}
/* fall through to LF case */
case LF: // (Spec 3.4)
scanCommentChar();
continue forEachLine;
default:
scanCommentChar();
}
} // rest of line
} // forEachLine
return;
}
/** Read token. /** Read token.
*/ */
public Token readToken() { public Token readToken() {
sp = 0; reader.sp = 0;
name = null; name = null;
deprecatedFlag = false;
radix = 0; radix = 0;
int pos = 0; int pos = 0;
int endPos = 0; int endPos = 0;
List<Comment> comments = null;
try { try {
loop: while (true) { loop: while (true) {
@ -656,7 +512,7 @@ public class JavaTokenizer {
scanNumber(pos, 2); scanNumber(pos, 2);
} }
} else { } else {
putChar('0'); reader.putChar('0');
if (reader.ch == '_') { if (reader.ch == '_') {
int savePos = reader.bp; int savePos = reader.bp;
do { do {
@ -676,14 +532,13 @@ public class JavaTokenizer {
case '.': case '.':
reader.scanChar(); reader.scanChar();
if ('0' <= reader.ch && reader.ch <= '9') { if ('0' <= reader.ch && reader.ch <= '9') {
putChar('.'); reader.putChar('.');
scanFractionAndSuffix(pos); scanFractionAndSuffix(pos);
} else if (reader.ch == '.') { } else if (reader.ch == '.') {
putChar('.'); putChar('.'); reader.putChar('.'); reader.putChar('.', true);
reader.scanChar();
if (reader.ch == '.') { if (reader.ch == '.') {
reader.scanChar(); reader.scanChar();
putChar('.'); reader.putChar('.');
tk = TokenKind.ELLIPSIS; tk = TokenKind.ELLIPSIS;
} else { } else {
lexError(pos, "malformed.fp.lit"); lexError(pos, "malformed.fp.lit");
@ -712,32 +567,36 @@ public class JavaTokenizer {
reader.scanChar(); reader.scanChar();
if (reader.ch == '/') { if (reader.ch == '/') {
do { do {
scanCommentChar(); reader.scanCommentChar();
} while (reader.ch != CR && reader.ch != LF && reader.bp < reader.buflen); } while (reader.ch != CR && reader.ch != LF && reader.bp < reader.buflen);
if (reader.bp < reader.buflen) { if (reader.bp < reader.buflen) {
processComment(pos, reader.bp, CommentStyle.LINE); comments = addDocReader(comments, processComment(pos, reader.bp, CommentStyle.LINE));
} }
break; break;
} else if (reader.ch == '*') { } else if (reader.ch == '*') {
boolean isEmpty = false;
reader.scanChar(); reader.scanChar();
CommentStyle style; CommentStyle style;
if (reader.ch == '*') { if (reader.ch == '*') {
style = CommentStyle.JAVADOC; style = CommentStyle.JAVADOC;
scanDocComment(); reader.scanCommentChar();
if (reader.ch == '/') {
isEmpty = true;
}
} else { } else {
style = CommentStyle.BLOCK; style = CommentStyle.BLOCK;
while (reader.bp < reader.buflen) { }
if (reader.ch == '*') { while (!isEmpty && reader.bp < reader.buflen) {
reader.scanChar(); if (reader.ch == '*') {
if (reader.ch == '/') break; reader.scanChar();
} else { if (reader.ch == '/') break;
scanCommentChar(); } else {
} reader.scanCommentChar();
} }
} }
if (reader.ch == '/') { if (reader.ch == '/') {
reader.scanChar(); reader.scanChar();
processComment(pos, reader.bp, style); comments = addDocReader(comments, processComment(pos, reader.bp, style));
break; break;
} else { } else {
lexError(pos, "unclosed.comment"); lexError(pos, "unclosed.comment");
@ -789,11 +648,7 @@ public class JavaTokenizer {
} else { } else {
char high = reader.scanSurrogates(); char high = reader.scanSurrogates();
if (high != 0) { if (high != 0) {
if (sp == sbuf.length) { reader.putChar(high);
putChar(high);
} else {
sbuf[sp++] = high;
}
isJavaIdentifierStart = Character.isJavaIdentifierStart( isJavaIdentifierStart = Character.isJavaIdentifierStart(
Character.toCodePoint(high, reader.ch)); Character.toCodePoint(high, reader.ch));
@ -816,10 +671,10 @@ public class JavaTokenizer {
} }
endPos = reader.bp; endPos = reader.bp;
switch (tk.tag) { switch (tk.tag) {
case DEFAULT: return new Token(tk, pos, endPos, deprecatedFlag); case DEFAULT: return new Token(tk, pos, endPos, comments);
case NAMED: return new NamedToken(tk, pos, endPos, name, deprecatedFlag); case NAMED: return new NamedToken(tk, pos, endPos, name, comments);
case STRING: return new StringToken(tk, pos, endPos, new String(sbuf, 0, sp), deprecatedFlag); case STRING: return new StringToken(tk, pos, endPos, reader.chars(), comments);
case NUMERIC: return new NumericToken(tk, pos, endPos, new String(sbuf, 0, sp), radix, deprecatedFlag); case NUMERIC: return new NumericToken(tk, pos, endPos, reader.chars(), radix, comments);
default: throw new AssertionError(); default: throw new AssertionError();
} }
} }
@ -832,6 +687,12 @@ public class JavaTokenizer {
} }
} }
} }
//where
List<Comment> addDocReader(List<Comment> docReaders, Comment docReader) {
return docReaders == null ?
List.of(docReader) :
docReaders.prepend(docReader);
}
/** Return the position where a lexical error occurred; /** Return the position where a lexical error occurred;
*/ */
@ -845,22 +706,18 @@ public class JavaTokenizer {
errPos = pos; errPos = pos;
} }
public enum CommentStyle {
LINE,
BLOCK,
JAVADOC,
}
/** /**
* Called when a complete comment has been scanned. pos and endPos * Called when a complete comment has been scanned. pos and endPos
* will mark the comment boundary. * will mark the comment boundary.
*/ */
protected void processComment(int pos, int endPos, CommentStyle style) { protected Tokens.Comment processComment(int pos, int endPos, CommentStyle style) {
if (scannerDebug) if (scannerDebug)
System.out.println("processComment(" + pos System.out.println("processComment(" + pos
+ "," + endPos + "," + style + ")=|" + "," + endPos + "," + style + ")=|"
+ new String(reader.getRawCharacters(pos, endPos)) + new String(reader.getRawCharacters(pos, endPos))
+ "|"); + "|");
char[] buf = reader.getRawCharacters(pos, endPos);
return new BasicComment<UnicodeReader>(new UnicodeReader(fac, buf, buf.length), style);
} }
/** /**
@ -893,4 +750,125 @@ public class JavaTokenizer {
public Position.LineMap getLineMap() { public Position.LineMap getLineMap() {
return Position.makeLineMap(reader.getRawCharacters(), reader.buflen, false); return Position.makeLineMap(reader.getRawCharacters(), reader.buflen, false);
} }
/**
* Scan a documentation comment; determine if a deprecated tag is present.
* Called once the initial /, * have been skipped, positioned at the second *
* (which is treated as the beginning of the first line).
* Stops positioned at the closing '/'.
*/
protected class BasicComment<U extends UnicodeReader> implements Comment {
CommentStyle cs;
U comment_reader;
protected boolean deprecatedFlag = false;
protected boolean scanned = false;
protected BasicComment(U comment_reader, CommentStyle cs) {
this.comment_reader = comment_reader;
this.cs = cs;
}
public String getText() {
return null;
}
public CommentStyle getStyle() {
return cs;
}
public boolean isDeprecated() {
if (!scanned && cs == CommentStyle.JAVADOC) {
scanDocComment();
}
return deprecatedFlag;
}
@SuppressWarnings("fallthrough")
protected void scanDocComment() {
try {
boolean deprecatedPrefix = false;
comment_reader.bp += 3; // '/**'
comment_reader.ch = comment_reader.buf[comment_reader.bp];
forEachLine:
while (comment_reader.bp < comment_reader.buflen) {
// Skip optional WhiteSpace at beginning of line
while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
comment_reader.scanCommentChar();
}
// Skip optional consecutive Stars
while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == '*') {
comment_reader.scanCommentChar();
if (comment_reader.ch == '/') {
return;
}
}
// Skip optional WhiteSpace after Stars
while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
comment_reader.scanCommentChar();
}
deprecatedPrefix = false;
// At beginning of line in the JavaDoc sense.
if (!deprecatedFlag) {
String deprecated = "@deprecated";
int i = 0;
while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == deprecated.charAt(i)) {
comment_reader.scanCommentChar();
i++;
if (i == deprecated.length()) {
deprecatedPrefix = true;
break;
}
}
}
if (deprecatedPrefix && comment_reader.bp < comment_reader.buflen) {
if (Character.isWhitespace(comment_reader.ch)) {
deprecatedFlag = true;
} else if (comment_reader.ch == '*') {
comment_reader.scanCommentChar();
if (comment_reader.ch == '/') {
deprecatedFlag = true;
return;
}
}
}
// Skip rest of line
while (comment_reader.bp < comment_reader.buflen) {
switch (comment_reader.ch) {
case '*':
comment_reader.scanCommentChar();
if (comment_reader.ch == '/') {
return;
}
break;
case CR: // (Spec 3.4)
comment_reader.scanCommentChar();
if (comment_reader.ch != LF) {
continue forEachLine;
}
/* fall through to LF case */
case LF: // (Spec 3.4)
comment_reader.scanCommentChar();
continue forEachLine;
default:
comment_reader.scanCommentChar();
}
} // rest of line
} // forEachLine
return;
} finally {
scanned = true;
}
}
}
} }

View file

@ -29,6 +29,7 @@ import java.util.*;
import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.*;
import com.sun.tools.javac.parser.Tokens.*; import com.sun.tools.javac.parser.Tokens.*;
import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.*;
@ -1584,7 +1585,7 @@ public class JavacParser implements Parser {
break; break;
case MONKEYS_AT: case MONKEYS_AT:
case FINAL: { case FINAL: {
String dc = token.docComment; String dc = token.comment(CommentStyle.JAVADOC);
JCModifiers mods = modifiersOpt(); JCModifiers mods = modifiersOpt();
if (token.kind == INTERFACE || if (token.kind == INTERFACE ||
token.kind == CLASS || token.kind == CLASS ||
@ -1601,21 +1602,21 @@ public class JavacParser implements Parser {
break; break;
} }
case ABSTRACT: case STRICTFP: { case ABSTRACT: case STRICTFP: {
String dc = token.docComment; String dc = token.comment(CommentStyle.JAVADOC);
JCModifiers mods = modifiersOpt(); JCModifiers mods = modifiersOpt();
stats.append(classOrInterfaceOrEnumDeclaration(mods, dc)); stats.append(classOrInterfaceOrEnumDeclaration(mods, dc));
break; break;
} }
case INTERFACE: case INTERFACE:
case CLASS: case CLASS:
String dc = token.docComment; String dc = token.comment(CommentStyle.JAVADOC);
stats.append(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc)); stats.append(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
break; break;
case ENUM: case ENUM:
case ASSERT: case ASSERT:
if (allowEnums && token.kind == ENUM) { if (allowEnums && token.kind == ENUM) {
error(token.pos, "local.enum"); error(token.pos, "local.enum");
dc = token.docComment; dc = token.comment(CommentStyle.JAVADOC);
stats.append(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc)); stats.append(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
break; break;
} else if (allowAsserts && token.kind == ASSERT) { } else if (allowAsserts && token.kind == ASSERT) {
@ -1991,7 +1992,7 @@ public class JavacParser implements Parser {
annotations.appendList(partial.annotations); annotations.appendList(partial.annotations);
pos = partial.pos; pos = partial.pos;
} }
if (token.deprecatedFlag) { if (token.deprecatedFlag()) {
flags |= Flags.DEPRECATED; flags |= Flags.DEPRECATED;
} }
int lastPos = Position.NOPOS; int lastPos = Position.NOPOS;
@ -2271,9 +2272,9 @@ public class JavacParser implements Parser {
seenImport = true; seenImport = true;
defs.append(importDeclaration()); defs.append(importDeclaration());
} else { } else {
String docComment = token.docComment; String docComment = token.comment(CommentStyle.JAVADOC);
if (firstTypeDecl && !seenImport && !seenPackage) { if (firstTypeDecl && !seenImport && !seenPackage) {
docComment = firstToken.docComment; docComment = firstToken.comment(CommentStyle.JAVADOC);
consumedToplevelDoc = true; consumedToplevelDoc = true;
} }
JCTree def = typeDeclaration(mods, docComment); JCTree def = typeDeclaration(mods, docComment);
@ -2288,7 +2289,7 @@ public class JavacParser implements Parser {
} }
JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(packageAnnotations, pid, defs.toList()); JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(packageAnnotations, pid, defs.toList());
if (!consumedToplevelDoc) if (!consumedToplevelDoc)
attach(toplevel, firstToken.docComment); attach(toplevel, firstToken.comment(CommentStyle.JAVADOC));
if (defs.elems.isEmpty()) if (defs.elems.isEmpty())
storeEnd(toplevel, S.prevToken().endPos); storeEnd(toplevel, S.prevToken().endPos);
if (keepDocComments) if (keepDocComments)
@ -2498,9 +2499,9 @@ public class JavacParser implements Parser {
/** EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ] /** EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ]
*/ */
JCTree enumeratorDeclaration(Name enumName) { JCTree enumeratorDeclaration(Name enumName) {
String dc = token.docComment; String dc = token.comment(CommentStyle.JAVADOC);
int flags = Flags.PUBLIC|Flags.STATIC|Flags.FINAL|Flags.ENUM; int flags = Flags.PUBLIC|Flags.STATIC|Flags.FINAL|Flags.ENUM;
if (token.deprecatedFlag) { if (token.deprecatedFlag()) {
flags |= Flags.DEPRECATED; flags |= Flags.DEPRECATED;
} }
int pos = token.pos; int pos = token.pos;
@ -2587,7 +2588,7 @@ public class JavacParser implements Parser {
nextToken(); nextToken();
return List.<JCTree>nil(); return List.<JCTree>nil();
} else { } else {
String dc = token.docComment; String dc = token.comment(CommentStyle.JAVADOC);
int pos = token.pos; int pos = token.pos;
JCModifiers mods = modifiersOpt(); JCModifiers mods = modifiersOpt();
if (token.kind == CLASS || if (token.kind == CLASS ||

View file

@ -25,8 +25,8 @@
package com.sun.tools.javac.parser; package com.sun.tools.javac.parser;
import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.parser.Tokens.Comment;
import com.sun.tools.javac.parser.Tokens.Token; import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.*;
import java.nio.*; import java.nio.*;
@ -59,352 +59,295 @@ public class JavadocTokenizer extends JavaTokenizer {
super(fac, input, inputLength); super(fac, input, inputLength);
} }
/** The comment input buffer, index of next chacter to be read, @Override
* index of one past last character in buffer. protected Comment processComment(int pos, int endPos, CommentStyle style) {
*/ char[] buf = reader.getRawCharacters(pos, endPos);
private char[] buf; return new JavadocComment(new ColReader(fac, buf, buf.length), style);
private int bp;
private int buflen;
/** The current character.
*/
private char ch;
/** The column number position of the current character.
*/
private int col;
/** The buffer index of the last converted Unicode character
*/
private int unicodeConversionBp = 0;
/**
* Buffer for doc comment.
*/
private char[] docCommentBuffer = new char[1024];
/**
* Number of characters in doc comment buffer.
*/
private int docCommentCount;
/**
* Translated and stripped contents of doc comment
*/
private String docComment = null;
/** Unconditionally expand the comment buffer.
*/
private void expandCommentBuffer() {
char[] newBuffer = new char[docCommentBuffer.length * 2];
System.arraycopy(docCommentBuffer, 0, newBuffer,
0, docCommentBuffer.length);
docCommentBuffer = newBuffer;
} }
/** Convert an ASCII digit from its base (8, 10, or 16) /**
* to its value. * This is a specialized version of UnicodeReader that keeps track of the
* column position within a given character stream (used for Javadoc processing).
*/ */
private int digit(int base) { static class ColReader extends UnicodeReader {
char c = ch;
int result = Character.digit(c, base); int col;
if (result >= 0 && c > 0x7f) {
ch = "0123456789abcdef".charAt(result); ColReader(ScannerFactory fac, char[] input, int inputLength) {
super(fac, input, inputLength);
}
@Override
protected void convertUnicode() {
if (ch == '\\' && unicodeConversionBp != bp) {
bp++; ch = buf[bp]; col++;
if (ch == 'u') {
do {
bp++; ch = buf[bp]; col++;
} while (ch == 'u');
int limit = bp + 3;
if (limit < buflen) {
int d = digit(bp, 16);
int code = d;
while (bp < limit && d >= 0) {
bp++; ch = buf[bp]; col++;
d = digit(bp, 16);
code = (code << 4) + d;
}
if (d >= 0) {
ch = (char)code;
unicodeConversionBp = bp;
return;
}
}
// "illegal.Unicode.esc", reported by base scanner
} else {
bp--;
ch = '\\';
col--;
}
}
}
@Override
protected void scanCommentChar() {
scanChar();
if (ch == '\\') {
if (peekChar() == '\\' && !isUnicode()) {
putChar(ch, false);
bp++; col++;
} else {
convertUnicode();
}
}
}
@Override
protected void scanChar() {
bp++;
ch = buf[bp];
switch (ch) {
case '\r': // return
col = 0;
break;
case '\n': // newline
if (bp == 0 || buf[bp-1] != '\r') {
col = 0;
}
break;
case '\t': // tab
col = (col / TabInc * TabInc) + TabInc;
break;
case '\\': // possible Unicode
col++;
convertUnicode();
break;
default:
col++;
break;
}
}
}
protected class JavadocComment extends JavaTokenizer.BasicComment<ColReader> {
/**
* Translated and stripped contents of doc comment
*/
private String docComment = null;
JavadocComment(ColReader comment_reader, CommentStyle cs) {
super(comment_reader, cs);
} }
return result;
}
/** Convert Unicode escape; bp points to initial '\' character public String getText() {
* (Spec 3.3). if (!scanned && cs == CommentStyle.JAVADOC) {
*/ scanDocComment();
private void convertUnicode() { }
if (ch == '\\' && unicodeConversionBp != bp) { return docComment;
bp++; ch = buf[bp]; col++; }
if (ch == 'u') {
do { @Override
bp++; ch = buf[bp]; col++; @SuppressWarnings("fallthrough")
} while (ch == 'u'); protected void scanDocComment() {
int limit = bp + 3; try {
if (limit < buflen) { boolean firstLine = true;
int d = digit(16);
int code = d; // Skip over first slash
while (bp < limit && d >= 0) { comment_reader.scanCommentChar();
bp++; ch = buf[bp]; col++; // Skip over first star
d = digit(16); comment_reader.scanCommentChar();
code = (code << 4) + d;
} // consume any number of stars
if (d >= 0) { while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == '*') {
ch = (char)code; comment_reader.scanCommentChar();
unicodeConversionBp = bp; }
return; // is the comment in the form /**/, /***/, /****/, etc. ?
} if (comment_reader.bp < comment_reader.buflen && comment_reader.ch == '/') {
docComment = "";
return;
}
// skip a newline on the first line of the comment.
if (comment_reader.bp < comment_reader.buflen) {
if (comment_reader.ch == LF) {
comment_reader.scanCommentChar();
firstLine = false;
} else if (comment_reader.ch == CR) {
comment_reader.scanCommentChar();
if (comment_reader.ch == LF) {
comment_reader.scanCommentChar();
firstLine = false;
}
}
}
outerLoop:
// The outerLoop processes the doc comment, looping once
// for each line. For each line, it first strips off
// whitespace, then it consumes any stars, then it
// puts the rest of the line into our buffer.
while (comment_reader.bp < comment_reader.buflen) {
// The wsLoop consumes whitespace from the beginning
// of each line.
wsLoop:
while (comment_reader.bp < comment_reader.buflen) {
switch(comment_reader.ch) {
case ' ':
comment_reader.scanCommentChar();
break;
case '\t':
comment_reader.col = ((comment_reader.col - 1) / TabInc * TabInc) + TabInc;
comment_reader.scanCommentChar();
break;
case FF:
comment_reader.col = 0;
comment_reader.scanCommentChar();
break;
// Treat newline at beginning of line (blank line, no star)
// as comment text. Old Javadoc compatibility requires this.
/*---------------------------------*
case CR: // (Spec 3.4)
doc_reader.scanCommentChar();
if (ch == LF) {
col = 0;
doc_reader.scanCommentChar();
}
break;
case LF: // (Spec 3.4)
doc_reader.scanCommentChar();
break;
*---------------------------------*/
default:
// we've seen something that isn't whitespace;
// jump out.
break wsLoop;
}
}
// Are there stars here? If so, consume them all
// and check for the end of comment.
if (comment_reader.ch == '*') {
// skip all of the stars
do {
comment_reader.scanCommentChar();
} while (comment_reader.ch == '*');
// check for the closing slash.
if (comment_reader.ch == '/') {
// We're done with the doc comment
// scanChar() and breakout.
break outerLoop;
}
} else if (! firstLine) {
//The current line does not begin with a '*' so we will indent it.
for (int i = 1; i < comment_reader.col; i++) {
comment_reader.putChar(' ', false);
}
}
// The textLoop processes the rest of the characters
// on the line, adding them to our buffer.
textLoop:
while (comment_reader.bp < comment_reader.buflen) {
switch (comment_reader.ch) {
case '*':
// Is this just a star? Or is this the
// end of a comment?
comment_reader.scanCommentChar();
if (comment_reader.ch == '/') {
// This is the end of the comment,
// set ch and return our buffer.
break outerLoop;
}
// This is just an ordinary star. Add it to
// the buffer.
comment_reader.putChar('*', false);
break;
case ' ':
case '\t':
comment_reader.putChar(comment_reader.ch, false);
comment_reader.scanCommentChar();
break;
case FF:
comment_reader.scanCommentChar();
break textLoop; // treat as end of line
case CR: // (Spec 3.4)
comment_reader.scanCommentChar();
if (comment_reader.ch != LF) {
// Canonicalize CR-only line terminator to LF
comment_reader.putChar((char)LF, false);
break textLoop;
}
/* fall through to LF case */
case LF: // (Spec 3.4)
// We've seen a newline. Add it to our
// buffer and break out of this loop,
// starting fresh on a new line.
comment_reader.putChar(comment_reader.ch, false);
comment_reader.scanCommentChar();
break textLoop;
default:
// Add the character to our buffer.
comment_reader.putChar(comment_reader.ch, false);
comment_reader.scanCommentChar();
}
} // end textLoop
firstLine = false;
} // end outerLoop
if (comment_reader.sp > 0) {
int i = comment_reader.sp - 1;
trailLoop:
while (i > -1) {
switch (comment_reader.sbuf[i]) {
case '*':
i--;
break;
default:
break trailLoop;
}
}
comment_reader.sp = i + 1;
// Store the text of the doc comment
docComment = comment_reader.chars();
} else {
docComment = "";
}
} finally {
scanned = true;
if (docComment != null &&
docComment.matches("(?sm).*^\\s*@deprecated( |$).*")) {
deprecatedFlag = true;
} }
// "illegal.Unicode.esc", reported by base scanner
} else {
bp--;
ch = '\\';
col--;
} }
} }
} }
/** Read next character.
*/
private void scanChar() {
bp++;
ch = buf[bp];
switch (ch) {
case '\r': // return
col = 0;
break;
case '\n': // newline
if (bp == 0 || buf[bp-1] != '\r') {
col = 0;
}
break;
case '\t': // tab
col = (col / TabInc * TabInc) + TabInc;
break;
case '\\': // possible Unicode
col++;
convertUnicode();
break;
default:
col++;
break;
}
}
@Override @Override
public Token readToken() {
docComment = null;
Token tk = super.readToken();
tk.docComment = docComment;
return tk;
}
/**
* Read next character in doc comment, skipping over double '\' characters.
* If a double '\' is skipped, put in the buffer and update buffer count.
*/
private void scanDocCommentChar() {
scanChar();
if (ch == '\\') {
if (buf[bp+1] == '\\' && unicodeConversionBp != bp) {
if (docCommentCount == docCommentBuffer.length)
expandCommentBuffer();
docCommentBuffer[docCommentCount++] = ch;
bp++; col++;
} else {
convertUnicode();
}
}
}
/**
* Process a doc comment and make the string content available.
* Strips leading whitespace and stars.
*/
@SuppressWarnings("fallthrough")
protected void processComment(int pos, int endPos, CommentStyle style) {
if (style != CommentStyle.JAVADOC) {
return;
}
buf = reader.getRawCharacters(pos, endPos);
buflen = buf.length;
bp = 0;
col = 0;
docCommentCount = 0;
boolean firstLine = true;
// Skip over first slash
scanDocCommentChar();
// Skip over first star
scanDocCommentChar();
// consume any number of stars
while (bp < buflen && ch == '*') {
scanDocCommentChar();
}
// is the comment in the form /**/, /***/, /****/, etc. ?
if (bp < buflen && ch == '/') {
docComment = "";
return;
}
// skip a newline on the first line of the comment.
if (bp < buflen) {
if (ch == LF) {
scanDocCommentChar();
firstLine = false;
} else if (ch == CR) {
scanDocCommentChar();
if (ch == LF) {
scanDocCommentChar();
firstLine = false;
}
}
}
outerLoop:
// The outerLoop processes the doc comment, looping once
// for each line. For each line, it first strips off
// whitespace, then it consumes any stars, then it
// puts the rest of the line into our buffer.
while (bp < buflen) {
// The wsLoop consumes whitespace from the beginning
// of each line.
wsLoop:
while (bp < buflen) {
switch(ch) {
case ' ':
scanDocCommentChar();
break;
case '\t':
col = ((col - 1) / TabInc * TabInc) + TabInc;
scanDocCommentChar();
break;
case FF:
col = 0;
scanDocCommentChar();
break;
// Treat newline at beginning of line (blank line, no star)
// as comment text. Old Javadoc compatibility requires this.
/*---------------------------------*
case CR: // (Spec 3.4)
scanDocCommentChar();
if (ch == LF) {
col = 0;
scanDocCommentChar();
}
break;
case LF: // (Spec 3.4)
scanDocCommentChar();
break;
*---------------------------------*/
default:
// we've seen something that isn't whitespace;
// jump out.
break wsLoop;
}
}
// Are there stars here? If so, consume them all
// and check for the end of comment.
if (ch == '*') {
// skip all of the stars
do {
scanDocCommentChar();
} while (ch == '*');
// check for the closing slash.
if (ch == '/') {
// We're done with the doc comment
// scanChar() and breakout.
break outerLoop;
}
} else if (! firstLine) {
//The current line does not begin with a '*' so we will indent it.
for (int i = 1; i < col; i++) {
if (docCommentCount == docCommentBuffer.length)
expandCommentBuffer();
docCommentBuffer[docCommentCount++] = ' ';
}
}
// The textLoop processes the rest of the characters
// on the line, adding them to our buffer.
textLoop:
while (bp < buflen) {
switch (ch) {
case '*':
// Is this just a star? Or is this the
// end of a comment?
scanDocCommentChar();
if (ch == '/') {
// This is the end of the comment,
// set ch and return our buffer.
break outerLoop;
}
// This is just an ordinary star. Add it to
// the buffer.
if (docCommentCount == docCommentBuffer.length)
expandCommentBuffer();
docCommentBuffer[docCommentCount++] = '*';
break;
case ' ':
case '\t':
if (docCommentCount == docCommentBuffer.length)
expandCommentBuffer();
docCommentBuffer[docCommentCount++] = ch;
scanDocCommentChar();
break;
case FF:
scanDocCommentChar();
break textLoop; // treat as end of line
case CR: // (Spec 3.4)
scanDocCommentChar();
if (ch != LF) {
// Canonicalize CR-only line terminator to LF
if (docCommentCount == docCommentBuffer.length)
expandCommentBuffer();
docCommentBuffer[docCommentCount++] = (char)LF;
break textLoop;
}
/* fall through to LF case */
case LF: // (Spec 3.4)
// We've seen a newline. Add it to our
// buffer and break out of this loop,
// starting fresh on a new line.
if (docCommentCount == docCommentBuffer.length)
expandCommentBuffer();
docCommentBuffer[docCommentCount++] = ch;
scanDocCommentChar();
break textLoop;
default:
// Add the character to our buffer.
if (docCommentCount == docCommentBuffer.length)
expandCommentBuffer();
docCommentBuffer[docCommentCount++] = ch;
scanDocCommentChar();
}
} // end textLoop
firstLine = false;
} // end outerLoop
if (docCommentCount > 0) {
int i = docCommentCount - 1;
trailLoop:
while (i > -1) {
switch (docCommentBuffer[i]) {
case '*':
i--;
break;
default:
break trailLoop;
}
}
docCommentCount = i + 1;
// Store the text of the doc comment
docComment = new String(docCommentBuffer, 0 , docCommentCount);
} else {
docComment = "";
}
}
/** Build a map for translating between line numbers and
* positions in the input.
*
* @return a LineMap */
public Position.LineMap getLineMap() { public Position.LineMap getLineMap() {
char[] buf = reader.getRawCharacters(); char[] buf = reader.getRawCharacters();
return Position.makeLineMap(buf, buf.length, true); return Position.makeLineMap(buf, buf.length, true);

View file

@ -30,8 +30,10 @@ import java.util.Locale;
import com.sun.tools.javac.api.Formattable; import com.sun.tools.javac.api.Formattable;
import com.sun.tools.javac.api.Messages; import com.sun.tools.javac.api.Messages;
import com.sun.tools.javac.parser.Tokens.Token.Tag; import com.sun.tools.javac.parser.Tokens.Token.Tag;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Names;
/** A class that defines codes/utilities for Java source tokens /** A class that defines codes/utilities for Java source tokens
@ -281,6 +283,19 @@ public class Tokens {
} }
} }
public interface Comment {
enum CommentStyle {
LINE,
BLOCK,
JAVADOC,
}
String getText();
CommentStyle getStyle();
boolean isDeprecated();
}
/** /**
* This is the class representing a javac token. Each token has several fields * This is the class representing a javac token. Each token has several fields
* that are set by the javac lexer (i.e. start/end position, string value, etc). * that are set by the javac lexer (i.e. start/end position, string value, etc).
@ -304,18 +319,14 @@ public class Tokens {
/** The end position of this token */ /** The end position of this token */
public final int endPos; public final int endPos;
/** Is this token preceeded by a deprecated comment? */ /** Comment reader associated with this token */
public final boolean deprecatedFlag; public final List<Comment> comments;
/** Is this token preceeded by a deprecated comment? */ Token(TokenKind kind, int pos, int endPos, List<Comment> comments) {
public String docComment;
Token(TokenKind kind, int pos, int endPos,
boolean deprecatedFlag) {
this.kind = kind; this.kind = kind;
this.pos = pos; this.pos = pos;
this.endPos = endPos; this.endPos = endPos;
this.deprecatedFlag = deprecatedFlag; this.comments = comments;
checkKind(); checkKind();
} }
@ -331,8 +342,8 @@ public class Tokens {
throw new AssertionError("Cant split - bad subtokens"); throw new AssertionError("Cant split - bad subtokens");
} }
return new Token[] { return new Token[] {
new Token(t1, pos, pos + t1.name.length(), deprecatedFlag), new Token(t1, pos, pos + t1.name.length(), comments),
new Token(t2, pos + t1.name.length(), endPos, false) new Token(t2, pos + t1.name.length(), endPos, null)
}; };
} }
@ -353,14 +364,52 @@ public class Tokens {
public int radix() { public int radix() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
/**
* Preserve classic semantics - if multiple javadocs are found on the token
* the last one is returned
*/
public String comment(Comment.CommentStyle style) {
List<Comment> readers = getReaders(Comment.CommentStyle.JAVADOC);
return readers.isEmpty() ?
null :
readers.head.getText();
}
/**
* Preserve classic semantics - deprecated should be set if at least one
* javadoc comment attached to this token contains the '@deprecated' string
*/
public boolean deprecatedFlag() {
for (Comment r : getReaders(Comment.CommentStyle.JAVADOC)) {
if (r.isDeprecated()) {
return true;
}
}
return false;
}
private List<Comment> getReaders(Comment.CommentStyle style) {
if (comments == null) {
return List.nil();
} else {
ListBuffer<Comment> buf = ListBuffer.lb();
for (Comment r : comments) {
if (r.getStyle() == style) {
buf.add(r);
}
}
return buf.toList();
}
}
} }
final static class NamedToken extends Token { final static class NamedToken extends Token {
/** The name of this token */ /** The name of this token */
public final Name name; public final Name name;
public NamedToken(TokenKind kind, int pos, int endPos, Name name, boolean deprecatedFlag) { public NamedToken(TokenKind kind, int pos, int endPos, Name name, List<Comment> comments) {
super(kind, pos, endPos, deprecatedFlag); super(kind, pos, endPos, comments);
this.name = name; this.name = name;
} }
@ -380,8 +429,8 @@ public class Tokens {
/** The string value of this token */ /** The string value of this token */
public final String stringVal; public final String stringVal;
public StringToken(TokenKind kind, int pos, int endPos, String stringVal, boolean deprecatedFlag) { public StringToken(TokenKind kind, int pos, int endPos, String stringVal, List<Comment> comments) {
super(kind, pos, endPos, deprecatedFlag); super(kind, pos, endPos, comments);
this.stringVal = stringVal; this.stringVal = stringVal;
} }
@ -401,8 +450,8 @@ public class Tokens {
/** The 'radix' value of this token */ /** The 'radix' value of this token */
public final int radix; public final int radix;
public NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, boolean deprecatedFlag) { public NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, List<Comment> comments) {
super(kind, pos, endPos, stringVal, deprecatedFlag); super(kind, pos, endPos, stringVal, comments);
this.radix = radix; this.radix = radix;
} }
@ -419,5 +468,5 @@ public class Tokens {
} }
public static final Token DUMMY = public static final Token DUMMY =
new Token(TokenKind.ERROR, 0, 0, false); new Token(TokenKind.ERROR, 0, 0, null);
} }

View file

@ -26,8 +26,12 @@
package com.sun.tools.javac.parser; package com.sun.tools.javac.parser;
import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.file.JavacFileManager;
import java.nio.CharBuffer;
import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import java.nio.CharBuffer;
import static com.sun.tools.javac.util.LayoutCharacters.*; import static com.sun.tools.javac.util.LayoutCharacters.*;
/** The char reader used by the javac lexer/tokenizer. Returns the sequence of /** The char reader used by the javac lexer/tokenizer. Returns the sequence of
@ -58,6 +62,12 @@ public class UnicodeReader {
protected int unicodeConversionBp = -1; protected int unicodeConversionBp = -1;
protected Log log; protected Log log;
protected Names names;
/** A character buffer for saved chars.
*/
protected char[] sbuf = new char[128];
protected int sp;
/** /**
* Create a scanner from the input array. This method might * Create a scanner from the input array. This method might
@ -76,6 +86,7 @@ public class UnicodeReader {
protected UnicodeReader(ScannerFactory sf, char[] input, int inputLength) { protected UnicodeReader(ScannerFactory sf, char[] input, int inputLength) {
log = sf.log; log = sf.log;
names = sf.names;
if (inputLength == input.length) { if (inputLength == input.length) {
if (input.length > 0 && Character.isWhitespace(input[input.length - 1])) { if (input.length > 0 && Character.isWhitespace(input[input.length - 1])) {
inputLength--; inputLength--;
@ -103,6 +114,48 @@ public class UnicodeReader {
} }
} }
/** Read next character in comment, skipping over double '\' characters.
*/
protected void scanCommentChar() {
scanChar();
if (ch == '\\') {
if (peekChar() == '\\' && !isUnicode()) {
skipChar();
} else {
convertUnicode();
}
}
}
/** Append a character to sbuf.
*/
protected void putChar(char ch, boolean scan) {
if (sp == sbuf.length) {
char[] newsbuf = new char[sbuf.length * 2];
System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
sbuf = newsbuf;
}
sbuf[sp++] = ch;
if (scan)
scanChar();
}
protected void putChar(char ch) {
putChar(ch, false);
}
protected void putChar(boolean scan) {
putChar(ch, scan);
}
Name name() {
return names.fromChars(sbuf, 0, sp);
}
String chars() {
return new String(sbuf, 0, sp);
}
/** Convert unicode escape; bp points to initial '\' character /** Convert unicode escape; bp points to initial '\' character
* (Spec 3.3). * (Spec 3.3).
*/ */

View file

@ -0,0 +1,20 @@
/**
* @test /nodynamiccopyright/
* @bug 7104201
* @summary Refactor DocCommentScanner
* @compile/fail/ref=DeprecatedDocComment4.out -XDrawDiagnostics -Werror -Xlint:dep-ann DeprecatedDocComment4.java
*/
class DeprecatedDocComment4 {
/** @deprecated **/
/* block */
void test1() {};
/** @deprecated **/
/** double javadoc */
void test2() {};
/** @deprecated **/
//line comment
void test3() {};
}

View file

@ -0,0 +1,6 @@
DeprecatedDocComment4.java:11:10: compiler.warn.missing.deprecated.annotation
DeprecatedDocComment4.java:15:10: compiler.warn.missing.deprecated.annotation
DeprecatedDocComment4.java:19:10: compiler.warn.missing.deprecated.annotation
- compiler.err.warnings.and.werror
1 error
3 warnings