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

Wed, 23 Jan 2013 13:27:24 -0800

author
jjg
date
Wed, 23 Jan 2013 13:27:24 -0800
changeset 1521
71f35e4b93a5
parent 1455
75ab654b5cd5
child 1529
950d8195a5a4
permissions
-rw-r--r--

8006775: JSR 308: Compiler changes in JDK8
Reviewed-by: jjg
Contributed-by: mernst@cs.washington.edu, wmdietl@cs.washington.edu, mpapi@csail.mit.edu, mahmood@notnoop.com

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 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@1409 282 int namePos = bp;
jjg@1409 283 nextChar();
jjg@1409 284 while (isIdentifierPart(ch))
jjg@1409 285 nextChar();
jjg@1409 286 int nameLen = bp - namePos;
jjg@1409 287
jjg@1409 288 Name name = names.fromChars(buf, namePos, nameLen);
jjg@1409 289 TagParser tp = tagParsers.get(name);
jjg@1409 290 if (tp == null) {
jjg@1409 291 List<DCTree> content = blockContent();
jjg@1409 292 return m.at(p).UnknownBlockTag(name, content);
jjg@1409 293 } else {
jjg@1409 294 switch (tp.getKind()) {
jjg@1409 295 case BLOCK:
jjg@1409 296 return tp.parse(p);
jjg@1409 297 case INLINE:
jjg@1409 298 return erroneous("dc.bad.inline.tag", p);
jjg@1409 299 }
jjg@1409 300 }
jjg@1409 301 }
jjg@1409 302 blockContent();
jjg@1409 303
jjg@1409 304 return erroneous("dc.no.tag.name", p);
jjg@1409 305 } catch (ParseException e) {
jjg@1409 306 blockContent();
jjg@1409 307 return erroneous(e.getMessage(), p);
jjg@1409 308 }
jjg@1409 309 }
jjg@1409 310
jjg@1409 311 protected void inlineTag(ListBuffer<DCTree> list) {
jjg@1409 312 newline = false;
jjg@1409 313 nextChar();
jjg@1409 314 if (ch == '@') {
jjg@1409 315 addPendingText(list, bp - 2);
jjg@1409 316 list.add(inlineTag());
jjg@1409 317 textStart = bp;
jjg@1409 318 lastNonWhite = -1;
jjg@1409 319 } else {
jjg@1409 320 if (textStart == -1)
jjg@1409 321 textStart = bp - 1;
jjg@1409 322 lastNonWhite = bp;
jjg@1409 323 }
jjg@1409 324 }
jjg@1409 325
jjg@1409 326 /**
jjg@1409 327 * Read a single inline tag, including its content.
jjg@1409 328 * Standard tags parse their content appropriately.
jjg@1409 329 * Non-standard tags are represented by {@link UnknownBlockTag}.
jjg@1409 330 * Malformed tags may be returned as {@link Erroneous}.
jjg@1409 331 */
jjg@1409 332 protected DCTree inlineTag() {
jjg@1409 333 int p = bp - 1;
jjg@1409 334 try {
jjg@1409 335 nextChar();
jjg@1409 336 if (isIdentifierStart(ch)) {
jjg@1409 337 int namePos = bp;
jjg@1409 338 nextChar();
jjg@1409 339 while (isIdentifierPart(ch))
jjg@1409 340 nextChar();
jjg@1409 341 int nameLen = bp - namePos;
jjg@1409 342 skipWhitespace();
jjg@1409 343
jjg@1409 344 Name name = names.fromChars(buf, namePos, nameLen);
jjg@1409 345 TagParser tp = tagParsers.get(name);
jjg@1409 346 if (tp == null) {
jjg@1409 347 DCTree text = inlineText();
jjg@1409 348 if (text != null) {
jjg@1409 349 nextChar();
jjg@1409 350 return m.at(p).UnknownInlineTag(name, List.of(text));
jjg@1409 351 }
jjg@1409 352 } else if (tp.getKind() == TagParser.Kind.INLINE) {
jjg@1409 353 DCTree tree = tp.parse(p);
jjg@1409 354 if (tree != null) {
jjg@1409 355 return tree;
jjg@1409 356 }
jjg@1409 357 } else {
jjg@1409 358 inlineText(); // skip content
jjg@1409 359 nextChar();
jjg@1409 360 }
jjg@1409 361 }
jjg@1409 362 return erroneous("dc.no.tag.name", p);
jjg@1409 363 } catch (ParseException e) {
jjg@1409 364 return erroneous(e.getMessage(), p);
jjg@1409 365 }
jjg@1409 366 }
jjg@1409 367
jjg@1409 368 /**
jjg@1409 369 * Read plain text content of an inline tag.
jjg@1409 370 * Matching pairs of { } are skipped; the text is terminated by the first
jjg@1409 371 * unmatched }. It is an error if the beginning of the next tag is detected.
jjg@1409 372 */
jjg@1409 373 protected DCTree inlineText() throws ParseException {
jjg@1409 374 skipWhitespace();
jjg@1409 375 int pos = bp;
jjg@1409 376 int depth = 1;
jjg@1409 377
jjg@1409 378 loop:
jjg@1409 379 while (bp < buflen) {
jjg@1409 380 switch (ch) {
jjg@1409 381 case '\n': case '\r': case '\f':
jjg@1409 382 newline = true;
jjg@1409 383 break;
jjg@1409 384
jjg@1409 385 case ' ': case '\t':
jjg@1409 386 break;
jjg@1409 387
jjg@1409 388 case '{':
jjg@1409 389 newline = false;
jjg@1409 390 lastNonWhite = bp;
jjg@1409 391 depth++;
jjg@1409 392 break;
jjg@1409 393
jjg@1409 394 case '}':
jjg@1409 395 if (--depth == 0) {
jjg@1409 396 return m.at(pos).Text(newString(pos, bp));
jjg@1409 397 }
jjg@1409 398 newline = false;
jjg@1409 399 lastNonWhite = bp;
jjg@1409 400 break;
jjg@1409 401
jjg@1409 402 case '@':
jjg@1409 403 if (newline)
jjg@1409 404 break loop;
jjg@1409 405 newline = false;
jjg@1409 406 lastNonWhite = bp;
jjg@1409 407 break;
jjg@1409 408
jjg@1409 409 default:
jjg@1409 410 newline = false;
jjg@1409 411 lastNonWhite = bp;
jjg@1409 412 break;
jjg@1409 413 }
jjg@1409 414 nextChar();
jjg@1409 415 }
jjg@1409 416 throw new ParseException("dc.unterminated.inline.tag");
jjg@1409 417 }
jjg@1409 418
jjg@1409 419 /**
jjg@1409 420 * Read Java class name, possibly followed by member
jjg@1409 421 * Matching pairs of < > are skipped. The text is terminated by the first
jjg@1409 422 * unmatched }. It is an error if the beginning of the next tag is detected.
jjg@1409 423 */
jjg@1409 424 // TODO: boolean allowMember should be enum FORBID, ALLOW, REQUIRE
jjg@1409 425 // TODO: improve quality of parse to forbid bad constructions.
jjg@1409 426 @SuppressWarnings("fallthrough")
jjg@1409 427 protected DCReference reference(boolean allowMember) throws ParseException {
jjg@1409 428 int pos = bp;
jjg@1409 429 int depth = 0;
jjg@1409 430
jjg@1409 431 // scan to find the end of the signature, by looking for the first
jjg@1409 432 // whitespace not enclosed in () or <>, or the end of the tag
jjg@1409 433 loop:
jjg@1409 434 while (bp < buflen) {
jjg@1409 435 switch (ch) {
jjg@1409 436 case '\n': case '\r': case '\f':
jjg@1409 437 newline = true;
jjg@1409 438 // fallthrough
jjg@1409 439
jjg@1409 440 case ' ': case '\t':
jjg@1409 441 if (depth == 0)
jjg@1409 442 break loop;
jjg@1409 443 break;
jjg@1409 444
jjg@1409 445 case '(':
jjg@1409 446 case '<':
jjg@1409 447 newline = false;
jjg@1409 448 depth++;
jjg@1409 449 break;
jjg@1409 450
jjg@1409 451 case ')':
jjg@1409 452 case '>':
jjg@1409 453 newline = false;
jjg@1409 454 --depth;
jjg@1409 455 break;
jjg@1409 456
jjg@1409 457 case '}':
jjg@1409 458 if (bp == pos)
jjg@1409 459 return null;
jjg@1409 460 newline = false;
jjg@1409 461 break loop;
jjg@1409 462
jjg@1409 463 case '@':
jjg@1409 464 if (newline)
jjg@1409 465 break loop;
jjg@1409 466 // fallthrough
jjg@1409 467
jjg@1409 468 default:
jjg@1409 469 newline = false;
jjg@1409 470
jjg@1409 471 }
jjg@1409 472 nextChar();
jjg@1409 473 }
jjg@1409 474
jjg@1409 475 if (depth != 0)
jjg@1409 476 throw new ParseException("dc.unterminated.signature");
jjg@1409 477
jjg@1409 478 String sig = newString(pos, bp);
jjg@1409 479
jjg@1409 480 // Break sig apart into qualifiedExpr member paramTypes.
jjg@1409 481 JCTree qualExpr;
jjg@1409 482 Name member;
jjg@1409 483 List<JCTree> paramTypes;
jjg@1409 484
jjg@1409 485 Log.DeferredDiagnosticHandler deferredDiagnosticHandler
jjg@1409 486 = new Log.DeferredDiagnosticHandler(fac.log);
jjg@1409 487
jjg@1409 488 try {
jjg@1409 489 int hash = sig.indexOf("#");
jjg@1409 490 int lparen = sig.indexOf("(", hash + 1);
jjg@1409 491 if (hash == -1) {
jjg@1409 492 if (lparen == -1) {
jjg@1409 493 qualExpr = parseType(sig);
jjg@1409 494 member = null;
jjg@1409 495 } else {
jjg@1409 496 qualExpr = null;
jjg@1409 497 member = parseMember(sig.substring(0, lparen));
jjg@1409 498 }
jjg@1409 499 } else {
jjg@1409 500 qualExpr = (hash == 0) ? null : parseType(sig.substring(0, hash));
jjg@1409 501 if (lparen == -1)
jjg@1409 502 member = parseMember(sig.substring(hash + 1));
jjg@1409 503 else
jjg@1409 504 member = parseMember(sig.substring(hash + 1, lparen));
jjg@1409 505 }
jjg@1409 506
jjg@1409 507 if (lparen < 0) {
jjg@1409 508 paramTypes = null;
jjg@1409 509 } else {
jjg@1409 510 int rparen = sig.indexOf(")", lparen);
jjg@1409 511 if (rparen != sig.length() - 1)
jjg@1409 512 throw new ParseException("dc.ref.bad.parens");
jjg@1409 513 paramTypes = parseParams(sig.substring(lparen + 1, rparen));
jjg@1409 514 }
jjg@1409 515
jjg@1409 516 if (!deferredDiagnosticHandler.getDiagnostics().isEmpty())
jjg@1409 517 throw new ParseException("dc.ref.syntax.error");
jjg@1409 518
jjg@1409 519 } finally {
jjg@1409 520 fac.log.popDiagnosticHandler(deferredDiagnosticHandler);
jjg@1409 521 }
jjg@1409 522
jjg@1409 523 return m.at(pos).Reference(sig, qualExpr, member, paramTypes);
jjg@1409 524 }
jjg@1409 525
jjg@1409 526 JCTree parseType(String s) throws ParseException {
jjg@1409 527 JavacParser p = fac.newParser(s, false, false, false);
jjg@1409 528 JCTree tree = p.parseType();
jjg@1409 529 if (p.token().kind != TokenKind.EOF)
jjg@1409 530 throw new ParseException("dc.ref.unexpected.input");
jjg@1409 531 return tree;
jjg@1409 532 }
jjg@1409 533
jjg@1409 534 Name parseMember(String s) throws ParseException {
jjg@1409 535 JavacParser p = fac.newParser(s, false, false, false);
jjg@1409 536 Name name = p.ident();
jjg@1409 537 if (p.token().kind != TokenKind.EOF)
jjg@1409 538 throw new ParseException("dc.ref.unexpected.input");
jjg@1409 539 return name;
jjg@1409 540 }
jjg@1409 541
jjg@1409 542 List<JCTree> parseParams(String s) throws ParseException {
jjg@1409 543 if (s.trim().isEmpty())
jjg@1409 544 return List.nil();
jjg@1409 545
jjg@1409 546 JavacParser p = fac.newParser(s.replace("...", "[]"), false, false, false);
jjg@1409 547 ListBuffer<JCTree> paramTypes = new ListBuffer<JCTree>();
jjg@1409 548 paramTypes.add(p.parseType());
jjg@1409 549
jjg@1409 550 if (p.token().kind == TokenKind.IDENTIFIER)
jjg@1409 551 p.nextToken();
jjg@1409 552
jjg@1409 553 while (p.token().kind == TokenKind.COMMA) {
jjg@1409 554 p.nextToken();
jjg@1409 555 paramTypes.add(p.parseType());
jjg@1409 556
jjg@1409 557 if (p.token().kind == TokenKind.IDENTIFIER)
jjg@1409 558 p.nextToken();
jjg@1409 559 }
jjg@1409 560
jjg@1409 561 if (p.token().kind != TokenKind.EOF)
jjg@1409 562 throw new ParseException("dc.ref.unexpected.input");
jjg@1409 563
jjg@1409 564 return paramTypes.toList();
jjg@1409 565 }
jjg@1409 566
jjg@1409 567 /**
jjg@1409 568 * Read Java identifier
jjg@1409 569 * Matching pairs of { } are skipped; the text is terminated by the first
jjg@1409 570 * unmatched }. It is an error if the beginning of the next tag is detected.
jjg@1409 571 */
jjg@1409 572 @SuppressWarnings("fallthrough")
jjg@1409 573 protected DCIdentifier identifier() throws ParseException {
jjg@1409 574 skipWhitespace();
jjg@1409 575 int pos = bp;
jjg@1409 576
jjg@1409 577 if (isJavaIdentifierStart(ch)) {
jjg@1409 578 nextChar();
jjg@1409 579 while (isJavaIdentifierPart(ch))
jjg@1409 580 nextChar();
jjg@1409 581 return m.at(pos).Identifier(names.fromChars(buf, pos, bp - pos));
jjg@1409 582 }
jjg@1409 583
jjg@1409 584 throw new ParseException("dc.identifier.expected");
jjg@1409 585 }
jjg@1409 586
jjg@1409 587 /**
jjg@1409 588 * Read a quoted string.
jjg@1409 589 * It is an error if the beginning of the next tag is detected.
jjg@1409 590 */
jjg@1409 591 @SuppressWarnings("fallthrough")
jjg@1409 592 protected DCText quotedString() {
jjg@1409 593 int pos = bp;
jjg@1409 594 nextChar();
jjg@1409 595
jjg@1409 596 loop:
jjg@1409 597 while (bp < buflen) {
jjg@1409 598 switch (ch) {
jjg@1409 599 case '\n': case '\r': case '\f':
jjg@1409 600 newline = true;
jjg@1409 601 break;
jjg@1409 602
jjg@1409 603 case ' ': case '\t':
jjg@1409 604 break;
jjg@1409 605
jjg@1409 606 case '"':
jjg@1409 607 nextChar();
jjg@1409 608 // trim trailing white-space?
jjg@1409 609 return m.at(pos).Text(newString(pos, bp));
jjg@1409 610
jjg@1409 611 case '@':
jjg@1409 612 if (newline)
jjg@1409 613 break loop;
jjg@1409 614
jjg@1409 615 }
jjg@1409 616 nextChar();
jjg@1409 617 }
jjg@1409 618 return null;
jjg@1409 619 }
jjg@1409 620
jjg@1409 621 /**
jjg@1409 622 * Read general text content of an inline tag, including HTML entities and elements.
jjg@1409 623 * Matching pairs of { } are skipped; the text is terminated by the first
jjg@1409 624 * unmatched }. It is an error if the beginning of the next tag is detected.
jjg@1409 625 */
jjg@1409 626 @SuppressWarnings("fallthrough")
jjg@1409 627 protected List<DCTree> inlineContent() {
jjg@1409 628 ListBuffer<DCTree> trees = new ListBuffer<DCTree>();
jjg@1409 629
jjg@1409 630 skipWhitespace();
jjg@1409 631 int pos = bp;
jjg@1409 632 int depth = 1;
jjg@1409 633 textStart = -1;
jjg@1409 634
jjg@1409 635 loop:
jjg@1409 636 while (bp < buflen) {
jjg@1409 637
jjg@1409 638 switch (ch) {
jjg@1409 639 case '\n': case '\r': case '\f':
jjg@1409 640 newline = true;
jjg@1409 641 // fall through
jjg@1409 642
jjg@1409 643 case ' ': case '\t':
jjg@1409 644 nextChar();
jjg@1409 645 break;
jjg@1409 646
jjg@1409 647 case '&':
jjg@1409 648 entity(trees);
jjg@1409 649 break;
jjg@1409 650
jjg@1409 651 case '<':
jjg@1409 652 newline = false;
jjg@1409 653 addPendingText(trees, bp - 1);
jjg@1409 654 trees.add(html());
jjg@1409 655 break;
jjg@1409 656
jjg@1409 657 case '{':
jjg@1409 658 newline = false;
jjg@1409 659 depth++;
jjg@1409 660 nextChar();
jjg@1409 661 break;
jjg@1409 662
jjg@1409 663 case '}':
jjg@1409 664 newline = false;
jjg@1409 665 if (--depth == 0) {
jjg@1409 666 addPendingText(trees, bp - 1);
jjg@1409 667 nextChar();
jjg@1409 668 return trees.toList();
jjg@1409 669 }
jjg@1409 670 nextChar();
jjg@1409 671 break;
jjg@1409 672
jjg@1409 673 case '@':
jjg@1409 674 if (newline)
jjg@1409 675 break loop;
jjg@1409 676 // fallthrough
jjg@1409 677
jjg@1409 678 default:
jjg@1409 679 if (textStart == -1)
jjg@1409 680 textStart = bp;
jjg@1409 681 nextChar();
jjg@1409 682 break;
jjg@1409 683 }
jjg@1409 684 }
jjg@1409 685
jjg@1409 686 return List.<DCTree>of(erroneous("dc.unterminated.inline.tag", pos));
jjg@1409 687 }
jjg@1409 688
jjg@1409 689 protected void entity(ListBuffer<DCTree> list) {
jjg@1409 690 newline = false;
jjg@1409 691 addPendingText(list, bp - 1);
jjg@1409 692 list.add(entity());
jjg@1409 693 if (textStart == -1) {
jjg@1409 694 textStart = bp;
jjg@1409 695 lastNonWhite = -1;
jjg@1409 696 }
jjg@1409 697 }
jjg@1409 698
jjg@1409 699 /**
jjg@1409 700 * Read an HTML entity.
jjg@1409 701 * {@literal &identifier; } or {@literal &#digits; } or {@literal &#xhex-digits; }
jjg@1409 702 */
jjg@1409 703 protected DCTree entity() {
jjg@1409 704 int p = bp;
jjg@1409 705 nextChar();
jjg@1409 706 int namep = bp;
jjg@1409 707 boolean checkSemi = false;
jjg@1409 708 if (ch == '#') {
jjg@1409 709 nextChar();
jjg@1409 710 if (isDecimalDigit(ch)) {
jjg@1409 711 nextChar();
jjg@1409 712 while (isDecimalDigit(ch))
jjg@1409 713 nextChar();
jjg@1409 714 checkSemi = true;
jjg@1409 715 } else if (ch == 'x' || ch == 'X') {
jjg@1409 716 nextChar();
jjg@1409 717 if (isHexDigit(ch)) {
jjg@1409 718 nextChar();
jjg@1409 719 while (isHexDigit(ch))
jjg@1409 720 nextChar();
jjg@1409 721 checkSemi = true;
jjg@1409 722 }
jjg@1409 723 }
jjg@1409 724 } else if (isIdentifierStart(ch)) {
jjg@1409 725 nextChar();
jjg@1409 726 while (isIdentifierPart(ch))
jjg@1409 727 nextChar();
jjg@1409 728 checkSemi = true;
jjg@1409 729 }
jjg@1409 730
jjg@1409 731 if (checkSemi && ch == ';') {
jjg@1409 732 nextChar();
jjg@1409 733 return m.at(p).Entity(names.fromChars(buf, namep, bp - namep - 1));
jjg@1409 734 } else {
jjg@1455 735 String code = checkSemi
jjg@1455 736 ? "dc.missing.semicolon"
jjg@1455 737 : "dc.bad.entity";
jjg@1409 738 return erroneous(code, p);
jjg@1409 739 }
jjg@1409 740 }
jjg@1409 741
jjg@1409 742 /**
jjg@1409 743 * Read the start or end of an HTML tag, or an HTML comment
jjg@1409 744 * {@literal <identifier attrs> } or {@literal </identifier> }
jjg@1409 745 */
jjg@1409 746 protected DCTree html() {
jjg@1409 747 int p = bp;
jjg@1409 748 nextChar();
jjg@1409 749 if (isIdentifierStart(ch)) {
jjg@1409 750 int namePos = bp;
jjg@1409 751 nextChar();
jjg@1409 752 while (isIdentifierPart(ch))
jjg@1409 753 nextChar();
jjg@1409 754 int nameLen = bp - namePos;
jjg@1409 755 List<DCTree> attrs = htmlAttrs();
jjg@1409 756 if (attrs != null) {
jjg@1409 757 boolean selfClosing = false;
jjg@1409 758 if (ch == '/') {
jjg@1409 759 nextChar();
jjg@1409 760 selfClosing = true;
jjg@1409 761 }
jjg@1409 762 if (ch == '>') {
jjg@1409 763 nextChar();
jjg@1409 764 Name name = names.fromChars(buf, namePos, nameLen);
jjg@1409 765 return m.at(p).StartElement(name, attrs, selfClosing);
jjg@1409 766 }
jjg@1409 767 }
jjg@1409 768 } else if (ch == '/') {
jjg@1409 769 nextChar();
jjg@1409 770 if (isIdentifierStart(ch)) {
jjg@1409 771 int namePos = bp;
jjg@1409 772 nextChar();
jjg@1409 773 while (isIdentifierPart(ch))
jjg@1409 774 nextChar();
jjg@1409 775 int nameLen = bp - namePos;
jjg@1409 776 skipWhitespace();
jjg@1409 777 if (ch == '>') {
jjg@1409 778 nextChar();
jjg@1409 779 Name name = names.fromChars(buf, namePos, nameLen);
jjg@1409 780 return m.at(p).EndElement(name);
jjg@1409 781 }
jjg@1409 782 }
jjg@1409 783 } else if (ch == '!') {
jjg@1409 784 nextChar();
jjg@1409 785 if (ch == '-') {
jjg@1409 786 nextChar();
jjg@1409 787 if (ch == '-') {
jjg@1409 788 nextChar();
jjg@1409 789 while (bp < buflen) {
jjg@1409 790 int dash = 0;
jjg@1409 791 while (ch == '-') {
jjg@1409 792 dash++;
jjg@1409 793 nextChar();
jjg@1409 794 }
jjg@1409 795 // strictly speaking, a comment should not contain "--"
jjg@1409 796 // so dash > 2 is an error, dash == 2 implies ch == '>'
jjg@1409 797 if (dash >= 2 && ch == '>') {
jjg@1409 798 nextChar();
jjg@1409 799 return m.at(p).Comment(newString(p, bp));
jjg@1409 800 }
jjg@1409 801
jjg@1409 802 nextChar();
jjg@1409 803 }
jjg@1409 804 }
jjg@1409 805 }
jjg@1409 806 }
jjg@1409 807
jjg@1409 808 bp = p + 1;
jjg@1409 809 ch = buf[bp];
jjg@1409 810 return erroneous("dc.malformed.html", p);
jjg@1409 811 }
jjg@1409 812
jjg@1409 813 /**
jjg@1409 814 * Read a series of HTML attributes, terminated by {@literal > }.
jjg@1409 815 * Each attribute is of the form {@literal identifier[=value] }.
jjg@1409 816 * "value" may be unquoted, single-quoted, or double-quoted.
jjg@1409 817 */
jjg@1409 818 protected List<DCTree> htmlAttrs() {
jjg@1409 819 ListBuffer<DCTree> attrs = new ListBuffer<DCTree>();
jjg@1409 820 skipWhitespace();
jjg@1409 821
jjg@1409 822 loop:
jjg@1409 823 while (isIdentifierStart(ch)) {
jjg@1409 824 int namePos = bp;
jjg@1409 825 nextChar();
jjg@1409 826 while (isIdentifierPart(ch))
jjg@1409 827 nextChar();
jjg@1409 828 int nameLen = bp - namePos;
jjg@1409 829 skipWhitespace();
jjg@1409 830 List<DCTree> value = null;
jjg@1409 831 ValueKind vkind = ValueKind.EMPTY;
jjg@1409 832 if (ch == '=') {
jjg@1409 833 ListBuffer<DCTree> v = new ListBuffer<DCTree>();
jjg@1409 834 nextChar();
jjg@1409 835 skipWhitespace();
jjg@1409 836 if (ch == '\'' || ch == '"') {
jjg@1409 837 vkind = (ch == '\'') ? ValueKind.SINGLE : ValueKind.DOUBLE;
jjg@1409 838 char quote = ch;
jjg@1409 839 nextChar();
jjg@1409 840 textStart = bp;
jjg@1409 841 while (bp < buflen && ch != quote) {
jjg@1409 842 if (newline && ch == '@') {
jjg@1409 843 attrs.add(erroneous("dc.unterminated.string", namePos));
jjg@1409 844 // No point trying to read more.
jjg@1409 845 // In fact, all attrs get discarded by the caller
jjg@1409 846 // and superseded by a malformed.html node because
jjg@1409 847 // the html tag itself is not terminated correctly.
jjg@1409 848 break loop;
jjg@1409 849 }
jjg@1409 850 attrValueChar(v);
jjg@1409 851 }
jjg@1409 852 addPendingText(v, bp - 1);
jjg@1409 853 nextChar();
jjg@1409 854 } else {
jjg@1409 855 vkind = ValueKind.UNQUOTED;
jjg@1409 856 textStart = bp;
jjg@1409 857 while (bp < buflen && !isUnquotedAttrValueTerminator(ch)) {
jjg@1409 858 attrValueChar(v);
jjg@1409 859 }
jjg@1409 860 addPendingText(v, bp - 1);
jjg@1409 861 }
jjg@1409 862 skipWhitespace();
jjg@1409 863 value = v.toList();
jjg@1409 864 }
jjg@1409 865 Name name = names.fromChars(buf, namePos, nameLen);
jjg@1409 866 DCAttribute attr = m.at(namePos).Attribute(name, vkind, value);
jjg@1409 867 attrs.add(attr);
jjg@1409 868 }
jjg@1409 869
jjg@1409 870 return attrs.toList();
jjg@1409 871 }
jjg@1409 872
jjg@1409 873 protected void attrValueChar(ListBuffer<DCTree> list) {
jjg@1409 874 switch (ch) {
jjg@1409 875 case '&':
jjg@1409 876 entity(list);
jjg@1409 877 break;
jjg@1409 878
jjg@1409 879 case '{':
jjg@1409 880 inlineTag(list);
jjg@1409 881 break;
jjg@1409 882
jjg@1409 883 default:
jjg@1409 884 nextChar();
jjg@1409 885 }
jjg@1409 886 }
jjg@1409 887
jjg@1409 888 protected void addPendingText(ListBuffer<DCTree> list, int textEnd) {
jjg@1455 889 if (textStart != -1) {
jjg@1455 890 if (textStart <= textEnd) {
jjg@1455 891 list.add(m.at(textStart).Text(newString(textStart, textEnd + 1)));
jjg@1455 892 }
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@1455 1199 case '@':
jjg@1455 1200 if (newline)
jjg@1455 1201 throw new ParseException("dc.no.content");
jjg@1455 1202 break;
jjg@1455 1203
jjg@1455 1204 case EOI:
jjg@1455 1205 if (bp == buf.length - 1)
jjg@1455 1206 throw new ParseException("dc.no.content");
jjg@1455 1207 break;
jjg@1455 1208
jjg@1409 1209 default:
jjg@1409 1210 if (isJavaIdentifierStart(ch) || ch == '#') {
jjg@1409 1211 DCReference ref = reference(true);
jjg@1409 1212 List<DCTree> description = blockContent();
jjg@1409 1213 return m.at(pos).See(description.prepend(ref));
jjg@1409 1214 }
jjg@1409 1215 }
jjg@1409 1216 throw new ParseException("dc.unexpected.content");
jjg@1409 1217 }
jjg@1409 1218 },
jjg@1409 1219
jjg@1409 1220 // @serialData data-description
jjg@1409 1221 new TagParser(Kind.BLOCK, DCTree.Kind.SERIAL_DATA) {
jjg@1409 1222 public DCTree parse(int pos) {
jjg@1409 1223 List<DCTree> description = blockContent();
jjg@1409 1224 return m.at(pos).SerialData(description);
jjg@1409 1225 }
jjg@1409 1226 },
jjg@1409 1227
jjg@1409 1228 // @serialField field-name field-type description
jjg@1409 1229 new TagParser(Kind.BLOCK, DCTree.Kind.SERIAL_FIELD) {
jjg@1409 1230 public DCTree parse(int pos) throws ParseException {
jjg@1409 1231 skipWhitespace();
jjg@1409 1232 DCIdentifier name = identifier();
jjg@1409 1233 skipWhitespace();
jjg@1409 1234 DCReference type = reference(false);
jjg@1409 1235 List<DCTree> description = null;
jjg@1409 1236 if (isWhitespace(ch)) {
jjg@1409 1237 skipWhitespace();
jjg@1409 1238 description = blockContent();
jjg@1409 1239 }
jjg@1409 1240 return m.at(pos).SerialField(name, type, description);
jjg@1409 1241 }
jjg@1409 1242 },
jjg@1409 1243
jjg@1409 1244 // @serial field-description | include | exclude
jjg@1409 1245 new TagParser(Kind.BLOCK, DCTree.Kind.SERIAL) {
jjg@1409 1246 public DCTree parse(int pos) {
jjg@1409 1247 List<DCTree> description = blockContent();
jjg@1409 1248 return m.at(pos).Serial(description);
jjg@1409 1249 }
jjg@1409 1250 },
jjg@1409 1251
jjg@1409 1252 // @since since-text
jjg@1409 1253 new TagParser(Kind.BLOCK, DCTree.Kind.SINCE) {
jjg@1409 1254 public DCTree parse(int pos) {
jjg@1409 1255 List<DCTree> description = blockContent();
jjg@1409 1256 return m.at(pos).Since(description);
jjg@1409 1257 }
jjg@1409 1258 },
jjg@1409 1259
jjg@1409 1260 // @throws class-name description
jjg@1409 1261 new TagParser(Kind.BLOCK, DCTree.Kind.THROWS) {
jjg@1409 1262 public DCTree parse(int pos) throws ParseException {
jjg@1409 1263 skipWhitespace();
jjg@1409 1264 DCReference ref = reference(false);
jjg@1409 1265 List<DCTree> description = blockContent();
jjg@1409 1266 return m.at(pos).Throws(ref, description);
jjg@1409 1267 }
jjg@1409 1268 },
jjg@1409 1269
jjg@1409 1270 // {@value package.class#field}
jjg@1409 1271 new TagParser(Kind.INLINE, DCTree.Kind.VALUE) {
jjg@1409 1272 public DCTree parse(int pos) throws ParseException {
jjg@1409 1273 DCReference ref = reference(true);
jjg@1409 1274 skipWhitespace();
jjg@1409 1275 if (ch == '}') {
jjg@1409 1276 nextChar();
jjg@1409 1277 return m.at(pos).Value(ref);
jjg@1409 1278 }
jjg@1409 1279 nextChar();
jjg@1409 1280 throw new ParseException("dc.unexpected.content");
jjg@1409 1281 }
jjg@1409 1282 },
jjg@1409 1283
jjg@1409 1284 // @version version-text
jjg@1409 1285 new TagParser(Kind.BLOCK, DCTree.Kind.VERSION) {
jjg@1409 1286 public DCTree parse(int pos) {
jjg@1409 1287 List<DCTree> description = blockContent();
jjg@1409 1288 return m.at(pos).Version(description);
jjg@1409 1289 }
jjg@1409 1290 },
jjg@1409 1291 };
jjg@1409 1292
jjg@1409 1293 tagParsers = new HashMap<Name,TagParser>();
jjg@1409 1294 for (TagParser p: parsers)
jjg@1409 1295 tagParsers.put(names.fromString(p.getTreeKind().tagName), p);
jjg@1409 1296
jjg@1409 1297 }
jjg@1409 1298 }

mercurial