Tue, 11 Aug 2009 01:13:14 +0100
6521805: Regression: JDK5/JDK6 javac allows write access to outer class reference
Summary: javac should warn/complain about identifiers with the same name as synthetic symbol
Reviewed-by: jjg
1 /*
2 * Copyright 1997-2009 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 @SuppressWarnings("fallthrough")
78 class CommentStringParser {
79 /**
80 * The entry point to the comment string parser
81 */
82 void parseCommentStateMachine() {
83 final int IN_TEXT = 1;
84 final int TAG_GAP = 2;
85 final int TAG_NAME = 3;
86 int state = TAG_GAP;
87 boolean newLine = true;
88 String tagName = null;
89 int tagStart = 0;
90 int textStart = 0;
91 int lastNonWhite = -1;
92 int len = commentString.length();
93 for (int inx = 0; inx < len; ++inx) {
94 char ch = commentString.charAt(inx);
95 boolean isWhite = Character.isWhitespace(ch);
96 switch (state) {
97 case TAG_NAME:
98 if (isWhite) {
99 tagName = commentString.substring(tagStart, inx);
100 state = TAG_GAP;
101 }
102 break;
103 case TAG_GAP:
104 if (isWhite) {
105 break;
106 }
107 textStart = inx;
108 state = IN_TEXT;
109 /* fall thru */
110 case IN_TEXT:
111 if (newLine && ch == '@') {
112 parseCommentComponent(tagName, textStart,
113 lastNonWhite+1);
114 tagStart = inx;
115 state = TAG_NAME;
116 }
117 break;
118 };
119 if (ch == '\n') {
120 newLine = true;
121 } else if (!isWhite) {
122 lastNonWhite = inx;
123 newLine = false;
124 }
125 }
126 // Finish what's currently being processed
127 switch (state) {
128 case TAG_NAME:
129 tagName = commentString.substring(tagStart, len);
130 /* fall thru */
131 case TAG_GAP:
132 textStart = len;
133 /* fall thru */
134 case IN_TEXT:
135 parseCommentComponent(tagName, textStart, lastNonWhite+1);
136 break;
137 };
138 }
140 /**
141 * Save away the last parsed item.
142 */
143 void parseCommentComponent(String tagName,
144 int from, int upto) {
145 String tx = upto <= from ? "" : commentString.substring(from, upto);
146 if (tagName == null) {
147 text = tx;
148 } else {
149 TagImpl tag;
150 if (tagName.equals("@exception") || tagName.equals("@throws")) {
151 warnIfEmpty(tagName, tx);
152 tag = new ThrowsTagImpl(holder, tagName, tx);
153 } else if (tagName.equals("@param")) {
154 warnIfEmpty(tagName, tx);
155 tag = new ParamTagImpl(holder, tagName, tx);
156 } else if (tagName.equals("@see")) {
157 warnIfEmpty(tagName, tx);
158 tag = new SeeTagImpl(holder, tagName, tx);
159 } else if (tagName.equals("@serialField")) {
160 warnIfEmpty(tagName, tx);
161 tag = new SerialFieldTagImpl(holder, tagName, tx);
162 } else if (tagName.equals("@return")) {
163 warnIfEmpty(tagName, tx);
164 tag = new TagImpl(holder, tagName, tx);
165 } else if (tagName.equals("@author")) {
166 warnIfEmpty(tagName, tx);
167 tag = new TagImpl(holder, tagName, tx);
168 } else if (tagName.equals("@version")) {
169 warnIfEmpty(tagName, tx);
170 tag = new TagImpl(holder, tagName, tx);
171 } else {
172 tag = new TagImpl(holder, tagName, tx);
173 }
174 tagList.append(tag);
175 }
176 }
178 void warnIfEmpty(String tagName, String tx) {
179 if (tx.length() == 0) {
180 docenv.warning(holder, "tag.tag_has_no_arguments", tagName);
181 }
182 }
184 }
186 new CommentStringParser().parseCommentStateMachine();
187 }
189 /**
190 * Return the text of the comment.
191 */
192 String commentText() {
193 return text;
194 }
196 /**
197 * Return all tags in this comment.
198 */
199 Tag[] tags() {
200 return tagList.toArray(new Tag[tagList.length()]);
201 }
203 /**
204 * Return tags of the specified kind in this comment.
205 */
206 Tag[] tags(String tagname) {
207 ListBuffer<Tag> found = new ListBuffer<Tag>();
208 String target = tagname;
209 if (target.charAt(0) != '@') {
210 target = "@" + target;
211 }
212 for (Tag tag : tagList) {
213 if (tag.kind().equals(target)) {
214 found.append(tag);
215 }
216 }
217 return found.toArray(new Tag[found.length()]);
218 }
220 /**
221 * Return throws tags in this comment.
222 */
223 ThrowsTag[] throwsTags() {
224 ListBuffer<ThrowsTag> found = new ListBuffer<ThrowsTag>();
225 for (Tag next : tagList) {
226 if (next instanceof ThrowsTag) {
227 found.append((ThrowsTag)next);
228 }
229 }
230 return found.toArray(new ThrowsTag[found.length()]);
231 }
233 /**
234 * Return param tags (excluding type param tags) in this comment.
235 */
236 ParamTag[] paramTags() {
237 return paramTags(false);
238 }
240 /**
241 * Return type param tags in this comment.
242 */
243 ParamTag[] typeParamTags() {
244 return paramTags(true);
245 }
247 /**
248 * Return param tags in this comment. If typeParams is true
249 * include only type param tags, otherwise include only ordinary
250 * param tags.
251 */
252 private ParamTag[] paramTags(boolean typeParams) {
253 ListBuffer<ParamTag> found = new ListBuffer<ParamTag>();
254 for (Tag next : tagList) {
255 if (next instanceof ParamTag) {
256 ParamTag p = (ParamTag)next;
257 if (typeParams == p.isTypeParameter()) {
258 found.append(p);
259 }
260 }
261 }
262 return found.toArray(new ParamTag[found.length()]);
263 }
265 /**
266 * Return see also tags in this comment.
267 */
268 SeeTag[] seeTags() {
269 ListBuffer<SeeTag> found = new ListBuffer<SeeTag>();
270 for (Tag next : tagList) {
271 if (next instanceof SeeTag) {
272 found.append((SeeTag)next);
273 }
274 }
275 return found.toArray(new SeeTag[found.length()]);
276 }
278 /**
279 * Return serialField tags in this comment.
280 */
281 SerialFieldTag[] serialFieldTags() {
282 ListBuffer<SerialFieldTag> found = new ListBuffer<SerialFieldTag>();
283 for (Tag next : tagList) {
284 if (next instanceof SerialFieldTag) {
285 found.append((SerialFieldTag)next);
286 }
287 }
288 return found.toArray(new SerialFieldTag[found.length()]);
289 }
291 /**
292 * Return array of tags with text and inline See Tags for a Doc comment.
293 */
294 static Tag[] getInlineTags(DocImpl holder, String inlinetext) {
295 ListBuffer<Tag> taglist = new ListBuffer<Tag>();
296 int delimend = 0, textstart = 0, len = inlinetext.length();
297 DocEnv docenv = holder.env;
299 if (len == 0) {
300 return taglist.toArray(new Tag[taglist.length()]);
301 }
302 while (true) {
303 int linkstart;
304 if ((linkstart = inlineTagFound(holder, inlinetext,
305 textstart)) == -1) {
306 taglist.append(new TagImpl(holder, "Text",
307 inlinetext.substring(textstart)));
308 break;
309 } else {
310 int seetextstart = linkstart;
311 for (int i = linkstart; i < inlinetext.length(); i++) {
312 char c = inlinetext.charAt(i);
313 if (Character.isWhitespace(c) ||
314 c == '}') {
315 seetextstart = i;
316 break;
317 }
318 }
319 String linkName = inlinetext.substring(linkstart+2, seetextstart);
320 //Move past the white space after the inline tag name.
321 while (Character.isWhitespace(inlinetext.
322 charAt(seetextstart))) {
323 if (inlinetext.length() <= seetextstart) {
324 taglist.append(new TagImpl(holder, "Text",
325 inlinetext.substring(textstart, seetextstart)));
326 docenv.warning(holder,
327 "tag.Improper_Use_Of_Link_Tag",
328 inlinetext);
329 return taglist.toArray(new Tag[taglist.length()]);
330 } else {
331 seetextstart++;
332 }
333 }
334 taglist.append(new TagImpl(holder, "Text",
335 inlinetext.substring(textstart, linkstart)));
336 textstart = seetextstart; // this text is actually seetag
337 if ((delimend = findInlineTagDelim(inlinetext, textstart)) == -1) {
338 //Missing closing '}' character.
339 // store the text as it is with the {@link.
340 taglist.append(new TagImpl(holder, "Text",
341 inlinetext.substring(textstart)));
342 docenv.warning(holder,
343 "tag.End_delimiter_missing_for_possible_SeeTag",
344 inlinetext);
345 return taglist.toArray(new Tag[taglist.length()]);
346 } else {
347 //Found closing '}' character.
348 if (linkName.equals("see")
349 || linkName.equals("link")
350 || linkName.equals("linkplain")) {
351 taglist.append( new SeeTagImpl(holder, "@" + linkName,
352 inlinetext.substring(textstart, delimend)));
353 } else {
354 taglist.append( new TagImpl(holder, "@" + linkName,
355 inlinetext.substring(textstart, delimend)));
356 }
357 textstart = delimend + 1;
358 }
359 }
360 if (textstart == inlinetext.length()) {
361 break;
362 }
363 }
364 return taglist.toArray(new Tag[taglist.length()]);
365 }
367 /**
368 * Recursively find the index of the closing '}' character for an inline tag
369 * and return it. If it can't be found, return -1.
370 * @param inlineText the text to search in.
371 * @param searchStart the index of the place to start searching at.
372 * @return the index of the closing '}' character for an inline tag.
373 * If it can't be found, return -1.
374 */
375 private static int findInlineTagDelim(String inlineText, int searchStart) {
376 int delimEnd, nestedOpenBrace;
377 if ((delimEnd = inlineText.indexOf("}", searchStart)) == -1) {
378 return -1;
379 } else if (((nestedOpenBrace = inlineText.indexOf("{", searchStart)) != -1) &&
380 nestedOpenBrace < delimEnd){
381 //Found a nested open brace.
382 int nestedCloseBrace = findInlineTagDelim(inlineText, nestedOpenBrace + 1);
383 return (nestedCloseBrace != -1) ?
384 findInlineTagDelim(inlineText, nestedCloseBrace + 1) :
385 -1;
386 } else {
387 return delimEnd;
388 }
389 }
391 /**
392 * Recursively search for the string "{@" followed by
393 * name of inline tag and white space,
394 * if found
395 * return the index of the text following the white space.
396 * else
397 * return -1.
398 */
399 private static int inlineTagFound(DocImpl holder, String inlinetext, int start) {
400 DocEnv docenv = holder.env;
401 int linkstart;
402 if (start == inlinetext.length() ||
403 (linkstart = inlinetext.indexOf("{@", start)) == -1) {
404 return -1;
405 } else if(inlinetext.indexOf('}', start) == -1) {
406 //Missing '}'.
407 docenv.warning(holder, "tag.Improper_Use_Of_Link_Tag",
408 inlinetext.substring(linkstart, inlinetext.length()));
409 return -1;
410 } else {
411 return linkstart;
412 }
413 }
416 /**
417 * Return array of tags for the locale specific first sentence in the text.
418 */
419 static Tag[] firstSentenceTags(DocImpl holder, String text) {
420 DocLocale doclocale = holder.env.doclocale;
421 return getInlineTags(holder,
422 doclocale.localeSpecificFirstSentence(holder, text));
423 }
425 /**
426 * Return text for this Doc comment.
427 */
428 public String toString() {
429 return text;
430 }
431 }