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

Thu, 12 Oct 2017 19:50:01 +0800

author
aoqi
date
Thu, 12 Oct 2017 19:50:01 +0800
changeset 2702
9ca8d8713094
parent 2628
a5eb8f677bd4
parent 2525
2eb010b6cb22
permissions
-rw-r--r--

merge

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

mercurial