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

Sun, 17 Feb 2013 16:44:55 -0500

author
dholmes
date
Sun, 17 Feb 2013 16:44:55 -0500
changeset 1571
af8417e590f4
parent 1529
950d8195a5a4
child 1704
ed918a442b83
permissions
-rw-r--r--

Merge

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

mercurial