src/share/classes/com/sun/tools/javac/parser/DocCommentParser.java

Mon, 25 Nov 2013 17:42:28 -0800

author
jjg
date
Mon, 25 Nov 2013 17:42:28 -0800
changeset 2204
a78f51d6bd5e
parent 1704
ed918a442b83
child 2268
b4e592c5314d
permissions
-rw-r--r--

8028318: [doclint] doclint will reject existing user-written doc comments using custom tags that follow the recommended rules
Reviewed-by: darcy

jjg@1409 1 /*
jjg@1529 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
jjg@1409 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jjg@1409 4 *
jjg@1409 5 * This code is free software; you can redistribute it and/or modify it
jjg@1409 6 * under the terms of the GNU General Public License version 2 only, as
jjg@1409 7 * published by the Free Software Foundation. Oracle designates this
jjg@1409 8 * particular file as subject to the "Classpath" exception as provided
jjg@1409 9 * by Oracle in the LICENSE file that accompanied this code.
jjg@1409 10 *
jjg@1409 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jjg@1409 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jjg@1409 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jjg@1409 14 * version 2 for more details (a copy is included in the LICENSE file that
jjg@1409 15 * accompanied this code).
jjg@1409 16 *
jjg@1409 17 * You should have received a copy of the GNU General Public License version
jjg@1409 18 * 2 along with this work; if not, write to the Free Software Foundation,
jjg@1409 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jjg@1409 20 *
jjg@1409 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jjg@1409 22 * or visit www.oracle.com if you need additional information or have any
jjg@1409 23 * questions.
jjg@1409 24 */
jjg@1409 25
jjg@1409 26 package com.sun.tools.javac.parser;
jjg@1409 27
jjg@1409 28 import java.text.BreakIterator;
jjg@1409 29 import java.util.Arrays;
jjg@1409 30 import java.util.HashMap;
jjg@1409 31 import java.util.HashSet;
jjg@1409 32 import java.util.Locale;
jjg@1409 33 import java.util.Map;
jjg@1409 34 import java.util.Set;
jjg@1409 35
jjg@1409 36 import com.sun.source.doctree.AttributeTree.ValueKind;
jjg@1409 37 import com.sun.tools.javac.parser.DocCommentParser.TagParser.Kind;
jjg@1409 38 import com.sun.tools.javac.parser.Tokens.Comment;
jjg@1409 39 import com.sun.tools.javac.parser.Tokens.TokenKind;
jjg@1409 40 import com.sun.tools.javac.tree.DCTree;
jjg@1409 41 import com.sun.tools.javac.tree.DCTree.DCAttribute;
jjg@1409 42 import com.sun.tools.javac.tree.DCTree.DCDocComment;
jjg@1409 43 import com.sun.tools.javac.tree.DCTree.DCEndElement;
jlahoda@1704 44 import com.sun.tools.javac.tree.DCTree.DCEndPosTree;
jjg@1409 45 import com.sun.tools.javac.tree.DCTree.DCErroneous;
jjg@1409 46 import com.sun.tools.javac.tree.DCTree.DCIdentifier;
jjg@1409 47 import com.sun.tools.javac.tree.DCTree.DCReference;
jjg@1409 48 import com.sun.tools.javac.tree.DCTree.DCStartElement;
jjg@1409 49 import com.sun.tools.javac.tree.DCTree.DCText;
jjg@1409 50 import com.sun.tools.javac.tree.DocTreeMaker;
jjg@1409 51 import com.sun.tools.javac.tree.JCTree;
jjg@1409 52 import com.sun.tools.javac.util.DiagnosticSource;
jjg@1409 53 import com.sun.tools.javac.util.List;
jjg@1409 54 import com.sun.tools.javac.util.ListBuffer;
jjg@1409 55 import com.sun.tools.javac.util.Log;
jjg@1409 56 import com.sun.tools.javac.util.Name;
jjg@1409 57 import com.sun.tools.javac.util.Names;
jjg@1409 58 import com.sun.tools.javac.util.Options;
jjg@1409 59 import com.sun.tools.javac.util.Position;
jjg@1409 60 import static com.sun.tools.javac.util.LayoutCharacters.*;
jjg@1409 61
jjg@1409 62 /**
jjg@1409 63 *
jjg@1409 64 * <p><b>This is NOT part of any supported API.
jjg@1409 65 * If you write code that depends on this, you do so at your own risk.
jjg@1409 66 * This code and its internal interfaces are subject to change or
jjg@1409 67 * deletion without notice.</b>
jjg@1409 68 */
jjg@1409 69 public class DocCommentParser {
jjg@1409 70 static class ParseException extends Exception {
jjg@1409 71 private static final long serialVersionUID = 0;
jjg@1409 72 ParseException(String key) {
jjg@1409 73 super(key);
jjg@1409 74 }
jjg@1409 75 }
jjg@1409 76
jjg@1409 77 final ParserFactory fac;
jjg@1409 78 final DiagnosticSource diagSource;
jjg@1409 79 final Comment comment;
jjg@1409 80 final DocTreeMaker m;
jjg@1409 81 final Names names;
jjg@1409 82
jjg@1409 83 BreakIterator sentenceBreaker;
jjg@1409 84
jjg@1409 85 /** The input buffer, index of most recent character read,
jjg@1409 86 * index of one past last character in buffer.
jjg@1409 87 */
jjg@1409 88 protected char[] buf;
jjg@1409 89 protected int bp;
jjg@1409 90 protected int buflen;
jjg@1409 91
jjg@1409 92 /** The current character.
jjg@1409 93 */
jjg@1409 94 protected char ch;
jjg@1409 95
jjg@1409 96 int textStart = -1;
jjg@1409 97 int lastNonWhite = -1;
jjg@1409 98 boolean newline = true;
jjg@1409 99
jjg@1409 100 Map<Name, TagParser> tagParsers;
jjg@1409 101
jjg@1409 102 DocCommentParser(ParserFactory fac, DiagnosticSource diagSource, Comment comment) {
jjg@1409 103 this.fac = fac;
jjg@1409 104 this.diagSource = diagSource;
jjg@1409 105 this.comment = comment;
jjg@1409 106 names = fac.names;
jjg@1409 107 m = fac.docTreeMaker;
jjg@1409 108
jjg@1409 109 Locale locale = (fac.locale == null) ? Locale.getDefault() : fac.locale;
jjg@1409 110
jjg@1409 111 Options options = fac.options;
jjg@1409 112 boolean useBreakIterator = options.isSet("breakIterator");
jjg@1409 113 if (useBreakIterator || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage()))
jjg@1409 114 sentenceBreaker = BreakIterator.getSentenceInstance(locale);
jjg@1409 115
jjg@1409 116 initTagParsers();
jjg@1409 117 }
jjg@1409 118
jjg@1409 119 DCDocComment parse() {
jjg@1409 120 String c = comment.getText();
jjg@1409 121 buf = new char[c.length() + 1];
jjg@1409 122 c.getChars(0, c.length(), buf, 0);
jjg@1409 123 buf[buf.length - 1] = EOI;
jjg@1409 124 buflen = buf.length - 1;
jjg@1409 125 bp = -1;
jjg@1409 126 nextChar();
jjg@1409 127
jjg@1409 128 List<DCTree> body = blockContent();
jjg@1409 129 List<DCTree> tags = blockTags();
jjg@1409 130
jjg@1409 131 // split body into first sentence and body
jjg@1409 132 ListBuffer<DCTree> fs = new ListBuffer<DCTree>();
jjg@1409 133 loop:
jjg@1409 134 for (; body.nonEmpty(); body = body.tail) {
jjg@1409 135 DCTree t = body.head;
jjg@1409 136 switch (t.getKind()) {
jjg@1409 137 case TEXT:
jjg@1409 138 String s = ((DCText) t).getBody();
jjg@1409 139 int i = getSentenceBreak(s);
jjg@1409 140 if (i > 0) {
jjg@1409 141 int i0 = i;
jjg@1409 142 while (i0 > 0 && isWhitespace(s.charAt(i0 - 1)))
jjg@1409 143 i0--;
jjg@1409 144 fs.add(m.at(t.pos).Text(s.substring(0, i0)));
jjg@1409 145 int i1 = i;
jjg@1409 146 while (i1 < s.length() && isWhitespace(s.charAt(i1)))
jjg@1409 147 i1++;
jjg@1409 148 body = body.tail;
jjg@1409 149 if (i1 < s.length())
jjg@1409 150 body = body.prepend(m.at(t.pos + i1).Text(s.substring(i1)));
jjg@1409 151 break loop;
jjg@1409 152 } else if (body.tail.nonEmpty()) {
jjg@1409 153 if (isSentenceBreak(body.tail.head)) {
jjg@1409 154 int i0 = s.length() - 1;
jjg@1409 155 while (i0 > 0 && isWhitespace(s.charAt(i0)))
jjg@1409 156 i0--;
jjg@1409 157 fs.add(m.at(t.pos).Text(s.substring(0, i0 + 1)));
jjg@1409 158 body = body.tail;
jjg@1409 159 break loop;
jjg@1409 160 }
jjg@1409 161 }
jjg@1409 162 break;
jjg@1409 163
jjg@1409 164 case START_ELEMENT:
jjg@1409 165 case END_ELEMENT:
jjg@1409 166 if (isSentenceBreak(t))
jjg@1409 167 break loop;
jjg@1409 168 break;
jjg@1409 169 }
jjg@1409 170 fs.add(t);
jjg@1409 171 }
jjg@1409 172
jjg@1409 173 @SuppressWarnings("unchecked")
jjg@1409 174 DCTree first = getFirst(fs.toList(), body, tags);
jjg@1409 175 int pos = (first == null) ? Position.NOPOS : first.pos;
jjg@1409 176
jjg@1409 177 DCDocComment dc = m.at(pos).DocComment(comment, fs.toList(), body, tags);
jjg@1409 178 return dc;
jjg@1409 179 }
jjg@1409 180
jjg@1409 181 void nextChar() {
jjg@1409 182 ch = buf[bp < buflen ? ++bp : buflen];
jjg@1409 183 switch (ch) {
jjg@1409 184 case '\f': case '\n': case '\r':
jjg@1409 185 newline = true;
jjg@1409 186 }
jjg@1409 187 }
jjg@1409 188
jjg@1409 189 /**
jjg@1409 190 * Read block content, consisting of text, html and inline tags.
jjg@1409 191 * Terminated by the end of input, or the beginning of the next block tag:
jjg@1409 192 * i.e. @ as the first non-whitespace character on a line.
jjg@1409 193 */
jjg@1409 194 @SuppressWarnings("fallthrough")
jjg@1409 195 protected List<DCTree> blockContent() {
jjg@1409 196 ListBuffer<DCTree> trees = new ListBuffer<DCTree>();
jjg@1409 197 textStart = -1;
jjg@1409 198
jjg@1409 199 loop:
jjg@1409 200 while (bp < buflen) {
jjg@1409 201 switch (ch) {
jjg@1409 202 case '\n': case '\r': case '\f':
jjg@1409 203 newline = true;
jjg@1409 204 // fallthrough
jjg@1409 205
jjg@1409 206 case ' ': case '\t':
jjg@1409 207 nextChar();
jjg@1409 208 break;
jjg@1409 209
jjg@1409 210 case '&':
jjg@1409 211 entity(trees);
jjg@1409 212 break;
jjg@1409 213
jjg@1409 214 case '<':
jjg@1409 215 newline = false;
jjg@1409 216 addPendingText(trees, bp - 1);
jjg@1409 217 trees.add(html());
jjg@1409 218 if (textStart == -1) {
jjg@1409 219 textStart = bp;
jjg@1409 220 lastNonWhite = -1;
jjg@1409 221 }
jjg@1409 222 break;
jjg@1409 223
jjg@1409 224 case '>':
jjg@1409 225 newline = false;
jjg@1409 226 addPendingText(trees, bp - 1);
jjg@1409 227 trees.add(m.at(bp).Erroneous(newString(bp, bp+1), diagSource, "dc.bad.gt"));
jjg@1409 228 nextChar();
jjg@1409 229 if (textStart == -1) {
jjg@1409 230 textStart = bp;
jjg@1409 231 lastNonWhite = -1;
jjg@1409 232 }
jjg@1409 233 break;
jjg@1409 234
jjg@1409 235 case '{':
jjg@1409 236 inlineTag(trees);
jjg@1409 237 break;
jjg@1409 238
jjg@1409 239 case '@':
jjg@1409 240 if (newline) {
jjg@1409 241 addPendingText(trees, lastNonWhite);
jjg@1409 242 break loop;
jjg@1409 243 }
jjg@1409 244 // fallthrough
jjg@1409 245
jjg@1409 246 default:
jjg@1409 247 newline = false;
jjg@1409 248 if (textStart == -1)
jjg@1409 249 textStart = bp;
jjg@1409 250 lastNonWhite = bp;
jjg@1409 251 nextChar();
jjg@1409 252 }
jjg@1409 253 }
jjg@1409 254
jjg@1409 255 if (lastNonWhite != -1)
jjg@1409 256 addPendingText(trees, lastNonWhite);
jjg@1409 257
jjg@1409 258 return trees.toList();
jjg@1409 259 }
jjg@1409 260
jjg@1409 261 /**
jjg@1409 262 * Read a series of block tags, including their content.
jjg@1409 263 * Standard tags parse their content appropriately.
jjg@1409 264 * Non-standard tags are represented by {@link UnknownBlockTag}.
jjg@1409 265 */
jjg@1409 266 protected List<DCTree> blockTags() {
jjg@1409 267 ListBuffer<DCTree> tags = new ListBuffer<DCTree>();
jjg@1409 268 while (ch == '@')
jjg@1409 269 tags.add(blockTag());
jjg@1409 270 return tags.toList();
jjg@1409 271 }
jjg@1409 272
jjg@1409 273 /**
jjg@1409 274 * Read a single block tag, including its content.
jjg@1409 275 * Standard tags parse their content appropriately.
jjg@1409 276 * Non-standard tags are represented by {@link UnknownBlockTag}.
jjg@1409 277 */
jjg@1409 278 protected DCTree blockTag() {
jjg@1409 279 int p = bp;
jjg@1409 280 try {
jjg@1409 281 nextChar();
jjg@1409 282 if (isIdentifierStart(ch)) {
jjg@2204 283 Name name = readTagName();
jjg@1409 284 TagParser tp = tagParsers.get(name);
jjg@1409 285 if (tp == null) {
jjg@1409 286 List<DCTree> content = blockContent();
jjg@1409 287 return m.at(p).UnknownBlockTag(name, content);
jjg@1409 288 } else {
jjg@1409 289 switch (tp.getKind()) {
jjg@1409 290 case BLOCK:
jjg@1409 291 return tp.parse(p);
jjg@1409 292 case INLINE:
jjg@1409 293 return erroneous("dc.bad.inline.tag", p);
jjg@1409 294 }
jjg@1409 295 }
jjg@1409 296 }
jjg@1409 297 blockContent();
jjg@1409 298
jjg@1409 299 return erroneous("dc.no.tag.name", p);
jjg@1409 300 } catch (ParseException e) {
jjg@1409 301 blockContent();
jjg@1409 302 return erroneous(e.getMessage(), p);
jjg@1409 303 }
jjg@1409 304 }
jjg@1409 305
jjg@1409 306 protected void inlineTag(ListBuffer<DCTree> list) {
jjg@1409 307 newline = false;
jjg@1409 308 nextChar();
jjg@1409 309 if (ch == '@') {
jjg@1409 310 addPendingText(list, bp - 2);
jjg@1409 311 list.add(inlineTag());
jjg@1409 312 textStart = bp;
jjg@1409 313 lastNonWhite = -1;
jjg@1409 314 } else {
jjg@1409 315 if (textStart == -1)
jjg@1409 316 textStart = bp - 1;
jjg@1409 317 lastNonWhite = bp;
jjg@1409 318 }
jjg@1409 319 }
jjg@1409 320
jjg@1409 321 /**
jjg@1409 322 * Read a single inline tag, including its content.
jjg@1409 323 * Standard tags parse their content appropriately.
jjg@1409 324 * Non-standard tags are represented by {@link UnknownBlockTag}.
jjg@1409 325 * Malformed tags may be returned as {@link Erroneous}.
jjg@1409 326 */
jjg@1409 327 protected DCTree inlineTag() {
jjg@1409 328 int p = bp - 1;
jjg@1409 329 try {
jjg@1409 330 nextChar();
jjg@1409 331 if (isIdentifierStart(ch)) {
jjg@2204 332 Name name = readTagName();
jjg@1409 333 skipWhitespace();
jjg@1409 334
jjg@1409 335 TagParser tp = tagParsers.get(name);
jjg@1409 336 if (tp == null) {
jjg@1409 337 DCTree text = inlineText();
jjg@1409 338 if (text != null) {
jjg@1409 339 nextChar();
jlahoda@1704 340 return m.at(p).UnknownInlineTag(name, List.of(text)).setEndPos(bp);
jjg@1409 341 }
jjg@1409 342 } else if (tp.getKind() == TagParser.Kind.INLINE) {
jlahoda@1704 343 DCEndPosTree<?> tree = (DCEndPosTree<?>) tp.parse(p);
jjg@1409 344 if (tree != null) {
jlahoda@1704 345 return tree.setEndPos(bp);
jjg@1409 346 }
jjg@1409 347 } else {
jjg@1409 348 inlineText(); // skip content
jjg@1409 349 nextChar();
jjg@1409 350 }
jjg@1409 351 }
jjg@1409 352 return erroneous("dc.no.tag.name", p);
jjg@1409 353 } catch (ParseException e) {
jjg@1409 354 return erroneous(e.getMessage(), p);
jjg@1409 355 }
jjg@1409 356 }
jjg@1409 357
jjg@1409 358 /**
jjg@1409 359 * Read plain text content of an inline tag.
jjg@1409 360 * Matching pairs of { } are skipped; the text is terminated by the first
jjg@1409 361 * unmatched }. It is an error if the beginning of the next tag is detected.
jjg@1409 362 */
jjg@1409 363 protected DCTree inlineText() throws ParseException {
jjg@1409 364 skipWhitespace();
jjg@1409 365 int pos = bp;
jjg@1409 366 int depth = 1;
jjg@1409 367
jjg@1409 368 loop:
jjg@1409 369 while (bp < buflen) {
jjg@1409 370 switch (ch) {
jjg@1409 371 case '\n': case '\r': case '\f':
jjg@1409 372 newline = true;
jjg@1409 373 break;
jjg@1409 374
jjg@1409 375 case ' ': case '\t':
jjg@1409 376 break;
jjg@1409 377
jjg@1409 378 case '{':
jjg@1409 379 newline = false;
jjg@1409 380 lastNonWhite = bp;
jjg@1409 381 depth++;
jjg@1409 382 break;
jjg@1409 383
jjg@1409 384 case '}':
jjg@1409 385 if (--depth == 0) {
jjg@1409 386 return m.at(pos).Text(newString(pos, bp));
jjg@1409 387 }
jjg@1409 388 newline = false;
jjg@1409 389 lastNonWhite = bp;
jjg@1409 390 break;
jjg@1409 391
jjg@1409 392 case '@':
jjg@1409 393 if (newline)
jjg@1409 394 break loop;
jjg@1409 395 newline = false;
jjg@1409 396 lastNonWhite = bp;
jjg@1409 397 break;
jjg@1409 398
jjg@1409 399 default:
jjg@1409 400 newline = false;
jjg@1409 401 lastNonWhite = bp;
jjg@1409 402 break;
jjg@1409 403 }
jjg@1409 404 nextChar();
jjg@1409 405 }
jjg@1409 406 throw new ParseException("dc.unterminated.inline.tag");
jjg@1409 407 }
jjg@1409 408
jjg@1409 409 /**
jjg@1409 410 * Read Java class name, possibly followed by member
jjg@1409 411 * Matching pairs of < > are skipped. The text is terminated by the first
jjg@1409 412 * unmatched }. It is an error if the beginning of the next tag is detected.
jjg@1409 413 */
jjg@1409 414 // TODO: boolean allowMember should be enum FORBID, ALLOW, REQUIRE
jjg@1409 415 // TODO: improve quality of parse to forbid bad constructions.
jjg@1409 416 @SuppressWarnings("fallthrough")
jjg@1409 417 protected DCReference reference(boolean allowMember) throws ParseException {
jjg@1409 418 int pos = bp;
jjg@1409 419 int depth = 0;
jjg@1409 420
jjg@1409 421 // scan to find the end of the signature, by looking for the first
jjg@1409 422 // whitespace not enclosed in () or <>, or the end of the tag
jjg@1409 423 loop:
jjg@1409 424 while (bp < buflen) {
jjg@1409 425 switch (ch) {
jjg@1409 426 case '\n': case '\r': case '\f':
jjg@1409 427 newline = true;
jjg@1409 428 // fallthrough
jjg@1409 429
jjg@1409 430 case ' ': case '\t':
jjg@1409 431 if (depth == 0)
jjg@1409 432 break loop;
jjg@1409 433 break;
jjg@1409 434
jjg@1409 435 case '(':
jjg@1409 436 case '<':
jjg@1409 437 newline = false;
jjg@1409 438 depth++;
jjg@1409 439 break;
jjg@1409 440
jjg@1409 441 case ')':
jjg@1409 442 case '>':
jjg@1409 443 newline = false;
jjg@1409 444 --depth;
jjg@1409 445 break;
jjg@1409 446
jjg@1409 447 case '}':
jjg@1409 448 if (bp == pos)
jjg@1409 449 return null;
jjg@1409 450 newline = false;
jjg@1409 451 break loop;
jjg@1409 452
jjg@1409 453 case '@':
jjg@1409 454 if (newline)
jjg@1409 455 break loop;
jjg@1409 456 // fallthrough
jjg@1409 457
jjg@1409 458 default:
jjg@1409 459 newline = false;
jjg@1409 460
jjg@1409 461 }
jjg@1409 462 nextChar();
jjg@1409 463 }
jjg@1409 464
jjg@1409 465 if (depth != 0)
jjg@1409 466 throw new ParseException("dc.unterminated.signature");
jjg@1409 467
jjg@1409 468 String sig = newString(pos, bp);
jjg@1409 469
jjg@1409 470 // Break sig apart into qualifiedExpr member paramTypes.
jjg@1409 471 JCTree qualExpr;
jjg@1409 472 Name member;
jjg@1409 473 List<JCTree> paramTypes;
jjg@1409 474
jjg@1409 475 Log.DeferredDiagnosticHandler deferredDiagnosticHandler
jjg@1409 476 = new Log.DeferredDiagnosticHandler(fac.log);
jjg@1409 477
jjg@1409 478 try {
jjg@1409 479 int hash = sig.indexOf("#");
jjg@1409 480 int lparen = sig.indexOf("(", hash + 1);
jjg@1409 481 if (hash == -1) {
jjg@1409 482 if (lparen == -1) {
jjg@1409 483 qualExpr = parseType(sig);
jjg@1409 484 member = null;
jjg@1409 485 } else {
jjg@1409 486 qualExpr = null;
jjg@1409 487 member = parseMember(sig.substring(0, lparen));
jjg@1409 488 }
jjg@1409 489 } else {
jjg@1409 490 qualExpr = (hash == 0) ? null : parseType(sig.substring(0, hash));
jjg@1409 491 if (lparen == -1)
jjg@1409 492 member = parseMember(sig.substring(hash + 1));
jjg@1409 493 else
jjg@1409 494 member = parseMember(sig.substring(hash + 1, lparen));
jjg@1409 495 }
jjg@1409 496
jjg@1409 497 if (lparen < 0) {
jjg@1409 498 paramTypes = null;
jjg@1409 499 } else {
jjg@1409 500 int rparen = sig.indexOf(")", lparen);
jjg@1409 501 if (rparen != sig.length() - 1)
jjg@1409 502 throw new ParseException("dc.ref.bad.parens");
jjg@1409 503 paramTypes = parseParams(sig.substring(lparen + 1, rparen));
jjg@1409 504 }
jjg@1409 505
jjg@1409 506 if (!deferredDiagnosticHandler.getDiagnostics().isEmpty())
jjg@1409 507 throw new ParseException("dc.ref.syntax.error");
jjg@1409 508
jjg@1409 509 } finally {
jjg@1409 510 fac.log.popDiagnosticHandler(deferredDiagnosticHandler);
jjg@1409 511 }
jjg@1409 512
jlahoda@1704 513 return m.at(pos).Reference(sig, qualExpr, member, paramTypes).setEndPos(bp);
jjg@1409 514 }
jjg@1409 515
jjg@1409 516 JCTree parseType(String s) throws ParseException {
jjg@1409 517 JavacParser p = fac.newParser(s, false, false, false);
jjg@1409 518 JCTree tree = p.parseType();
jjg@1409 519 if (p.token().kind != TokenKind.EOF)
jjg@1409 520 throw new ParseException("dc.ref.unexpected.input");
jjg@1409 521 return tree;
jjg@1409 522 }
jjg@1409 523
jjg@1409 524 Name parseMember(String s) throws ParseException {
jjg@1409 525 JavacParser p = fac.newParser(s, false, false, false);
jjg@1409 526 Name name = p.ident();
jjg@1409 527 if (p.token().kind != TokenKind.EOF)
jjg@1409 528 throw new ParseException("dc.ref.unexpected.input");
jjg@1409 529 return name;
jjg@1409 530 }
jjg@1409 531
jjg@1409 532 List<JCTree> parseParams(String s) throws ParseException {
jjg@1409 533 if (s.trim().isEmpty())
jjg@1409 534 return List.nil();
jjg@1409 535
jjg@1409 536 JavacParser p = fac.newParser(s.replace("...", "[]"), false, false, false);
jjg@1409 537 ListBuffer<JCTree> paramTypes = new ListBuffer<JCTree>();
jjg@1409 538 paramTypes.add(p.parseType());
jjg@1409 539
jjg@1409 540 if (p.token().kind == TokenKind.IDENTIFIER)
jjg@1409 541 p.nextToken();
jjg@1409 542
jjg@1409 543 while (p.token().kind == TokenKind.COMMA) {
jjg@1409 544 p.nextToken();
jjg@1409 545 paramTypes.add(p.parseType());
jjg@1409 546
jjg@1409 547 if (p.token().kind == TokenKind.IDENTIFIER)
jjg@1409 548 p.nextToken();
jjg@1409 549 }
jjg@1409 550
jjg@1409 551 if (p.token().kind != TokenKind.EOF)
jjg@1409 552 throw new ParseException("dc.ref.unexpected.input");
jjg@1409 553
jjg@1409 554 return paramTypes.toList();
jjg@1409 555 }
jjg@1409 556
jjg@1409 557 /**
jjg@1409 558 * Read Java identifier
jjg@1409 559 * Matching pairs of { } are skipped; the text is terminated by the first
jjg@1409 560 * unmatched }. It is an error if the beginning of the next tag is detected.
jjg@1409 561 */
jjg@1409 562 @SuppressWarnings("fallthrough")
jjg@1409 563 protected DCIdentifier identifier() throws ParseException {
jjg@1409 564 skipWhitespace();
jjg@1409 565 int pos = bp;
jjg@1409 566
jjg@1409 567 if (isJavaIdentifierStart(ch)) {
jjg@1529 568 Name name = readJavaIdentifier();
jjg@1529 569 return m.at(pos).Identifier(name);
jjg@1409 570 }
jjg@1409 571
jjg@1409 572 throw new ParseException("dc.identifier.expected");
jjg@1409 573 }
jjg@1409 574
jjg@1409 575 /**
jjg@1409 576 * Read a quoted string.
jjg@1409 577 * It is an error if the beginning of the next tag is detected.
jjg@1409 578 */
jjg@1409 579 @SuppressWarnings("fallthrough")
jjg@1409 580 protected DCText quotedString() {
jjg@1409 581 int pos = bp;
jjg@1409 582 nextChar();
jjg@1409 583
jjg@1409 584 loop:
jjg@1409 585 while (bp < buflen) {
jjg@1409 586 switch (ch) {
jjg@1409 587 case '\n': case '\r': case '\f':
jjg@1409 588 newline = true;
jjg@1409 589 break;
jjg@1409 590
jjg@1409 591 case ' ': case '\t':
jjg@1409 592 break;
jjg@1409 593
jjg@1409 594 case '"':
jjg@1409 595 nextChar();
jjg@1409 596 // trim trailing white-space?
jjg@1409 597 return m.at(pos).Text(newString(pos, bp));
jjg@1409 598
jjg@1409 599 case '@':
jjg@1409 600 if (newline)
jjg@1409 601 break loop;
jjg@1409 602
jjg@1409 603 }
jjg@1409 604 nextChar();
jjg@1409 605 }
jjg@1409 606 return null;
jjg@1409 607 }
jjg@1409 608
jjg@1409 609 /**
jjg@1409 610 * Read general text content of an inline tag, including HTML entities and elements.
jjg@1409 611 * Matching pairs of { } are skipped; the text is terminated by the first
jjg@1409 612 * unmatched }. It is an error if the beginning of the next tag is detected.
jjg@1409 613 */
jjg@1409 614 @SuppressWarnings("fallthrough")
jjg@1409 615 protected List<DCTree> inlineContent() {
jjg@1409 616 ListBuffer<DCTree> trees = new ListBuffer<DCTree>();
jjg@1409 617
jjg@1409 618 skipWhitespace();
jjg@1409 619 int pos = bp;
jjg@1409 620 int depth = 1;
jjg@1409 621 textStart = -1;
jjg@1409 622
jjg@1409 623 loop:
jjg@1409 624 while (bp < buflen) {
jjg@1409 625
jjg@1409 626 switch (ch) {
jjg@1409 627 case '\n': case '\r': case '\f':
jjg@1409 628 newline = true;
jjg@1409 629 // fall through
jjg@1409 630
jjg@1409 631 case ' ': case '\t':
jjg@1409 632 nextChar();
jjg@1409 633 break;
jjg@1409 634
jjg@1409 635 case '&':
jjg@1409 636 entity(trees);
jjg@1409 637 break;
jjg@1409 638
jjg@1409 639 case '<':
jjg@1409 640 newline = false;
jjg@1409 641 addPendingText(trees, bp - 1);
jjg@1409 642 trees.add(html());
jjg@1409 643 break;
jjg@1409 644
jjg@1409 645 case '{':
jjg@1409 646 newline = false;
jjg@1409 647 depth++;
jjg@1409 648 nextChar();
jjg@1409 649 break;
jjg@1409 650
jjg@1409 651 case '}':
jjg@1409 652 newline = false;
jjg@1409 653 if (--depth == 0) {
jjg@1409 654 addPendingText(trees, bp - 1);
jjg@1409 655 nextChar();
jjg@1409 656 return trees.toList();
jjg@1409 657 }
jjg@1409 658 nextChar();
jjg@1409 659 break;
jjg@1409 660
jjg@1409 661 case '@':
jjg@1409 662 if (newline)
jjg@1409 663 break loop;
jjg@1409 664 // fallthrough
jjg@1409 665
jjg@1409 666 default:
jjg@1409 667 if (textStart == -1)
jjg@1409 668 textStart = bp;
jjg@1409 669 nextChar();
jjg@1409 670 break;
jjg@1409 671 }
jjg@1409 672 }
jjg@1409 673
jjg@1409 674 return List.<DCTree>of(erroneous("dc.unterminated.inline.tag", pos));
jjg@1409 675 }
jjg@1409 676
jjg@1409 677 protected void entity(ListBuffer<DCTree> list) {
jjg@1409 678 newline = false;
jjg@1409 679 addPendingText(list, bp - 1);
jjg@1409 680 list.add(entity());
jjg@1409 681 if (textStart == -1) {
jjg@1409 682 textStart = bp;
jjg@1409 683 lastNonWhite = -1;
jjg@1409 684 }
jjg@1409 685 }
jjg@1409 686
jjg@1409 687 /**
jjg@1409 688 * Read an HTML entity.
jjg@1409 689 * {@literal &identifier; } or {@literal &#digits; } or {@literal &#xhex-digits; }
jjg@1409 690 */
jjg@1409 691 protected DCTree entity() {
jjg@1409 692 int p = bp;
jjg@1409 693 nextChar();
jjg@1529 694 Name name = null;
jjg@1409 695 boolean checkSemi = false;
jjg@1409 696 if (ch == '#') {
jjg@1529 697 int namep = bp;
jjg@1409 698 nextChar();
jjg@1409 699 if (isDecimalDigit(ch)) {
jjg@1409 700 nextChar();
jjg@1409 701 while (isDecimalDigit(ch))
jjg@1409 702 nextChar();
jjg@1529 703 name = names.fromChars(buf, namep, bp - namep);
jjg@1409 704 } else if (ch == 'x' || ch == 'X') {
jjg@1409 705 nextChar();
jjg@1409 706 if (isHexDigit(ch)) {
jjg@1409 707 nextChar();
jjg@1409 708 while (isHexDigit(ch))
jjg@1409 709 nextChar();
jjg@1529 710 name = names.fromChars(buf, namep, bp - namep);
jjg@1409 711 }
jjg@1409 712 }
jjg@1409 713 } else if (isIdentifierStart(ch)) {
jjg@1529 714 name = readIdentifier();
jjg@1409 715 }
jjg@1409 716
jjg@1529 717 if (name == null)
jjg@1529 718 return erroneous("dc.bad.entity", p);
jjg@1529 719 else {
jjg@1529 720 if (ch != ';')
jjg@1529 721 return erroneous("dc.missing.semicolon", p);
jjg@1409 722 nextChar();
jjg@1529 723 return m.at(p).Entity(name);
jjg@1409 724 }
jjg@1409 725 }
jjg@1409 726
jjg@1409 727 /**
jjg@1409 728 * Read the start or end of an HTML tag, or an HTML comment
jjg@1409 729 * {@literal <identifier attrs> } or {@literal </identifier> }
jjg@1409 730 */
jjg@1409 731 protected DCTree html() {
jjg@1409 732 int p = bp;
jjg@1409 733 nextChar();
jjg@1409 734 if (isIdentifierStart(ch)) {
jjg@1529 735 Name name = readIdentifier();
jjg@1409 736 List<DCTree> attrs = htmlAttrs();
jjg@1409 737 if (attrs != null) {
jjg@1409 738 boolean selfClosing = false;
jjg@1409 739 if (ch == '/') {
jjg@1409 740 nextChar();
jjg@1409 741 selfClosing = true;
jjg@1409 742 }
jjg@1409 743 if (ch == '>') {
jjg@1409 744 nextChar();
jlahoda@1704 745 return m.at(p).StartElement(name, attrs, selfClosing).setEndPos(bp);
jjg@1409 746 }
jjg@1409 747 }
jjg@1409 748 } else if (ch == '/') {
jjg@1409 749 nextChar();
jjg@1409 750 if (isIdentifierStart(ch)) {
jjg@1529 751 Name name = readIdentifier();
jjg@1409 752 skipWhitespace();
jjg@1409 753 if (ch == '>') {
jjg@1409 754 nextChar();
jjg@1409 755 return m.at(p).EndElement(name);
jjg@1409 756 }
jjg@1409 757 }
jjg@1409 758 } else if (ch == '!') {
jjg@1409 759 nextChar();
jjg@1409 760 if (ch == '-') {
jjg@1409 761 nextChar();
jjg@1409 762 if (ch == '-') {
jjg@1409 763 nextChar();
jjg@1409 764 while (bp < buflen) {
jjg@1409 765 int dash = 0;
jjg@1409 766 while (ch == '-') {
jjg@1409 767 dash++;
jjg@1409 768 nextChar();
jjg@1409 769 }
jjg@1409 770 // strictly speaking, a comment should not contain "--"
jjg@1409 771 // so dash > 2 is an error, dash == 2 implies ch == '>'
jjg@1409 772 if (dash >= 2 && ch == '>') {
jjg@1409 773 nextChar();
jjg@1409 774 return m.at(p).Comment(newString(p, bp));
jjg@1409 775 }
jjg@1409 776
jjg@1409 777 nextChar();
jjg@1409 778 }
jjg@1409 779 }
jjg@1409 780 }
jjg@1409 781 }
jjg@1409 782
jjg@1409 783 bp = p + 1;
jjg@1409 784 ch = buf[bp];
jjg@1409 785 return erroneous("dc.malformed.html", p);
jjg@1409 786 }
jjg@1409 787
jjg@1409 788 /**
jjg@1409 789 * Read a series of HTML attributes, terminated by {@literal > }.
jjg@1409 790 * Each attribute is of the form {@literal identifier[=value] }.
jjg@1409 791 * "value" may be unquoted, single-quoted, or double-quoted.
jjg@1409 792 */
jjg@1409 793 protected List<DCTree> htmlAttrs() {
jjg@1409 794 ListBuffer<DCTree> attrs = new ListBuffer<DCTree>();
jjg@1409 795 skipWhitespace();
jjg@1409 796
jjg@1409 797 loop:
jjg@1409 798 while (isIdentifierStart(ch)) {
jjg@1409 799 int namePos = bp;
jjg@1529 800 Name name = readIdentifier();
jjg@1409 801 skipWhitespace();
jjg@1409 802 List<DCTree> value = null;
jjg@1409 803 ValueKind vkind = ValueKind.EMPTY;
jjg@1409 804 if (ch == '=') {
jjg@1409 805 ListBuffer<DCTree> v = new ListBuffer<DCTree>();
jjg@1409 806 nextChar();
jjg@1409 807 skipWhitespace();
jjg@1409 808 if (ch == '\'' || ch == '"') {
jjg@1409 809 vkind = (ch == '\'') ? ValueKind.SINGLE : ValueKind.DOUBLE;
jjg@1409 810 char quote = ch;
jjg@1409 811 nextChar();
jjg@1409 812 textStart = bp;
jjg@1409 813 while (bp < buflen && ch != quote) {
jjg@1409 814 if (newline && ch == '@') {
jjg@1409 815 attrs.add(erroneous("dc.unterminated.string", namePos));
jjg@1409 816 // No point trying to read more.
jjg@1409 817 // In fact, all attrs get discarded by the caller
jjg@1409 818 // and superseded by a malformed.html node because
jjg@1409 819 // the html tag itself is not terminated correctly.
jjg@1409 820 break loop;
jjg@1409 821 }
jjg@1409 822 attrValueChar(v);
jjg@1409 823 }
jjg@1409 824 addPendingText(v, bp - 1);
jjg@1409 825 nextChar();
jjg@1409 826 } else {
jjg@1409 827 vkind = ValueKind.UNQUOTED;
jjg@1409 828 textStart = bp;
jjg@1409 829 while (bp < buflen && !isUnquotedAttrValueTerminator(ch)) {
jjg@1409 830 attrValueChar(v);
jjg@1409 831 }
jjg@1409 832 addPendingText(v, bp - 1);
jjg@1409 833 }
jjg@1409 834 skipWhitespace();
jjg@1409 835 value = v.toList();
jjg@1409 836 }
jjg@1409 837 DCAttribute attr = m.at(namePos).Attribute(name, vkind, value);
jjg@1409 838 attrs.add(attr);
jjg@1409 839 }
jjg@1409 840
jjg@1409 841 return attrs.toList();
jjg@1409 842 }
jjg@1409 843
jjg@1409 844 protected void attrValueChar(ListBuffer<DCTree> list) {
jjg@1409 845 switch (ch) {
jjg@1409 846 case '&':
jjg@1409 847 entity(list);
jjg@1409 848 break;
jjg@1409 849
jjg@1409 850 case '{':
jjg@1409 851 inlineTag(list);
jjg@1409 852 break;
jjg@1409 853
jjg@1409 854 default:
jjg@1409 855 nextChar();
jjg@1409 856 }
jjg@1409 857 }
jjg@1409 858
jjg@1409 859 protected void addPendingText(ListBuffer<DCTree> list, int textEnd) {
jjg@1455 860 if (textStart != -1) {
jjg@1455 861 if (textStart <= textEnd) {
jjg@1455 862 list.add(m.at(textStart).Text(newString(textStart, textEnd + 1)));
jjg@1455 863 }
jjg@1409 864 textStart = -1;
jjg@1409 865 }
jjg@1409 866 }
jjg@1409 867
jjg@1409 868 protected DCErroneous erroneous(String code, int pos) {
jjg@1409 869 int i = bp - 1;
jjg@1409 870 loop:
jjg@1529 871 while (i > pos) {
jjg@1409 872 switch (buf[i]) {
jjg@1409 873 case '\f': case '\n': case '\r':
jjg@1409 874 newline = true;
jjg@1409 875 break;
jjg@1409 876 case '\t': case ' ':
jjg@1409 877 break;
jjg@1409 878 default:
jjg@1409 879 break loop;
jjg@1409 880 }
jjg@1409 881 i--;
jjg@1409 882 }
jjg@1409 883 textStart = -1;
jjg@1409 884 return m.at(pos).Erroneous(newString(pos, i + 1), diagSource, code);
jjg@1409 885 }
jjg@1409 886
jjg@1409 887 @SuppressWarnings("unchecked")
jjg@1409 888 <T> T getFirst(List<T>... lists) {
jjg@1409 889 for (List<T> list: lists) {
jjg@1409 890 if (list.nonEmpty())
jjg@1409 891 return list.head;
jjg@1409 892 }
jjg@1409 893 return null;
jjg@1409 894 }
jjg@1409 895
jjg@1409 896 protected boolean isIdentifierStart(char ch) {
jjg@1409 897 return Character.isUnicodeIdentifierStart(ch);
jjg@1409 898 }
jjg@1409 899
jjg@1529 900 protected Name readIdentifier() {
jjg@1529 901 int start = bp;
jjg@1529 902 nextChar();
jjg@1529 903 while (bp < buflen && Character.isUnicodeIdentifierPart(ch))
jjg@1529 904 nextChar();
jjg@1529 905 return names.fromChars(buf, start, bp - start);
jjg@1409 906 }
jjg@1409 907
jjg@2204 908 protected Name readTagName() {
jjg@2204 909 int start = bp;
jjg@2204 910 nextChar();
jjg@2204 911 while (bp < buflen && (Character.isUnicodeIdentifierPart(ch) || ch == '.'))
jjg@2204 912 nextChar();
jjg@2204 913 return names.fromChars(buf, start, bp - start);
jjg@2204 914 }
jjg@2204 915
jjg@1409 916 protected boolean isJavaIdentifierStart(char ch) {
jjg@1409 917 return Character.isJavaIdentifierStart(ch);
jjg@1409 918 }
jjg@1409 919
jjg@1529 920 protected Name readJavaIdentifier() {
jjg@1529 921 int start = bp;
jjg@1529 922 nextChar();
jjg@1529 923 while (bp < buflen && Character.isJavaIdentifierPart(ch))
jjg@1529 924 nextChar();
jjg@1529 925 return names.fromChars(buf, start, bp - start);
jjg@1409 926 }
jjg@1409 927
jjg@1409 928 protected boolean isDecimalDigit(char ch) {
jjg@1409 929 return ('0' <= ch && ch <= '9');
jjg@1409 930 }
jjg@1409 931
jjg@1409 932 protected boolean isHexDigit(char ch) {
jjg@1409 933 return ('0' <= ch && ch <= '9')
jjg@1409 934 || ('a' <= ch && ch <= 'f')
jjg@1409 935 || ('A' <= ch && ch <= 'F');
jjg@1409 936 }
jjg@1409 937
jjg@1409 938 protected boolean isUnquotedAttrValueTerminator(char ch) {
jjg@1409 939 switch (ch) {
jjg@1409 940 case '\f': case '\n': case '\r': case '\t':
jjg@1409 941 case ' ':
jjg@1409 942 case '"': case '\'': case '`':
jjg@1409 943 case '=': case '<': case '>':
jjg@1409 944 return true;
jjg@1409 945 default:
jjg@1409 946 return false;
jjg@1409 947 }
jjg@1409 948 }
jjg@1409 949
jjg@1409 950 protected boolean isWhitespace(char ch) {
jjg@1409 951 return Character.isWhitespace(ch);
jjg@1409 952 }
jjg@1409 953
jjg@1409 954 protected void skipWhitespace() {
jjg@1409 955 while (isWhitespace(ch))
jjg@1409 956 nextChar();
jjg@1409 957 }
jjg@1409 958
jjg@1409 959 protected int getSentenceBreak(String s) {
jjg@1409 960 if (sentenceBreaker != null) {
jjg@1409 961 sentenceBreaker.setText(s);
jjg@1409 962 int i = sentenceBreaker.next();
jjg@1409 963 return (i == s.length()) ? -1 : i;
jjg@1409 964 }
jjg@1409 965
jjg@1409 966 // scan for period followed by whitespace
jjg@1409 967 boolean period = false;
jjg@1409 968 for (int i = 0; i < s.length(); i++) {
jjg@1409 969 switch (s.charAt(i)) {
jjg@1409 970 case '.':
jjg@1409 971 period = true;
jjg@1409 972 break;
jjg@1409 973
jjg@1409 974 case ' ':
jjg@1409 975 case '\f':
jjg@1409 976 case '\n':
jjg@1409 977 case '\r':
jjg@1409 978 case '\t':
jjg@1409 979 if (period)
jjg@1409 980 return i;
jjg@1409 981 break;
jjg@1409 982
jjg@1409 983 default:
jjg@1409 984 period = false;
jjg@1409 985 break;
jjg@1409 986 }
jjg@1409 987 }
jjg@1409 988 return -1;
jjg@1409 989 }
jjg@1409 990
jjg@1409 991
jjg@1409 992 Set<String> htmlBlockTags = new HashSet<String>(Arrays.asList(
jjg@1409 993 "h1", "h2", "h3", "h4", "h5", "h6", "p", "pre"));
jjg@1409 994
jjg@1409 995 protected boolean isSentenceBreak(Name n) {
jjg@1409 996 return htmlBlockTags.contains(n.toString().toLowerCase());
jjg@1409 997 }
jjg@1409 998
jjg@1409 999 protected boolean isSentenceBreak(DCTree t) {
jjg@1409 1000 switch (t.getKind()) {
jjg@1409 1001 case START_ELEMENT:
jjg@1409 1002 return isSentenceBreak(((DCStartElement) t).getName());
jjg@1409 1003
jjg@1409 1004 case END_ELEMENT:
jjg@1409 1005 return isSentenceBreak(((DCEndElement) t).getName());
jjg@1409 1006 }
jjg@1409 1007 return false;
jjg@1409 1008 }
jjg@1409 1009
jjg@1409 1010 /**
jjg@1409 1011 * @param start position of first character of string
jjg@1409 1012 * @param end position of character beyond last character to be included
jjg@1409 1013 */
jjg@1409 1014 String newString(int start, int end) {
jjg@1409 1015 return new String(buf, start, end - start);
jjg@1409 1016 }
jjg@1409 1017
jjg@1409 1018 static abstract class TagParser {
jjg@1409 1019 enum Kind { INLINE, BLOCK }
jjg@1409 1020
jjg@1409 1021 Kind kind;
jjg@1409 1022 DCTree.Kind treeKind;
jjg@1409 1023
jjg@1409 1024 TagParser(Kind k, DCTree.Kind tk) {
jjg@1409 1025 kind = k;
jjg@1409 1026 treeKind = tk;
jjg@1409 1027 }
jjg@1409 1028
jjg@1409 1029 Kind getKind() {
jjg@1409 1030 return kind;
jjg@1409 1031 }
jjg@1409 1032
jjg@1409 1033 DCTree.Kind getTreeKind() {
jjg@1409 1034 return treeKind;
jjg@1409 1035 }
jjg@1409 1036
jjg@1409 1037 abstract DCTree parse(int pos) throws ParseException;
jjg@1409 1038 }
jjg@1409 1039
jjg@1409 1040 /**
jjg@1409 1041 * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javadoc.html#javadoctags">Javadoc Tags</a>
jjg@1409 1042 */
jjg@1409 1043 private void initTagParsers() {
jjg@1409 1044 TagParser[] parsers = {
jjg@1409 1045 // @author name-text
jjg@1409 1046 new TagParser(Kind.BLOCK, DCTree.Kind.AUTHOR) {
jjg@1409 1047 public DCTree parse(int pos) {
jjg@1409 1048 List<DCTree> name = blockContent();
jjg@1409 1049 return m.at(pos).Author(name);
jjg@1409 1050 }
jjg@1409 1051 },
jjg@1409 1052
jjg@1409 1053 // {@code text}
jjg@1409 1054 new TagParser(Kind.INLINE, DCTree.Kind.CODE) {
jjg@1409 1055 public DCTree parse(int pos) throws ParseException {
jjg@1409 1056 DCTree text = inlineText();
jjg@1409 1057 nextChar();
jjg@1409 1058 return m.at(pos).Code((DCText) text);
jjg@1409 1059 }
jjg@1409 1060 },
jjg@1409 1061
jjg@1409 1062 // @deprecated deprecated-text
jjg@1409 1063 new TagParser(Kind.BLOCK, DCTree.Kind.DEPRECATED) {
jjg@1409 1064 public DCTree parse(int pos) {
jjg@1409 1065 List<DCTree> reason = blockContent();
jjg@1409 1066 return m.at(pos).Deprecated(reason);
jjg@1409 1067 }
jjg@1409 1068 },
jjg@1409 1069
jjg@1409 1070 // {@docRoot}
jjg@1409 1071 new TagParser(Kind.INLINE, DCTree.Kind.DOC_ROOT) {
jjg@1409 1072 public DCTree parse(int pos) throws ParseException {
jjg@1409 1073 if (ch == '}') {
jjg@1409 1074 nextChar();
jjg@1409 1075 return m.at(pos).DocRoot();
jjg@1409 1076 }
jjg@1409 1077 inlineText(); // skip unexpected content
jjg@1409 1078 nextChar();
jjg@1409 1079 throw new ParseException("dc.unexpected.content");
jjg@1409 1080 }
jjg@1409 1081 },
jjg@1409 1082
jjg@1409 1083 // @exception class-name description
jjg@1409 1084 new TagParser(Kind.BLOCK, DCTree.Kind.EXCEPTION) {
jjg@1409 1085 public DCTree parse(int pos) throws ParseException {
jjg@1409 1086 skipWhitespace();
jjg@1409 1087 DCReference ref = reference(false);
jjg@1409 1088 List<DCTree> description = blockContent();
jjg@1409 1089 return m.at(pos).Exception(ref, description);
jjg@1409 1090 }
jjg@1409 1091 },
jjg@1409 1092
jjg@1409 1093 // {@inheritDoc}
jjg@1409 1094 new TagParser(Kind.INLINE, DCTree.Kind.INHERIT_DOC) {
jjg@1409 1095 public DCTree parse(int pos) throws ParseException {
jjg@1409 1096 if (ch == '}') {
jjg@1409 1097 nextChar();
jjg@1409 1098 return m.at(pos).InheritDoc();
jjg@1409 1099 }
jjg@1409 1100 inlineText(); // skip unexpected content
jjg@1409 1101 nextChar();
jjg@1409 1102 throw new ParseException("dc.unexpected.content");
jjg@1409 1103 }
jjg@1409 1104 },
jjg@1409 1105
jjg@1409 1106 // {@link package.class#member label}
jjg@1409 1107 new TagParser(Kind.INLINE, DCTree.Kind.LINK) {
jjg@1409 1108 public DCTree parse(int pos) throws ParseException {
jjg@1409 1109 DCReference ref = reference(true);
jjg@1409 1110 List<DCTree> label = inlineContent();
jjg@1409 1111 return m.at(pos).Link(ref, label);
jjg@1409 1112 }
jjg@1409 1113 },
jjg@1409 1114
jjg@1409 1115 // {@linkplain package.class#member label}
jjg@1409 1116 new TagParser(Kind.INLINE, DCTree.Kind.LINK_PLAIN) {
jjg@1409 1117 public DCTree parse(int pos) throws ParseException {
jjg@1409 1118 DCReference ref = reference(true);
jjg@1409 1119 List<DCTree> label = inlineContent();
jjg@1409 1120 return m.at(pos).LinkPlain(ref, label);
jjg@1409 1121 }
jjg@1409 1122 },
jjg@1409 1123
jjg@1409 1124 // {@literal text}
jjg@1409 1125 new TagParser(Kind.INLINE, DCTree.Kind.LITERAL) {
jjg@1409 1126 public DCTree parse(int pos) throws ParseException {
jjg@1409 1127 DCTree text = inlineText();
jjg@1409 1128 nextChar();
jjg@1409 1129 return m.at(pos).Literal((DCText) text);
jjg@1409 1130 }
jjg@1409 1131 },
jjg@1409 1132
jjg@1409 1133 // @param parameter-name description
jjg@1409 1134 new TagParser(Kind.BLOCK, DCTree.Kind.PARAM) {
jjg@1409 1135 public DCTree parse(int pos) throws ParseException {
jjg@1409 1136 skipWhitespace();
jjg@1409 1137
jjg@1409 1138 boolean typaram = false;
jjg@1409 1139 if (ch == '<') {
jjg@1409 1140 typaram = true;
jjg@1409 1141 nextChar();
jjg@1409 1142 }
jjg@1409 1143
jjg@1409 1144 DCIdentifier id = identifier();
jjg@1409 1145
jjg@1409 1146 if (typaram) {
jjg@1409 1147 if (ch != '>')
jjg@1409 1148 throw new ParseException("dc.gt.expected");
jjg@1409 1149 nextChar();
jjg@1409 1150 }
jjg@1409 1151
jjg@1409 1152 skipWhitespace();
jjg@1409 1153 List<DCTree> desc = blockContent();
jjg@1409 1154 return m.at(pos).Param(typaram, id, desc);
jjg@1409 1155 }
jjg@1409 1156 },
jjg@1409 1157
jjg@1409 1158 // @return description
jjg@1409 1159 new TagParser(Kind.BLOCK, DCTree.Kind.RETURN) {
jjg@1409 1160 public DCTree parse(int pos) {
jjg@1409 1161 List<DCTree> description = blockContent();
jjg@1409 1162 return m.at(pos).Return(description);
jjg@1409 1163 }
jjg@1409 1164 },
jjg@1409 1165
jjg@1409 1166 // @see reference | quoted-string | HTML
jjg@1409 1167 new TagParser(Kind.BLOCK, DCTree.Kind.SEE) {
jjg@1409 1168 public DCTree parse(int pos) throws ParseException {
jjg@1409 1169 skipWhitespace();
jjg@1409 1170 switch (ch) {
jjg@1409 1171 case '"':
jjg@1409 1172 DCText string = quotedString();
jjg@1409 1173 if (string != null) {
jjg@1409 1174 skipWhitespace();
jjg@1409 1175 if (ch == '@')
jjg@1409 1176 return m.at(pos).See(List.<DCTree>of(string));
jjg@1409 1177 }
jjg@1409 1178 break;
jjg@1409 1179
jjg@1409 1180 case '<':
jjg@1409 1181 List<DCTree> html = blockContent();
jjg@1409 1182 if (html != null)
jjg@1409 1183 return m.at(pos).See(html);
jjg@1409 1184 break;
jjg@1409 1185
jjg@1455 1186 case '@':
jjg@1455 1187 if (newline)
jjg@1455 1188 throw new ParseException("dc.no.content");
jjg@1455 1189 break;
jjg@1455 1190
jjg@1455 1191 case EOI:
jjg@1455 1192 if (bp == buf.length - 1)
jjg@1455 1193 throw new ParseException("dc.no.content");
jjg@1455 1194 break;
jjg@1455 1195
jjg@1409 1196 default:
jjg@1409 1197 if (isJavaIdentifierStart(ch) || ch == '#') {
jjg@1409 1198 DCReference ref = reference(true);
jjg@1409 1199 List<DCTree> description = blockContent();
jjg@1409 1200 return m.at(pos).See(description.prepend(ref));
jjg@1409 1201 }
jjg@1409 1202 }
jjg@1409 1203 throw new ParseException("dc.unexpected.content");
jjg@1409 1204 }
jjg@1409 1205 },
jjg@1409 1206
jjg@1409 1207 // @serialData data-description
jjg@1409 1208 new TagParser(Kind.BLOCK, DCTree.Kind.SERIAL_DATA) {
jjg@1409 1209 public DCTree parse(int pos) {
jjg@1409 1210 List<DCTree> description = blockContent();
jjg@1409 1211 return m.at(pos).SerialData(description);
jjg@1409 1212 }
jjg@1409 1213 },
jjg@1409 1214
jjg@1409 1215 // @serialField field-name field-type description
jjg@1409 1216 new TagParser(Kind.BLOCK, DCTree.Kind.SERIAL_FIELD) {
jjg@1409 1217 public DCTree parse(int pos) throws ParseException {
jjg@1409 1218 skipWhitespace();
jjg@1409 1219 DCIdentifier name = identifier();
jjg@1409 1220 skipWhitespace();
jjg@1409 1221 DCReference type = reference(false);
jjg@1409 1222 List<DCTree> description = null;
jjg@1409 1223 if (isWhitespace(ch)) {
jjg@1409 1224 skipWhitespace();
jjg@1409 1225 description = blockContent();
jjg@1409 1226 }
jjg@1409 1227 return m.at(pos).SerialField(name, type, description);
jjg@1409 1228 }
jjg@1409 1229 },
jjg@1409 1230
jjg@1409 1231 // @serial field-description | include | exclude
jjg@1409 1232 new TagParser(Kind.BLOCK, DCTree.Kind.SERIAL) {
jjg@1409 1233 public DCTree parse(int pos) {
jjg@1409 1234 List<DCTree> description = blockContent();
jjg@1409 1235 return m.at(pos).Serial(description);
jjg@1409 1236 }
jjg@1409 1237 },
jjg@1409 1238
jjg@1409 1239 // @since since-text
jjg@1409 1240 new TagParser(Kind.BLOCK, DCTree.Kind.SINCE) {
jjg@1409 1241 public DCTree parse(int pos) {
jjg@1409 1242 List<DCTree> description = blockContent();
jjg@1409 1243 return m.at(pos).Since(description);
jjg@1409 1244 }
jjg@1409 1245 },
jjg@1409 1246
jjg@1409 1247 // @throws class-name description
jjg@1409 1248 new TagParser(Kind.BLOCK, DCTree.Kind.THROWS) {
jjg@1409 1249 public DCTree parse(int pos) throws ParseException {
jjg@1409 1250 skipWhitespace();
jjg@1409 1251 DCReference ref = reference(false);
jjg@1409 1252 List<DCTree> description = blockContent();
jjg@1409 1253 return m.at(pos).Throws(ref, description);
jjg@1409 1254 }
jjg@1409 1255 },
jjg@1409 1256
jjg@1409 1257 // {@value package.class#field}
jjg@1409 1258 new TagParser(Kind.INLINE, DCTree.Kind.VALUE) {
jjg@1409 1259 public DCTree parse(int pos) throws ParseException {
jjg@1409 1260 DCReference ref = reference(true);
jjg@1409 1261 skipWhitespace();
jjg@1409 1262 if (ch == '}') {
jjg@1409 1263 nextChar();
jjg@1409 1264 return m.at(pos).Value(ref);
jjg@1409 1265 }
jjg@1409 1266 nextChar();
jjg@1409 1267 throw new ParseException("dc.unexpected.content");
jjg@1409 1268 }
jjg@1409 1269 },
jjg@1409 1270
jjg@1409 1271 // @version version-text
jjg@1409 1272 new TagParser(Kind.BLOCK, DCTree.Kind.VERSION) {
jjg@1409 1273 public DCTree parse(int pos) {
jjg@1409 1274 List<DCTree> description = blockContent();
jjg@1409 1275 return m.at(pos).Version(description);
jjg@1409 1276 }
jjg@1409 1277 },
jjg@1409 1278 };
jjg@1409 1279
jjg@1409 1280 tagParsers = new HashMap<Name,TagParser>();
jjg@1409 1281 for (TagParser p: parsers)
jjg@1409 1282 tagParsers.put(names.fromString(p.getTreeKind().tagName), p);
jjg@1409 1283
jjg@1409 1284 }
jjg@1409 1285 }

mercurial