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

Sat, 17 Nov 2012 19:01:03 +0000

author
mcimadamore
date
Sat, 17 Nov 2012 19:01:03 +0000
changeset 1415
01c9d4161882
parent 1409
33abf479f202
child 1455
75ab654b5cd5
permissions
-rw-r--r--

8003280: Add lambda tests
Summary: Turn on lambda expression, method reference and default method support
Reviewed-by: jjg

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