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