7104201: Refactor DocCommentScanner
authormcimadamore
Fri, 04 Nov 2011 12:36:40 +0000
changeset 10948 063463f6535f
parent 10947 47788da3eb3f
child 10949 42f7cc0468dd
7104201: Refactor DocCommentScanner Summary: Add new Comment helper class to parse contents of comments in source code Reviewed-by: jjg
langtools/src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java
langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java
langtools/src/share/classes/com/sun/tools/javac/parser/JavadocTokenizer.java
langtools/src/share/classes/com/sun/tools/javac/parser/Tokens.java
langtools/src/share/classes/com/sun/tools/javac/parser/UnicodeReader.java
langtools/test/tools/javac/depDocComment/DeprecatedDocComment4.java
langtools/test/tools/javac/depDocComment/DeprecatedDocComment4.out
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java	Tue Nov 01 15:49:45 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java	Fri Nov 04 12:36:40 2011 +0000
@@ -25,10 +25,11 @@
 
 package com.sun.tools.javac.parser;
 
-import java.nio.CharBuffer;
 import com.sun.tools.javac.code.Source;
+import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
 import com.sun.tools.javac.util.*;
 
+import java.nio.CharBuffer;
 
 import static com.sun.tools.javac.parser.Tokens.*;
 import static com.sun.tools.javac.util.LayoutCharacters.*;
@@ -65,9 +66,6 @@
      */
     private final Log log;
 
-    /** The name table. */
-    private final Names names;
-
     /** The token factory. */
     private final Tokens tokens;
 
@@ -87,17 +85,11 @@
      */
     protected int errPos = Position.NOPOS;
 
-    /** Has a @deprecated been encountered in last doc comment?
-     *  this needs to be reset by client.
+    /** The Unicode reader (low-level stream reader).
      */
-    protected boolean deprecatedFlag = false;
+    protected UnicodeReader reader;
 
-    /** A character buffer for saved chars.
-     */
-    protected char[] sbuf = new char[128];
-    protected int sp;
-
-    protected UnicodeReader reader;
+    protected ScannerFactory fac;
 
     private static final boolean hexFloatsWork = hexFloatsWork();
     private static boolean hexFloatsWork() {
@@ -129,14 +121,14 @@
     }
 
     protected JavaTokenizer(ScannerFactory fac, UnicodeReader reader) {
-        log = fac.log;
-        names = fac.names;
-        tokens = fac.tokens;
-        source = fac.source;
+        this.fac = fac;
+        this.log = fac.log;
+        this.tokens = fac.tokens;
+        this.source = fac.source;
         this.reader = reader;
-        allowBinaryLiterals = source.allowBinaryLiterals();
-        allowHexFloats = source.allowHexFloats();
-        allowUnderscoresInLiterals = source.allowUnderscoresInLiterals();
+        this.allowBinaryLiterals = source.allowBinaryLiterals();
+        this.allowHexFloats = source.allowHexFloats();
+        this.allowUnderscoresInLiterals = source.allowUnderscoresInLiterals();
     }
 
     /** Report an error at the given position using the provided arguments.
@@ -147,38 +139,13 @@
         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.
      */
     private void scanLitChar(int pos) {
         if (reader.ch == '\\') {
             if (reader.peekChar() == '\\' && !reader.isUnicode()) {
                 reader.skipChar();
-                putChar('\\');
-                reader.scanChar();
+                reader.putChar('\\', true);
             } else {
                 reader.scanChar();
                 switch (reader.ch) {
@@ -195,30 +162,30 @@
                             reader.scanChar();
                         }
                     }
-                    putChar((char)oct);
+                    reader.putChar((char)oct);
                     break;
                 case 'b':
-                    putChar('\b'); reader.scanChar(); break;
+                    reader.putChar('\b', true); break;
                 case 't':
-                    putChar('\t'); reader.scanChar(); break;
+                    reader.putChar('\t', true); break;
                 case 'n':
-                    putChar('\n'); reader.scanChar(); break;
+                    reader.putChar('\n', true); break;
                 case 'f':
-                    putChar('\f'); reader.scanChar(); break;
+                    reader.putChar('\f', true); break;
                 case 'r':
-                    putChar('\r'); reader.scanChar(); break;
+                    reader.putChar('\r', true); break;
                 case '\'':
-                    putChar('\''); reader.scanChar(); break;
+                    reader.putChar('\'', true); break;
                 case '\"':
-                    putChar('\"'); reader.scanChar(); break;
+                    reader.putChar('\"', true); break;
                 case '\\':
-                    putChar('\\'); reader.scanChar(); break;
+                    reader.putChar('\\', true); break;
                 default:
                     lexError(reader.bp, "illegal.esc.char");
                 }
             }
         } else if (reader.bp != reader.buflen) {
-            putChar(reader.ch); reader.scanChar();
+            reader.putChar(true);
         }
     }
 
@@ -227,7 +194,7 @@
         int savePos;
         do {
             if (reader.ch != '_') {
-                putChar(reader.ch);
+                reader.putChar(false);
             } else {
                 if (!allowUnderscoresInLiterals) {
                     lexError(pos, "unsupported.underscore.lit", source.name);
@@ -246,12 +213,10 @@
      */
     private void scanHexExponentAndSuffix(int pos) {
         if (reader.ch == 'p' || reader.ch == 'P') {
-            putChar(reader.ch);
-            reader.scanChar();
+            reader.putChar(true);
             skipIllegalUnderscores();
             if (reader.ch == '+' || reader.ch == '-') {
-                putChar(reader.ch);
-                reader.scanChar();
+                reader.putChar(true);
             }
             skipIllegalUnderscores();
             if ('0' <= reader.ch && reader.ch <= '9') {
@@ -268,14 +233,12 @@
             lexError(pos, "malformed.fp.lit");
         }
         if (reader.ch == 'f' || reader.ch == 'F') {
-            putChar(reader.ch);
-            reader.scanChar();
+            reader.putChar(true);
             tk = TokenKind.FLOATLITERAL;
             radix = 16;
         } else {
             if (reader.ch == 'd' || reader.ch == 'D') {
-                putChar(reader.ch);
-                reader.scanChar();
+                reader.putChar(true);
             }
             tk = TokenKind.DOUBLELITERAL;
             radix = 16;
@@ -289,14 +252,12 @@
         if ('0' <= reader.ch && reader.ch <= '9') {
             scanDigits(pos, 10);
         }
-        int sp1 = sp;
+        int sp1 = reader.sp;
         if (reader.ch == 'e' || reader.ch == 'E') {
-            putChar(reader.ch);
-            reader.scanChar();
+            reader.putChar(true);
             skipIllegalUnderscores();
             if (reader.ch == '+' || reader.ch == '-') {
-                putChar(reader.ch);
-                reader.scanChar();
+                reader.putChar(true);
             }
             skipIllegalUnderscores();
             if ('0' <= reader.ch && reader.ch <= '9') {
@@ -304,7 +265,7 @@
                 return;
             }
             lexError(pos, "malformed.fp.lit");
-            sp = sp1;
+            reader.sp = sp1;
         }
     }
 
@@ -314,13 +275,11 @@
         radix = 10;
         scanFraction(pos);
         if (reader.ch == 'f' || reader.ch == 'F') {
-            putChar(reader.ch);
-            reader.scanChar();
+            reader.putChar(true);
             tk = TokenKind.FLOATLITERAL;
         } else {
             if (reader.ch == 'd' || reader.ch == 'D') {
-                putChar(reader.ch);
-                reader.scanChar();
+                reader.putChar(true);
             }
             tk = TokenKind.DOUBLELITERAL;
         }
@@ -331,8 +290,7 @@
     private void scanHexFractionAndSuffix(int pos, boolean seendigit) {
         radix = 16;
         Assert.check(reader.ch == '.');
-        putChar(reader.ch);
-        reader.scanChar();
+        reader.putChar(true);
         skipIllegalUnderscores();
         if (reader.digit(pos, 16) >= 0) {
             seendigit = true;
@@ -369,8 +327,7 @@
         } else if (seendigit && radix == 16 && (reader.ch == 'p' || reader.ch == 'P')) {
             scanHexExponentAndSuffix(pos);
         } else if (digitRadix == 10 && reader.ch == '.') {
-            putChar(reader.ch);
-            reader.scanChar();
+            reader.putChar(true);
             scanFractionAndSuffix(pos);
         } else if (digitRadix == 10 &&
                    (reader.ch == 'e' || reader.ch == 'E' ||
@@ -393,10 +350,7 @@
         boolean isJavaIdentifierPart;
         char high;
         do {
-            if (sp == sbuf.length) putChar(reader.ch); else sbuf[sp++] = reader.ch;
-            // optimization, was: putChar(reader.ch);
-
-            reader.scanChar();
+            reader.putChar(true);
             switch (reader.ch) {
             case 'A': case 'B': case 'C': case 'D': case 'E':
             case 'F': case 'G': case 'H': case 'I': case 'J':
@@ -423,7 +377,7 @@
                 break;
             case '\u001A': // EOI is also a legal identifier part
                 if (reader.bp >= reader.buflen) {
-                    name = names.fromChars(sbuf, 0, sp);
+                    name = reader.name();
                     tk = tokens.lookupKind(name);
                     return;
                 }
@@ -435,11 +389,7 @@
                 } else {
                     high = reader.scanSurrogates();
                     if (high != 0) {
-                        if (sp == sbuf.length) {
-                            putChar(high);
-                        } else {
-                            sbuf[sp++] = high;
-                        }
+                        reader.putChar(high);
                         isJavaIdentifierPart = Character.isJavaIdentifierPart(
                             Character.toCodePoint(high, reader.ch));
                     } else {
@@ -447,7 +397,7 @@
                     }
                 }
                 if (!isJavaIdentifierPart) {
-                    name = names.fromChars(sbuf, 0, sp);
+                    name = reader.name();
                     tk = tokens.lookupKind(name);
                     return;
                 }
@@ -474,11 +424,11 @@
      */
     private void scanOperator() {
         while (true) {
-            putChar(reader.ch);
-            Name newname = names.fromChars(sbuf, 0, sp);
+            reader.putChar(false);
+            Name newname = reader.name();
             TokenKind tk1 = tokens.lookupKind(newname);
             if (tk1 == TokenKind.IDENTIFIER) {
-                sp--;
+                reader.sp--;
                 break;
             }
             tk = tk1;
@@ -487,111 +437,17 @@
         }
     }
 
-    /**
-     * 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.
      */
     public Token readToken() {
 
-        sp = 0;
+        reader.sp = 0;
         name = null;
-        deprecatedFlag = false;
         radix = 0;
+
         int pos = 0;
         int endPos = 0;
+        List<Comment> comments = null;
 
         try {
             loop: while (true) {
@@ -656,7 +512,7 @@
                             scanNumber(pos, 2);
                         }
                     } else {
-                        putChar('0');
+                        reader.putChar('0');
                         if (reader.ch == '_') {
                             int savePos = reader.bp;
                             do {
@@ -676,14 +532,13 @@
                 case '.':
                     reader.scanChar();
                     if ('0' <= reader.ch && reader.ch <= '9') {
-                        putChar('.');
+                        reader.putChar('.');
                         scanFractionAndSuffix(pos);
                     } else if (reader.ch == '.') {
-                        putChar('.'); putChar('.');
-                        reader.scanChar();
+                        reader.putChar('.'); reader.putChar('.', true);
                         if (reader.ch == '.') {
                             reader.scanChar();
-                            putChar('.');
+                            reader.putChar('.');
                             tk = TokenKind.ELLIPSIS;
                         } else {
                             lexError(pos, "malformed.fp.lit");
@@ -712,32 +567,36 @@
                     reader.scanChar();
                     if (reader.ch == '/') {
                         do {
-                            scanCommentChar();
+                            reader.scanCommentChar();
                         } while (reader.ch != CR && reader.ch != LF && 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;
                     } else if (reader.ch == '*') {
+                        boolean isEmpty = false;
                         reader.scanChar();
                         CommentStyle style;
                         if (reader.ch == '*') {
                             style = CommentStyle.JAVADOC;
-                            scanDocComment();
+                            reader.scanCommentChar();
+                            if (reader.ch == '/') {
+                                isEmpty = true;
+                            }
                         } else {
                             style = CommentStyle.BLOCK;
-                            while (reader.bp < reader.buflen) {
-                                if (reader.ch == '*') {
-                                    reader.scanChar();
-                                    if (reader.ch == '/') break;
-                                } else {
-                                    scanCommentChar();
-                                }
+                        }
+                        while (!isEmpty && reader.bp < reader.buflen) {
+                            if (reader.ch == '*') {
+                                reader.scanChar();
+                                if (reader.ch == '/') break;
+                            } else {
+                                reader.scanCommentChar();
                             }
                         }
                         if (reader.ch == '/') {
                             reader.scanChar();
-                            processComment(pos, reader.bp, style);
+                            comments = addDocReader(comments, processComment(pos, reader.bp, style));
                             break;
                         } else {
                             lexError(pos, "unclosed.comment");
@@ -789,11 +648,7 @@
                         } else {
                             char high = reader.scanSurrogates();
                             if (high != 0) {
-                                if (sp == sbuf.length) {
-                                    putChar(high);
-                                } else {
-                                    sbuf[sp++] = high;
-                                }
+                                reader.putChar(high);
 
                                 isJavaIdentifierStart = Character.isJavaIdentifierStart(
                                     Character.toCodePoint(high, reader.ch));
@@ -816,10 +671,10 @@
             }
             endPos = reader.bp;
             switch (tk.tag) {
-                case DEFAULT: return new Token(tk, pos, endPos, deprecatedFlag);
-                case NAMED: return new NamedToken(tk, pos, endPos, name, deprecatedFlag);
-                case STRING: return new StringToken(tk, pos, endPos, new String(sbuf, 0, sp), deprecatedFlag);
-                case NUMERIC: return new NumericToken(tk, pos, endPos, new String(sbuf, 0, sp), radix, deprecatedFlag);
+                case DEFAULT: return new Token(tk, pos, endPos, comments);
+                case NAMED: return new NamedToken(tk, pos, endPos, name, comments);
+                case STRING: return new StringToken(tk, pos, endPos, reader.chars(), comments);
+                case NUMERIC: return new NumericToken(tk, pos, endPos, reader.chars(), radix, comments);
                 default: throw new AssertionError();
             }
         }
@@ -832,6 +687,12 @@
             }
         }
     }
+    //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;
      */
@@ -845,22 +706,18 @@
         errPos = pos;
     }
 
-    public enum CommentStyle {
-        LINE,
-        BLOCK,
-        JAVADOC,
-    }
-
     /**
      * Called when a complete comment has been scanned. pos and endPos
      * 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)
             System.out.println("processComment(" + pos
                                + "," + endPos + "," + style + ")=|"
                                + 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 Position.LineMap getLineMap() {
         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;
+            }
+        }
+    }
 }
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Tue Nov 01 15:49:45 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Nov 04 12:36:40 2011 +0000
@@ -29,6 +29,7 @@
 
 import com.sun.tools.javac.code.*;
 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.JCTree.*;
 import com.sun.tools.javac.util.*;
@@ -1584,7 +1585,7 @@
                 break;
             case MONKEYS_AT:
             case FINAL: {
-                String dc = token.docComment;
+                String dc = token.comment(CommentStyle.JAVADOC);
                 JCModifiers mods = modifiersOpt();
                 if (token.kind == INTERFACE ||
                     token.kind == CLASS ||
@@ -1601,21 +1602,21 @@
                 break;
             }
             case ABSTRACT: case STRICTFP: {
-                String dc = token.docComment;
+                String dc = token.comment(CommentStyle.JAVADOC);
                 JCModifiers mods = modifiersOpt();
                 stats.append(classOrInterfaceOrEnumDeclaration(mods, dc));
                 break;
             }
             case INTERFACE:
             case CLASS:
-                String dc = token.docComment;
+                String dc = token.comment(CommentStyle.JAVADOC);
                 stats.append(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
                 break;
             case ENUM:
             case ASSERT:
                 if (allowEnums && token.kind == ENUM) {
                     error(token.pos, "local.enum");
-                    dc = token.docComment;
+                    dc = token.comment(CommentStyle.JAVADOC);
                     stats.append(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
                     break;
                 } else if (allowAsserts && token.kind == ASSERT) {
@@ -1991,7 +1992,7 @@
             annotations.appendList(partial.annotations);
             pos = partial.pos;
         }
-        if (token.deprecatedFlag) {
+        if (token.deprecatedFlag()) {
             flags |= Flags.DEPRECATED;
         }
         int lastPos = Position.NOPOS;
@@ -2271,9 +2272,9 @@
                 seenImport = true;
                 defs.append(importDeclaration());
             } else {
-                String docComment = token.docComment;
+                String docComment = token.comment(CommentStyle.JAVADOC);
                 if (firstTypeDecl && !seenImport && !seenPackage) {
-                    docComment = firstToken.docComment;
+                    docComment = firstToken.comment(CommentStyle.JAVADOC);
                     consumedToplevelDoc = true;
                 }
                 JCTree def = typeDeclaration(mods, docComment);
@@ -2288,7 +2289,7 @@
         }
         JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(packageAnnotations, pid, defs.toList());
         if (!consumedToplevelDoc)
-            attach(toplevel, firstToken.docComment);
+            attach(toplevel, firstToken.comment(CommentStyle.JAVADOC));
         if (defs.elems.isEmpty())
             storeEnd(toplevel, S.prevToken().endPos);
         if (keepDocComments)
@@ -2498,9 +2499,9 @@
     /** EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ]
      */
     JCTree enumeratorDeclaration(Name enumName) {
-        String dc = token.docComment;
+        String dc = token.comment(CommentStyle.JAVADOC);
         int flags = Flags.PUBLIC|Flags.STATIC|Flags.FINAL|Flags.ENUM;
-        if (token.deprecatedFlag) {
+        if (token.deprecatedFlag()) {
             flags |= Flags.DEPRECATED;
         }
         int pos = token.pos;
@@ -2587,7 +2588,7 @@
             nextToken();
             return List.<JCTree>nil();
         } else {
-            String dc = token.docComment;
+            String dc = token.comment(CommentStyle.JAVADOC);
             int pos = token.pos;
             JCModifiers mods = modifiersOpt();
             if (token.kind == CLASS ||
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavadocTokenizer.java	Tue Nov 01 15:49:45 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavadocTokenizer.java	Fri Nov 04 12:36:40 2011 +0000
@@ -25,8 +25,8 @@
 
 package com.sun.tools.javac.parser;
 
-import com.sun.tools.javac.file.JavacFileManager;
-import com.sun.tools.javac.parser.Tokens.Token;
+import com.sun.tools.javac.parser.Tokens.Comment;
+import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
 import com.sun.tools.javac.util.*;
 
 import java.nio.*;
@@ -59,352 +59,295 @@
         super(fac, input, inputLength);
     }
 
-    /** The comment input buffer, index of next chacter to be read,
-     *  index of one past last character in buffer.
-     */
-    private char[] buf;
-    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.
-     */
-    private int digit(int base) {
-        char c = ch;
-        int result = Character.digit(c, base);
-        if (result >= 0 && c > 0x7f) {
-            ch = "0123456789abcdef".charAt(result);
-        }
-        return result;
-    }
-
-    /** Convert Unicode escape; bp points to initial '\' character
-     *  (Spec 3.3).
-     */
-    private 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(16);
-                    int code = d;
-                    while (bp < limit && d >= 0) {
-                        bp++; ch = buf[bp]; col++;
-                        d = digit(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--;
-            }
-        }
-    }
-
-
-    /** 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
-    public Token readToken() {
-        docComment = null;
-        Token tk = super.readToken();
-        tk.docComment = docComment;
-        return tk;
+    protected Comment processComment(int pos, int endPos, CommentStyle style) {
+        char[] buf = reader.getRawCharacters(pos, endPos);
+        return new JavadocComment(new ColReader(fac, buf, buf.length), style);
     }
 
     /**
-     * Read next character in doc comment, skipping over double '\' characters.
-     * If a double '\' is skipped, put in the buffer and update buffer count.
+     * This is a specialized version of UnicodeReader that keeps track of the
+     * column position within a given character stream (used for Javadoc processing).
      */
-    private void scanDocCommentChar() {
-        scanChar();
-        if (ch == '\\') {
-            if (buf[bp+1] == '\\' && unicodeConversionBp != bp) {
-                if (docCommentCount == docCommentBuffer.length)
-                    expandCommentBuffer();
-                docCommentBuffer[docCommentCount++] = ch;
-                bp++; col++;
-            } else {
-                convertUnicode();
+    static class ColReader extends UnicodeReader {
+
+         int col;
+
+         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);
+        }
+
+        public String getText() {
+            if (!scanned && cs == CommentStyle.JAVADOC) {
+                scanDocComment();
+            }
+            return docComment;
+        }
+
+        @Override
+        @SuppressWarnings("fallthrough")
+        protected void scanDocComment() {
+             try {
+                 boolean firstLine = true;
+
+                 // Skip over first slash
+                 comment_reader.scanCommentChar();
+                 // Skip over first star
+                 comment_reader.scanCommentChar();
+
+                 // consume any number of stars
+                 while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == '*') {
+                     comment_reader.scanCommentChar();
+                 }
+                 // 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;
+                }
             }
         }
     }
 
-    /**
-     * 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 */
+    @Override
     public Position.LineMap getLineMap() {
         char[] buf = reader.getRawCharacters();
         return Position.makeLineMap(buf, buf.length, true);
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/Tokens.java	Tue Nov 01 15:49:45 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/Tokens.java	Fri Nov 04 12:36:40 2011 +0000
@@ -30,8 +30,10 @@
 import com.sun.tools.javac.api.Formattable;
 import com.sun.tools.javac.api.Messages;
 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.Context;
+import com.sun.tools.javac.util.ListBuffer;
 import com.sun.tools.javac.util.Names;
 
 /** A class that defines codes/utilities for Java source tokens
@@ -281,6 +283,19 @@
         }
     }
 
+    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
      * that are set by the javac lexer (i.e. start/end position, string value, etc).
@@ -304,18 +319,14 @@
         /** The end position of this token */
         public final int endPos;
 
-        /** Is this token preceeded by a deprecated comment? */
-        public final boolean deprecatedFlag;
+        /** Comment reader associated with this token */
+        public final List<Comment> comments;
 
-        /** Is this token preceeded by a deprecated comment? */
-        public String docComment;
-
-        Token(TokenKind kind, int pos, int endPos,
-                boolean deprecatedFlag) {
+        Token(TokenKind kind, int pos, int endPos, List<Comment> comments) {
             this.kind = kind;
             this.pos = pos;
             this.endPos = endPos;
-            this.deprecatedFlag = deprecatedFlag;
+            this.comments = comments;
             checkKind();
         }
 
@@ -331,8 +342,8 @@
                 throw new AssertionError("Cant split - bad subtokens");
             }
             return new Token[] {
-                new Token(t1, pos, pos + t1.name.length(), deprecatedFlag),
-                new Token(t2, pos + t1.name.length(), endPos, false)
+                new Token(t1, pos, pos + t1.name.length(), comments),
+                new Token(t2, pos + t1.name.length(), endPos, null)
             };
         }
 
@@ -353,14 +364,52 @@
         public int radix() {
             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 {
         /** The name of this token */
         public final Name name;
 
-        public NamedToken(TokenKind kind, int pos, int endPos, Name name, boolean deprecatedFlag) {
-            super(kind, pos, endPos, deprecatedFlag);
+        public NamedToken(TokenKind kind, int pos, int endPos, Name name, List<Comment> comments) {
+            super(kind, pos, endPos, comments);
             this.name = name;
         }
 
@@ -380,8 +429,8 @@
         /** The string value of this token */
         public final String stringVal;
 
-        public StringToken(TokenKind kind, int pos, int endPos, String stringVal, boolean deprecatedFlag) {
-            super(kind, pos, endPos, deprecatedFlag);
+        public StringToken(TokenKind kind, int pos, int endPos, String stringVal, List<Comment> comments) {
+            super(kind, pos, endPos, comments);
             this.stringVal = stringVal;
         }
 
@@ -401,8 +450,8 @@
         /** The 'radix' value of this token */
         public final int radix;
 
-        public NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, boolean deprecatedFlag) {
-            super(kind, pos, endPos, stringVal, deprecatedFlag);
+        public NumericToken(TokenKind kind, int pos, int endPos, String stringVal, int radix, List<Comment> comments) {
+            super(kind, pos, endPos, stringVal, comments);
             this.radix = radix;
         }
 
@@ -419,5 +468,5 @@
     }
 
     public static final Token DUMMY =
-                new Token(TokenKind.ERROR, 0, 0, false);
+                new Token(TokenKind.ERROR, 0, 0, null);
 }
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/UnicodeReader.java	Tue Nov 01 15:49:45 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/UnicodeReader.java	Fri Nov 04 12:36:40 2011 +0000
@@ -26,8 +26,12 @@
 package com.sun.tools.javac.parser;
 
 import com.sun.tools.javac.file.JavacFileManager;
+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 com.sun.tools.javac.util.Log;
+
 import static com.sun.tools.javac.util.LayoutCharacters.*;
 
 /** The char reader used by the javac lexer/tokenizer. Returns the sequence of
@@ -58,6 +62,12 @@
     protected int unicodeConversionBp = -1;
 
     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
@@ -76,6 +86,7 @@
 
     protected UnicodeReader(ScannerFactory sf, char[] input, int inputLength) {
         log = sf.log;
+        names = sf.names;
         if (inputLength == input.length) {
             if (input.length > 0 && Character.isWhitespace(input[input.length - 1])) {
                 inputLength--;
@@ -103,6 +114,48 @@
         }
     }
 
+    /** 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
      *  (Spec 3.3).
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/depDocComment/DeprecatedDocComment4.java	Fri Nov 04 12:36:40 2011 +0000
@@ -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() {};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/depDocComment/DeprecatedDocComment4.out	Fri Nov 04 12:36:40 2011 +0000
@@ -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