1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/classes/com/sun/tools/javadoc/Comment.java Sat Dec 01 00:00:00 2007 +0000 1.3 @@ -0,0 +1,430 @@ 1.4 +/* 1.5 + * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Sun designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Sun in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.25 + * CA 95054 USA or visit www.sun.com if you need additional information or 1.26 + * have any questions. 1.27 + */ 1.28 + 1.29 +package com.sun.tools.javadoc; 1.30 + 1.31 +import java.util.Locale; 1.32 + 1.33 +import com.sun.javadoc.*; 1.34 + 1.35 +import com.sun.tools.javac.util.ListBuffer; 1.36 + 1.37 +/** 1.38 + * Comment contains all information in comment part. 1.39 + * It allows users to get first sentence of this comment, get 1.40 + * comment for different tags... 1.41 + * 1.42 + * @author Kaiyang Liu (original) 1.43 + * @author Robert Field (rewrite) 1.44 + * @author Atul M Dambalkar 1.45 + * @author Neal Gafter (rewrite) 1.46 + */ 1.47 +class Comment { 1.48 + 1.49 + /** 1.50 + * sorted comments with different tags. 1.51 + */ 1.52 + private final ListBuffer<Tag> tagList = new ListBuffer<Tag>(); 1.53 + 1.54 + /** 1.55 + * text minus any tags. 1.56 + */ 1.57 + private String text; 1.58 + 1.59 + /** 1.60 + * Doc environment 1.61 + */ 1.62 + private final DocEnv docenv; 1.63 + 1.64 + /** 1.65 + * constructor of Comment. 1.66 + */ 1.67 + Comment(final DocImpl holder, final String commentString) { 1.68 + this.docenv = holder.env; 1.69 + 1.70 + /** 1.71 + * Separate the comment into the text part and zero to N tags. 1.72 + * Simple state machine is in one of three states: 1.73 + * <pre> 1.74 + * IN_TEXT: parsing the comment text or tag text. 1.75 + * TAG_NAME: parsing the name of a tag. 1.76 + * TAG_GAP: skipping through the gap between the tag name and 1.77 + * the tag text. 1.78 + * </pre> 1.79 + */ 1.80 + class CommentStringParser { 1.81 + /** 1.82 + * The entry point to the comment string parser 1.83 + */ 1.84 + void parseCommentStateMachine() { 1.85 + final int IN_TEXT = 1; 1.86 + final int TAG_GAP = 2; 1.87 + final int TAG_NAME = 3; 1.88 + int state = TAG_GAP; 1.89 + boolean newLine = true; 1.90 + String tagName = null; 1.91 + int tagStart = 0; 1.92 + int textStart = 0; 1.93 + int lastNonWhite = -1; 1.94 + int len = commentString.length(); 1.95 + for (int inx = 0; inx < len; ++inx) { 1.96 + char ch = commentString.charAt(inx); 1.97 + boolean isWhite = Character.isWhitespace(ch); 1.98 + switch (state) { 1.99 + case TAG_NAME: 1.100 + if (isWhite) { 1.101 + tagName = commentString.substring(tagStart, inx); 1.102 + state = TAG_GAP; 1.103 + } 1.104 + break; 1.105 + case TAG_GAP: 1.106 + if (isWhite) { 1.107 + break; 1.108 + } 1.109 + textStart = inx; 1.110 + state = IN_TEXT; 1.111 + /* fall thru */ 1.112 + case IN_TEXT: 1.113 + if (newLine && ch == '@') { 1.114 + parseCommentComponent(tagName, textStart, 1.115 + lastNonWhite+1); 1.116 + tagStart = inx; 1.117 + state = TAG_NAME; 1.118 + } 1.119 + break; 1.120 + }; 1.121 + if (ch == '\n') { 1.122 + newLine = true; 1.123 + } else if (!isWhite) { 1.124 + lastNonWhite = inx; 1.125 + newLine = false; 1.126 + } 1.127 + } 1.128 + // Finish what's currently being processed 1.129 + switch (state) { 1.130 + case TAG_NAME: 1.131 + tagName = commentString.substring(tagStart, len); 1.132 + /* fall thru */ 1.133 + case TAG_GAP: 1.134 + textStart = len; 1.135 + /* fall thru */ 1.136 + case IN_TEXT: 1.137 + parseCommentComponent(tagName, textStart, lastNonWhite+1); 1.138 + break; 1.139 + }; 1.140 + } 1.141 + 1.142 + /** 1.143 + * Save away the last parsed item. 1.144 + */ 1.145 + void parseCommentComponent(String tagName, 1.146 + int from, int upto) { 1.147 + String tx = upto <= from ? "" : commentString.substring(from, upto); 1.148 + if (tagName == null) { 1.149 + text = tx; 1.150 + } else { 1.151 + TagImpl tag; 1.152 + if (tagName.equals("@exception") || tagName.equals("@throws")) { 1.153 + warnIfEmpty(tagName, tx); 1.154 + tag = new ThrowsTagImpl(holder, tagName, tx); 1.155 + } else if (tagName.equals("@param")) { 1.156 + warnIfEmpty(tagName, tx); 1.157 + tag = new ParamTagImpl(holder, tagName, tx); 1.158 + } else if (tagName.equals("@see")) { 1.159 + warnIfEmpty(tagName, tx); 1.160 + tag = new SeeTagImpl(holder, tagName, tx); 1.161 + } else if (tagName.equals("@serialField")) { 1.162 + warnIfEmpty(tagName, tx); 1.163 + tag = new SerialFieldTagImpl(holder, tagName, tx); 1.164 + } else if (tagName.equals("@return")) { 1.165 + warnIfEmpty(tagName, tx); 1.166 + tag = new TagImpl(holder, tagName, tx); 1.167 + } else if (tagName.equals("@author")) { 1.168 + warnIfEmpty(tagName, tx); 1.169 + tag = new TagImpl(holder, tagName, tx); 1.170 + } else if (tagName.equals("@version")) { 1.171 + warnIfEmpty(tagName, tx); 1.172 + tag = new TagImpl(holder, tagName, tx); 1.173 + } else { 1.174 + tag = new TagImpl(holder, tagName, tx); 1.175 + } 1.176 + tagList.append(tag); 1.177 + } 1.178 + } 1.179 + 1.180 + void warnIfEmpty(String tagName, String tx) { 1.181 + if (tx.length() == 0) { 1.182 + docenv.warning(holder, "tag.tag_has_no_arguments", tagName); 1.183 + } 1.184 + } 1.185 + 1.186 + } 1.187 + 1.188 + new CommentStringParser().parseCommentStateMachine(); 1.189 + } 1.190 + 1.191 + /** 1.192 + * Return the text of the comment. 1.193 + */ 1.194 + String commentText() { 1.195 + return text; 1.196 + } 1.197 + 1.198 + /** 1.199 + * Return all tags in this comment. 1.200 + */ 1.201 + Tag[] tags() { 1.202 + return tagList.toArray(new Tag[tagList.length()]); 1.203 + } 1.204 + 1.205 + /** 1.206 + * Return tags of the specified kind in this comment. 1.207 + */ 1.208 + Tag[] tags(String tagname) { 1.209 + ListBuffer<Tag> found = new ListBuffer<Tag>(); 1.210 + String target = tagname; 1.211 + if (target.charAt(0) != '@') { 1.212 + target = "@" + target; 1.213 + } 1.214 + for (Tag tag : tagList) { 1.215 + if (tag.kind().equals(target)) { 1.216 + found.append(tag); 1.217 + } 1.218 + } 1.219 + return found.toArray(new Tag[found.length()]); 1.220 + } 1.221 + 1.222 + /** 1.223 + * Return throws tags in this comment. 1.224 + */ 1.225 + ThrowsTag[] throwsTags() { 1.226 + ListBuffer<ThrowsTag> found = new ListBuffer<ThrowsTag>(); 1.227 + for (Tag next : tagList) { 1.228 + if (next instanceof ThrowsTag) { 1.229 + found.append((ThrowsTag)next); 1.230 + } 1.231 + } 1.232 + return found.toArray(new ThrowsTag[found.length()]); 1.233 + } 1.234 + 1.235 + /** 1.236 + * Return param tags (excluding type param tags) in this comment. 1.237 + */ 1.238 + ParamTag[] paramTags() { 1.239 + return paramTags(false); 1.240 + } 1.241 + 1.242 + /** 1.243 + * Return type param tags in this comment. 1.244 + */ 1.245 + ParamTag[] typeParamTags() { 1.246 + return paramTags(true); 1.247 + } 1.248 + 1.249 + /** 1.250 + * Return param tags in this comment. If typeParams is true 1.251 + * include only type param tags, otherwise include only ordinary 1.252 + * param tags. 1.253 + */ 1.254 + private ParamTag[] paramTags(boolean typeParams) { 1.255 + ListBuffer<ParamTag> found = new ListBuffer<ParamTag>(); 1.256 + for (Tag next : tagList) { 1.257 + if (next instanceof ParamTag) { 1.258 + ParamTag p = (ParamTag)next; 1.259 + if (typeParams == p.isTypeParameter()) { 1.260 + found.append(p); 1.261 + } 1.262 + } 1.263 + } 1.264 + return found.toArray(new ParamTag[found.length()]); 1.265 + } 1.266 + 1.267 + /** 1.268 + * Return see also tags in this comment. 1.269 + */ 1.270 + SeeTag[] seeTags() { 1.271 + ListBuffer<SeeTag> found = new ListBuffer<SeeTag>(); 1.272 + for (Tag next : tagList) { 1.273 + if (next instanceof SeeTag) { 1.274 + found.append((SeeTag)next); 1.275 + } 1.276 + } 1.277 + return found.toArray(new SeeTag[found.length()]); 1.278 + } 1.279 + 1.280 + /** 1.281 + * Return serialField tags in this comment. 1.282 + */ 1.283 + SerialFieldTag[] serialFieldTags() { 1.284 + ListBuffer<SerialFieldTag> found = new ListBuffer<SerialFieldTag>(); 1.285 + for (Tag next : tagList) { 1.286 + if (next instanceof SerialFieldTag) { 1.287 + found.append((SerialFieldTag)next); 1.288 + } 1.289 + } 1.290 + return found.toArray(new SerialFieldTag[found.length()]); 1.291 + } 1.292 + 1.293 + /** 1.294 + * Return array of tags with text and inline See Tags for a Doc comment. 1.295 + */ 1.296 + static Tag[] getInlineTags(DocImpl holder, String inlinetext) { 1.297 + ListBuffer<Tag> taglist = new ListBuffer<Tag>(); 1.298 + int delimend = 0, textstart = 0, len = inlinetext.length(); 1.299 + DocEnv docenv = holder.env; 1.300 + 1.301 + if (len == 0) { 1.302 + return taglist.toArray(new Tag[taglist.length()]); 1.303 + } 1.304 + while (true) { 1.305 + int linkstart; 1.306 + if ((linkstart = inlineTagFound(holder, inlinetext, 1.307 + textstart)) == -1) { 1.308 + taglist.append(new TagImpl(holder, "Text", 1.309 + inlinetext.substring(textstart))); 1.310 + break; 1.311 + } else { 1.312 + int seetextstart = linkstart; 1.313 + for (int i = linkstart; i < inlinetext.length(); i++) { 1.314 + char c = inlinetext.charAt(i); 1.315 + if (Character.isWhitespace(c) || 1.316 + c == '}') { 1.317 + seetextstart = i; 1.318 + break; 1.319 + } 1.320 + } 1.321 + String linkName = inlinetext.substring(linkstart+2, seetextstart); 1.322 + //Move past the white space after the inline tag name. 1.323 + while (Character.isWhitespace(inlinetext. 1.324 + charAt(seetextstart))) { 1.325 + if (inlinetext.length() <= seetextstart) { 1.326 + taglist.append(new TagImpl(holder, "Text", 1.327 + inlinetext.substring(textstart, seetextstart))); 1.328 + docenv.warning(holder, 1.329 + "tag.Improper_Use_Of_Link_Tag", 1.330 + inlinetext); 1.331 + return taglist.toArray(new Tag[taglist.length()]); 1.332 + } else { 1.333 + seetextstart++; 1.334 + } 1.335 + } 1.336 + taglist.append(new TagImpl(holder, "Text", 1.337 + inlinetext.substring(textstart, linkstart))); 1.338 + textstart = seetextstart; // this text is actually seetag 1.339 + if ((delimend = findInlineTagDelim(inlinetext, textstart)) == -1) { 1.340 + //Missing closing '}' character. 1.341 + // store the text as it is with the {@link. 1.342 + taglist.append(new TagImpl(holder, "Text", 1.343 + inlinetext.substring(textstart))); 1.344 + docenv.warning(holder, 1.345 + "tag.End_delimiter_missing_for_possible_SeeTag", 1.346 + inlinetext); 1.347 + return taglist.toArray(new Tag[taglist.length()]); 1.348 + } else { 1.349 + //Found closing '}' character. 1.350 + if (linkName.equals("see") 1.351 + || linkName.equals("link") 1.352 + || linkName.equals("linkplain")) { 1.353 + taglist.append( new SeeTagImpl(holder, "@" + linkName, 1.354 + inlinetext.substring(textstart, delimend))); 1.355 + } else { 1.356 + taglist.append( new TagImpl(holder, "@" + linkName, 1.357 + inlinetext.substring(textstart, delimend))); 1.358 + } 1.359 + textstart = delimend + 1; 1.360 + } 1.361 + } 1.362 + if (textstart == inlinetext.length()) { 1.363 + break; 1.364 + } 1.365 + } 1.366 + return taglist.toArray(new Tag[taglist.length()]); 1.367 + } 1.368 + 1.369 + /** 1.370 + * Recursively find the index of the closing '}' character for an inline tag 1.371 + * and return it. If it can't be found, return -1. 1.372 + * @param inlineText the text to search in. 1.373 + * @param searchStart the index of the place to start searching at. 1.374 + * @return the index of the closing '}' character for an inline tag. 1.375 + * If it can't be found, return -1. 1.376 + */ 1.377 + private static int findInlineTagDelim(String inlineText, int searchStart) { 1.378 + int delimEnd, nestedOpenBrace; 1.379 + if ((delimEnd = inlineText.indexOf("}", searchStart)) == -1) { 1.380 + return -1; 1.381 + } else if (((nestedOpenBrace = inlineText.indexOf("{", searchStart)) != -1) && 1.382 + nestedOpenBrace < delimEnd){ 1.383 + //Found a nested open brace. 1.384 + int nestedCloseBrace = findInlineTagDelim(inlineText, nestedOpenBrace + 1); 1.385 + return (nestedCloseBrace != -1) ? 1.386 + findInlineTagDelim(inlineText, nestedCloseBrace + 1) : 1.387 + -1; 1.388 + } else { 1.389 + return delimEnd; 1.390 + } 1.391 + } 1.392 + 1.393 + /** 1.394 + * Recursively search for the string "{@" followed by 1.395 + * name of inline tag and white space, 1.396 + * if found 1.397 + * return the index of the text following the white space. 1.398 + * else 1.399 + * return -1. 1.400 + */ 1.401 + private static int inlineTagFound(DocImpl holder, String inlinetext, int start) { 1.402 + DocEnv docenv = holder.env; 1.403 + int linkstart; 1.404 + if (start == inlinetext.length() || 1.405 + (linkstart = inlinetext.indexOf("{@", start)) == -1) { 1.406 + return -1; 1.407 + } else if(inlinetext.indexOf('}', start) == -1) { 1.408 + //Missing '}'. 1.409 + docenv.warning(holder, "tag.Improper_Use_Of_Link_Tag", 1.410 + inlinetext.substring(linkstart, inlinetext.length())); 1.411 + return -1; 1.412 + } else { 1.413 + return linkstart; 1.414 + } 1.415 + } 1.416 + 1.417 + 1.418 + /** 1.419 + * Return array of tags for the locale specific first sentence in the text. 1.420 + */ 1.421 + static Tag[] firstSentenceTags(DocImpl holder, String text) { 1.422 + DocLocale doclocale = holder.env.doclocale; 1.423 + return getInlineTags(holder, 1.424 + doclocale.localeSpecificFirstSentence(holder, text)); 1.425 + } 1.426 + 1.427 + /** 1.428 + * Return text for this Doc comment. 1.429 + */ 1.430 + public String toString() { 1.431 + return text; 1.432 + } 1.433 +}