src/share/classes/com/sun/tools/javadoc/Comment.java

Thu, 31 Aug 2017 15:17:03 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:17:03 +0800
changeset 2525
2eb010b6cb22
parent 1722
38c4bade0ec1
parent 0
959103a6100f
permissions
-rw-r--r--

merge

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation. Oracle designates this
aoqi@0 8 * particular file as subject to the "Classpath" exception as provided
aoqi@0 9 * by Oracle in the LICENSE file that accompanied this code.
aoqi@0 10 *
aoqi@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 14 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 15 * accompanied this code).
aoqi@0 16 *
aoqi@0 17 * You should have received a copy of the GNU General Public License version
aoqi@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 20 *
aoqi@0 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 22 * or visit www.oracle.com if you need additional information or have any
aoqi@0 23 * questions.
aoqi@0 24 */
aoqi@0 25
aoqi@0 26 package com.sun.tools.javadoc;
aoqi@0 27
aoqi@0 28 import java.util.regex.Matcher;
aoqi@0 29 import java.util.regex.Pattern;
aoqi@0 30 import com.sun.javadoc.*;
aoqi@0 31 import com.sun.tools.javac.util.ListBuffer;
aoqi@0 32
aoqi@0 33 /**
aoqi@0 34 * Comment contains all information in comment part.
aoqi@0 35 * It allows users to get first sentence of this comment, get
aoqi@0 36 * comment for different tags...
aoqi@0 37 *
aoqi@0 38 * <p><b>This is NOT part of any supported API.
aoqi@0 39 * If you write code that depends on this, you do so at your own risk.
aoqi@0 40 * This code and its internal interfaces are subject to change or
aoqi@0 41 * deletion without notice.</b>
aoqi@0 42 *
aoqi@0 43 * @author Kaiyang Liu (original)
aoqi@0 44 * @author Robert Field (rewrite)
aoqi@0 45 * @author Atul M Dambalkar
aoqi@0 46 * @author Neal Gafter (rewrite)
aoqi@0 47 */
aoqi@0 48 class Comment {
aoqi@0 49
aoqi@0 50 /**
aoqi@0 51 * sorted comments with different tags.
aoqi@0 52 */
aoqi@0 53 private final ListBuffer<Tag> tagList = new ListBuffer<Tag>();
aoqi@0 54
aoqi@0 55 /**
aoqi@0 56 * text minus any tags.
aoqi@0 57 */
aoqi@0 58 private String text;
aoqi@0 59
aoqi@0 60 /**
aoqi@0 61 * Doc environment
aoqi@0 62 */
aoqi@0 63 private final DocEnv docenv;
aoqi@0 64
aoqi@0 65 /**
aoqi@0 66 * constructor of Comment.
aoqi@0 67 */
aoqi@0 68 Comment(final DocImpl holder, final String commentString) {
aoqi@0 69 this.docenv = holder.env;
aoqi@0 70
aoqi@0 71 /**
aoqi@0 72 * Separate the comment into the text part and zero to N tags.
aoqi@0 73 * Simple state machine is in one of three states:
aoqi@0 74 * <pre>
aoqi@0 75 * IN_TEXT: parsing the comment text or tag text.
aoqi@0 76 * TAG_NAME: parsing the name of a tag.
aoqi@0 77 * TAG_GAP: skipping through the gap between the tag name and
aoqi@0 78 * the tag text.
aoqi@0 79 * </pre>
aoqi@0 80 */
aoqi@0 81 @SuppressWarnings("fallthrough")
aoqi@0 82 class CommentStringParser {
aoqi@0 83 /**
aoqi@0 84 * The entry point to the comment string parser
aoqi@0 85 */
aoqi@0 86 void parseCommentStateMachine() {
aoqi@0 87 final int IN_TEXT = 1;
aoqi@0 88 final int TAG_GAP = 2;
aoqi@0 89 final int TAG_NAME = 3;
aoqi@0 90 int state = TAG_GAP;
aoqi@0 91 boolean newLine = true;
aoqi@0 92 String tagName = null;
aoqi@0 93 int tagStart = 0;
aoqi@0 94 int textStart = 0;
aoqi@0 95 int lastNonWhite = -1;
aoqi@0 96 int len = commentString.length();
aoqi@0 97 for (int inx = 0; inx < len; ++inx) {
aoqi@0 98 char ch = commentString.charAt(inx);
aoqi@0 99 boolean isWhite = Character.isWhitespace(ch);
aoqi@0 100 switch (state) {
aoqi@0 101 case TAG_NAME:
aoqi@0 102 if (isWhite) {
aoqi@0 103 tagName = commentString.substring(tagStart, inx);
aoqi@0 104 state = TAG_GAP;
aoqi@0 105 }
aoqi@0 106 break;
aoqi@0 107 case TAG_GAP:
aoqi@0 108 if (isWhite) {
aoqi@0 109 break;
aoqi@0 110 }
aoqi@0 111 textStart = inx;
aoqi@0 112 state = IN_TEXT;
aoqi@0 113 /* fall thru */
aoqi@0 114 case IN_TEXT:
aoqi@0 115 if (newLine && ch == '@') {
aoqi@0 116 parseCommentComponent(tagName, textStart,
aoqi@0 117 lastNonWhite+1);
aoqi@0 118 tagStart = inx;
aoqi@0 119 state = TAG_NAME;
aoqi@0 120 }
aoqi@0 121 break;
aoqi@0 122 }
aoqi@0 123 if (ch == '\n') {
aoqi@0 124 newLine = true;
aoqi@0 125 } else if (!isWhite) {
aoqi@0 126 lastNonWhite = inx;
aoqi@0 127 newLine = false;
aoqi@0 128 }
aoqi@0 129 }
aoqi@0 130 // Finish what's currently being processed
aoqi@0 131 switch (state) {
aoqi@0 132 case TAG_NAME:
aoqi@0 133 tagName = commentString.substring(tagStart, len);
aoqi@0 134 /* fall thru */
aoqi@0 135 case TAG_GAP:
aoqi@0 136 textStart = len;
aoqi@0 137 /* fall thru */
aoqi@0 138 case IN_TEXT:
aoqi@0 139 parseCommentComponent(tagName, textStart, lastNonWhite+1);
aoqi@0 140 break;
aoqi@0 141 }
aoqi@0 142 }
aoqi@0 143
aoqi@0 144 /**
aoqi@0 145 * Save away the last parsed item.
aoqi@0 146 */
aoqi@0 147 void parseCommentComponent(String tagName,
aoqi@0 148 int from, int upto) {
aoqi@0 149 String tx = upto <= from ? "" : commentString.substring(from, upto);
aoqi@0 150 if (tagName == null) {
aoqi@0 151 text = tx;
aoqi@0 152 } else {
aoqi@0 153 TagImpl tag;
aoqi@0 154 if (tagName.equals("@exception") || tagName.equals("@throws")) {
aoqi@0 155 warnIfEmpty(tagName, tx);
aoqi@0 156 tag = new ThrowsTagImpl(holder, tagName, tx);
aoqi@0 157 } else if (tagName.equals("@param")) {
aoqi@0 158 warnIfEmpty(tagName, tx);
aoqi@0 159 tag = new ParamTagImpl(holder, tagName, tx);
aoqi@0 160 } else if (tagName.equals("@see")) {
aoqi@0 161 warnIfEmpty(tagName, tx);
aoqi@0 162 tag = new SeeTagImpl(holder, tagName, tx);
aoqi@0 163 } else if (tagName.equals("@serialField")) {
aoqi@0 164 warnIfEmpty(tagName, tx);
aoqi@0 165 tag = new SerialFieldTagImpl(holder, tagName, tx);
aoqi@0 166 } else if (tagName.equals("@return")) {
aoqi@0 167 warnIfEmpty(tagName, tx);
aoqi@0 168 tag = new TagImpl(holder, tagName, tx);
aoqi@0 169 } else if (tagName.equals("@author")) {
aoqi@0 170 warnIfEmpty(tagName, tx);
aoqi@0 171 tag = new TagImpl(holder, tagName, tx);
aoqi@0 172 } else if (tagName.equals("@version")) {
aoqi@0 173 warnIfEmpty(tagName, tx);
aoqi@0 174 tag = new TagImpl(holder, tagName, tx);
aoqi@0 175 } else {
aoqi@0 176 tag = new TagImpl(holder, tagName, tx);
aoqi@0 177 }
aoqi@0 178 tagList.append(tag);
aoqi@0 179 }
aoqi@0 180 }
aoqi@0 181
aoqi@0 182 void warnIfEmpty(String tagName, String tx) {
aoqi@0 183 if (tx.length() == 0) {
aoqi@0 184 docenv.warning(holder, "tag.tag_has_no_arguments", tagName);
aoqi@0 185 }
aoqi@0 186 }
aoqi@0 187
aoqi@0 188 }
aoqi@0 189
aoqi@0 190 new CommentStringParser().parseCommentStateMachine();
aoqi@0 191 }
aoqi@0 192
aoqi@0 193 /**
aoqi@0 194 * Return the text of the comment.
aoqi@0 195 */
aoqi@0 196 String commentText() {
aoqi@0 197 return text;
aoqi@0 198 }
aoqi@0 199
aoqi@0 200 /**
aoqi@0 201 * Return all tags in this comment.
aoqi@0 202 */
aoqi@0 203 Tag[] tags() {
aoqi@0 204 return tagList.toArray(new Tag[tagList.length()]);
aoqi@0 205 }
aoqi@0 206
aoqi@0 207 /**
aoqi@0 208 * Return tags of the specified kind in this comment.
aoqi@0 209 */
aoqi@0 210 Tag[] tags(String tagname) {
aoqi@0 211 ListBuffer<Tag> found = new ListBuffer<Tag>();
aoqi@0 212 String target = tagname;
aoqi@0 213 if (target.charAt(0) != '@') {
aoqi@0 214 target = "@" + target;
aoqi@0 215 }
aoqi@0 216 for (Tag tag : tagList) {
aoqi@0 217 if (tag.kind().equals(target)) {
aoqi@0 218 found.append(tag);
aoqi@0 219 }
aoqi@0 220 }
aoqi@0 221 return found.toArray(new Tag[found.length()]);
aoqi@0 222 }
aoqi@0 223
aoqi@0 224 /**
aoqi@0 225 * Return throws tags in this comment.
aoqi@0 226 */
aoqi@0 227 ThrowsTag[] throwsTags() {
aoqi@0 228 ListBuffer<ThrowsTag> found = new ListBuffer<ThrowsTag>();
aoqi@0 229 for (Tag next : tagList) {
aoqi@0 230 if (next instanceof ThrowsTag) {
aoqi@0 231 found.append((ThrowsTag)next);
aoqi@0 232 }
aoqi@0 233 }
aoqi@0 234 return found.toArray(new ThrowsTag[found.length()]);
aoqi@0 235 }
aoqi@0 236
aoqi@0 237 /**
aoqi@0 238 * Return param tags (excluding type param tags) in this comment.
aoqi@0 239 */
aoqi@0 240 ParamTag[] paramTags() {
aoqi@0 241 return paramTags(false);
aoqi@0 242 }
aoqi@0 243
aoqi@0 244 /**
aoqi@0 245 * Return type param tags in this comment.
aoqi@0 246 */
aoqi@0 247 ParamTag[] typeParamTags() {
aoqi@0 248 return paramTags(true);
aoqi@0 249 }
aoqi@0 250
aoqi@0 251 /**
aoqi@0 252 * Return param tags in this comment. If typeParams is true
aoqi@0 253 * include only type param tags, otherwise include only ordinary
aoqi@0 254 * param tags.
aoqi@0 255 */
aoqi@0 256 private ParamTag[] paramTags(boolean typeParams) {
aoqi@0 257 ListBuffer<ParamTag> found = new ListBuffer<ParamTag>();
aoqi@0 258 for (Tag next : tagList) {
aoqi@0 259 if (next instanceof ParamTag) {
aoqi@0 260 ParamTag p = (ParamTag)next;
aoqi@0 261 if (typeParams == p.isTypeParameter()) {
aoqi@0 262 found.append(p);
aoqi@0 263 }
aoqi@0 264 }
aoqi@0 265 }
aoqi@0 266 return found.toArray(new ParamTag[found.length()]);
aoqi@0 267 }
aoqi@0 268
aoqi@0 269 /**
aoqi@0 270 * Return see also tags in this comment.
aoqi@0 271 */
aoqi@0 272 SeeTag[] seeTags() {
aoqi@0 273 ListBuffer<SeeTag> found = new ListBuffer<SeeTag>();
aoqi@0 274 for (Tag next : tagList) {
aoqi@0 275 if (next instanceof SeeTag) {
aoqi@0 276 found.append((SeeTag)next);
aoqi@0 277 }
aoqi@0 278 }
aoqi@0 279 return found.toArray(new SeeTag[found.length()]);
aoqi@0 280 }
aoqi@0 281
aoqi@0 282 /**
aoqi@0 283 * Return serialField tags in this comment.
aoqi@0 284 */
aoqi@0 285 SerialFieldTag[] serialFieldTags() {
aoqi@0 286 ListBuffer<SerialFieldTag> found = new ListBuffer<SerialFieldTag>();
aoqi@0 287 for (Tag next : tagList) {
aoqi@0 288 if (next instanceof SerialFieldTag) {
aoqi@0 289 found.append((SerialFieldTag)next);
aoqi@0 290 }
aoqi@0 291 }
aoqi@0 292 return found.toArray(new SerialFieldTag[found.length()]);
aoqi@0 293 }
aoqi@0 294
aoqi@0 295 /**
aoqi@0 296 * Return array of tags with text and inline See Tags for a Doc comment.
aoqi@0 297 */
aoqi@0 298 static Tag[] getInlineTags(DocImpl holder, String inlinetext) {
aoqi@0 299 ListBuffer<Tag> taglist = new ListBuffer<Tag>();
aoqi@0 300 int delimend = 0, textstart = 0, len = inlinetext.length();
aoqi@0 301 boolean inPre = false;
aoqi@0 302 DocEnv docenv = holder.env;
aoqi@0 303
aoqi@0 304 if (len == 0) {
aoqi@0 305 return taglist.toArray(new Tag[taglist.length()]);
aoqi@0 306 }
aoqi@0 307 while (true) {
aoqi@0 308 int linkstart;
aoqi@0 309 if ((linkstart = inlineTagFound(holder, inlinetext,
aoqi@0 310 textstart)) == -1) {
aoqi@0 311 taglist.append(new TagImpl(holder, "Text",
aoqi@0 312 inlinetext.substring(textstart)));
aoqi@0 313 break;
aoqi@0 314 } else {
aoqi@0 315 inPre = scanForPre(inlinetext, textstart, linkstart, inPre);
aoqi@0 316 int seetextstart = linkstart;
aoqi@0 317 for (int i = linkstart; i < inlinetext.length(); i++) {
aoqi@0 318 char c = inlinetext.charAt(i);
aoqi@0 319 if (Character.isWhitespace(c) ||
aoqi@0 320 c == '}') {
aoqi@0 321 seetextstart = i;
aoqi@0 322 break;
aoqi@0 323 }
aoqi@0 324 }
aoqi@0 325 String linkName = inlinetext.substring(linkstart+2, seetextstart);
aoqi@0 326 if (!(inPre && (linkName.equals("code") || linkName.equals("literal")))) {
aoqi@0 327 //Move past the white space after the inline tag name.
aoqi@0 328 while (Character.isWhitespace(inlinetext.
aoqi@0 329 charAt(seetextstart))) {
aoqi@0 330 if (inlinetext.length() <= seetextstart) {
aoqi@0 331 taglist.append(new TagImpl(holder, "Text",
aoqi@0 332 inlinetext.substring(textstart, seetextstart)));
aoqi@0 333 docenv.warning(holder,
aoqi@0 334 "tag.Improper_Use_Of_Link_Tag",
aoqi@0 335 inlinetext);
aoqi@0 336 return taglist.toArray(new Tag[taglist.length()]);
aoqi@0 337 } else {
aoqi@0 338 seetextstart++;
aoqi@0 339 }
aoqi@0 340 }
aoqi@0 341 }
aoqi@0 342 taglist.append(new TagImpl(holder, "Text",
aoqi@0 343 inlinetext.substring(textstart, linkstart)));
aoqi@0 344 textstart = seetextstart; // this text is actually seetag
aoqi@0 345 if ((delimend = findInlineTagDelim(inlinetext, textstart)) == -1) {
aoqi@0 346 //Missing closing '}' character.
aoqi@0 347 // store the text as it is with the {@link.
aoqi@0 348 taglist.append(new TagImpl(holder, "Text",
aoqi@0 349 inlinetext.substring(textstart)));
aoqi@0 350 docenv.warning(holder,
aoqi@0 351 "tag.End_delimiter_missing_for_possible_SeeTag",
aoqi@0 352 inlinetext);
aoqi@0 353 return taglist.toArray(new Tag[taglist.length()]);
aoqi@0 354 } else {
aoqi@0 355 //Found closing '}' character.
aoqi@0 356 if (linkName.equals("see")
aoqi@0 357 || linkName.equals("link")
aoqi@0 358 || linkName.equals("linkplain")) {
aoqi@0 359 taglist.append( new SeeTagImpl(holder, "@" + linkName,
aoqi@0 360 inlinetext.substring(textstart, delimend)));
aoqi@0 361 } else {
aoqi@0 362 taglist.append( new TagImpl(holder, "@" + linkName,
aoqi@0 363 inlinetext.substring(textstart, delimend)));
aoqi@0 364 }
aoqi@0 365 textstart = delimend + 1;
aoqi@0 366 }
aoqi@0 367 }
aoqi@0 368 if (textstart == inlinetext.length()) {
aoqi@0 369 break;
aoqi@0 370 }
aoqi@0 371 }
aoqi@0 372 return taglist.toArray(new Tag[taglist.length()]);
aoqi@0 373 }
aoqi@0 374
aoqi@0 375 /** regex for case-insensitive match for {@literal <pre> } and {@literal </pre> }. */
aoqi@0 376 private static final Pattern prePat = Pattern.compile("(?i)<(/?)pre>");
aoqi@0 377
aoqi@0 378 private static boolean scanForPre(String inlinetext, int start, int end, boolean inPre) {
aoqi@0 379 Matcher m = prePat.matcher(inlinetext).region(start, end);
aoqi@0 380 while (m.find()) {
aoqi@0 381 inPre = m.group(1).isEmpty();
aoqi@0 382 }
aoqi@0 383 return inPre;
aoqi@0 384 }
aoqi@0 385
aoqi@0 386 /**
aoqi@0 387 * Recursively find the index of the closing '}' character for an inline tag
aoqi@0 388 * and return it. If it can't be found, return -1.
aoqi@0 389 * @param inlineText the text to search in.
aoqi@0 390 * @param searchStart the index of the place to start searching at.
aoqi@0 391 * @return the index of the closing '}' character for an inline tag.
aoqi@0 392 * If it can't be found, return -1.
aoqi@0 393 */
aoqi@0 394 private static int findInlineTagDelim(String inlineText, int searchStart) {
aoqi@0 395 int delimEnd, nestedOpenBrace;
aoqi@0 396 if ((delimEnd = inlineText.indexOf("}", searchStart)) == -1) {
aoqi@0 397 return -1;
aoqi@0 398 } else if (((nestedOpenBrace = inlineText.indexOf("{", searchStart)) != -1) &&
aoqi@0 399 nestedOpenBrace < delimEnd){
aoqi@0 400 //Found a nested open brace.
aoqi@0 401 int nestedCloseBrace = findInlineTagDelim(inlineText, nestedOpenBrace + 1);
aoqi@0 402 return (nestedCloseBrace != -1) ?
aoqi@0 403 findInlineTagDelim(inlineText, nestedCloseBrace + 1) :
aoqi@0 404 -1;
aoqi@0 405 } else {
aoqi@0 406 return delimEnd;
aoqi@0 407 }
aoqi@0 408 }
aoqi@0 409
aoqi@0 410 /**
aoqi@0 411 * Recursively search for the characters '{', '@', followed by
aoqi@0 412 * name of inline tag and white space,
aoqi@0 413 * if found
aoqi@0 414 * return the index of the text following the white space.
aoqi@0 415 * else
aoqi@0 416 * return -1.
aoqi@0 417 */
aoqi@0 418 private static int inlineTagFound(DocImpl holder, String inlinetext, int start) {
aoqi@0 419 DocEnv docenv = holder.env;
aoqi@0 420 int linkstart = inlinetext.indexOf("{@", start);
aoqi@0 421 if (start == inlinetext.length() || linkstart == -1) {
aoqi@0 422 return -1;
aoqi@0 423 } else if (inlinetext.indexOf('}', linkstart) == -1) {
aoqi@0 424 //Missing '}'.
aoqi@0 425 docenv.warning(holder, "tag.Improper_Use_Of_Link_Tag",
aoqi@0 426 inlinetext.substring(linkstart, inlinetext.length()));
aoqi@0 427 return -1;
aoqi@0 428 } else {
aoqi@0 429 return linkstart;
aoqi@0 430 }
aoqi@0 431 }
aoqi@0 432
aoqi@0 433
aoqi@0 434 /**
aoqi@0 435 * Return array of tags for the locale specific first sentence in the text.
aoqi@0 436 */
aoqi@0 437 static Tag[] firstSentenceTags(DocImpl holder, String text) {
aoqi@0 438 DocLocale doclocale = holder.env.doclocale;
aoqi@0 439 return getInlineTags(holder,
aoqi@0 440 doclocale.localeSpecificFirstSentence(holder, text));
aoqi@0 441 }
aoqi@0 442
aoqi@0 443 /**
aoqi@0 444 * Return text for this Doc comment.
aoqi@0 445 */
aoqi@0 446 @Override
aoqi@0 447 public String toString() {
aoqi@0 448 return text;
aoqi@0 449 }
aoqi@0 450 }

mercurial