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

Tue, 13 Jan 2015 12:41:16 -0800

author
bpatel
date
Tue, 13 Jan 2015 12:41:16 -0800
changeset 2628
a5eb8f677bd4
parent 2413
fe033d997ddf
child 2702
9ca8d8713094
permissions
-rw-r--r--

8068495: Update the protocol for references of docs.oracle.com to HTTPS in langtools.
Reviewed-by: coffeys

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

mercurial