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

Tue, 24 Dec 2013 09:17:37 -0800

author
ksrini
date
Tue, 24 Dec 2013 09:17:37 -0800
changeset 2227
998b10c43157
parent 1722
38c4bade0ec1
child 2525
2eb010b6cb22
permissions
-rw-r--r--

8029230: Update copyright year to match last edit in jdk8 langtools repository for 2013
Reviewed-by: ksrini
Contributed-by: steve.sides@oracle.com

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

mercurial