Tue, 09 Oct 2012 19:10:00 -0700
8000663: clean up langtools imports
Reviewed-by: darcy
1 /*
2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.tools.javadoc;
28 import com.sun.javadoc.*;
29 import com.sun.tools.javac.util.*;
31 /**
32 * Represents a see also documentation tag.
33 * The @see tag can be plain text, or reference a class or member.
34 *
35 * @author Kaiyang Liu (original)
36 * @author Robert Field (rewrite)
37 * @author Atul M Dambalkar
38 *
39 */
40 class SeeTagImpl extends TagImpl implements SeeTag, LayoutCharacters {
42 //### TODO: Searching for classes, fields, and methods
43 //### should follow the normal rules applied by the compiler.
45 /**
46 * where of where#what - i.e. the class name (may be empty)
47 */
48 private String where;
50 /**
51 * what of where#what - i.e. the member (may be null)
52 */
53 private String what;
55 private PackageDoc referencedPackage;
56 private ClassDoc referencedClass;
57 private MemberDoc referencedMember;
59 String label = "";
61 SeeTagImpl(DocImpl holder, String name, String text) {
62 super(holder, name, text);
63 parseSeeString();
64 if (where != null) {
65 ClassDocImpl container = null;
66 if (holder instanceof MemberDoc) {
67 container =
68 (ClassDocImpl)((ProgramElementDoc)holder).containingClass();
69 } else if (holder instanceof ClassDoc) {
70 container = (ClassDocImpl)holder;
71 }
72 findReferenced(container);
73 }
74 }
76 /**
77 * get the class name part of @see, For instance,
78 * if the comment is @see String#startsWith(java.lang.String) .
79 * This function returns String.
80 * Returns null if format was not that of java reference.
81 * Return empty string if class name was not specified..
82 */
83 public String referencedClassName() {
84 return where;
85 }
87 /**
88 * get the package referenced by @see. For instance,
89 * if the comment is @see java.lang
90 * This function returns a PackageDocImpl for java.lang
91 * Returns null if no known package found.
92 */
93 public PackageDoc referencedPackage() {
94 return referencedPackage;
95 }
97 /**
98 * get the class referenced by the class name part of @see, For instance,
99 * if the comment is @see String#startsWith(java.lang.String) .
100 * This function returns a ClassDocImpl for java.lang.String.
101 * Returns null if class is not a class specified on the javadoc command line..
102 */
103 public ClassDoc referencedClass() {
104 return referencedClass;
105 }
107 /**
108 * get the name of the member referenced by the prototype part of @see,
109 * For instance,
110 * if the comment is @see String#startsWith(java.lang.String) .
111 * This function returns "startsWith(java.lang.String)"
112 * Returns null if format was not that of java reference.
113 * Return empty string if member name was not specified..
114 */
115 public String referencedMemberName() {
116 return what;
117 }
119 /**
120 * get the member referenced by the prototype part of @see,
121 * For instance,
122 * if the comment is @see String#startsWith(java.lang.String) .
123 * This function returns a MethodDocImpl for startsWith.
124 * Returns null if member could not be determined.
125 */
126 public MemberDoc referencedMember() {
127 return referencedMember;
128 }
131 /**
132 * parse @see part of comment. Determine 'where' and 'what'
133 */
134 private void parseSeeString() {
135 int len = text.length();
136 if (len == 0) {
137 return;
138 }
139 switch (text.charAt(0)) {
140 case '<':
141 if (text.charAt(len-1) != '>') {
142 docenv().warning(holder,
143 "tag.see.no_close_bracket_on_url",
144 name, text);
145 }
146 return;
147 case '"':
148 if (len == 1 || text.charAt(len-1) != '"') {
149 docenv().warning(holder,
150 "tag.see.no_close_quote",
151 name, text);
152 } else {
153 // text = text.substring(1,len-1); // strip quotes
154 }
155 return;
156 }
158 // check that the text is one word, with possible parentheses
159 // this part of code doesn't allow
160 // @see <a href=.....>asfd</a>
161 // comment it.
163 // the code assumes that there is no initial white space.
164 int parens = 0;
165 int commentstart = 0;
166 int start = 0;
167 int cp;
168 for (int i = start; i < len ; i += Character.charCount(cp)) {
169 cp = text.codePointAt(i);
170 switch (cp) {
171 case '(': parens++; break;
172 case ')': parens--; break;
173 case '[': case ']': case '.': case '#': break;
174 case ',':
175 if (parens <= 0) {
176 docenv().warning(holder,
177 "tag.see.malformed_see_tag",
178 name, text);
179 return;
180 }
181 break;
182 case ' ': case '\t': case '\n': case CR:
183 if (parens == 0) { //here onwards the comment starts.
184 commentstart = i;
185 i = len;
186 }
187 break;
188 default:
189 if (!Character.isJavaIdentifierPart(cp)) {
190 docenv().warning(holder,
191 "tag.see.illegal_character",
192 name, ""+cp, text);
193 }
194 break;
195 }
196 }
197 if (parens != 0) {
198 docenv().warning(holder,
199 "tag.see.malformed_see_tag",
200 name, text);
201 return;
202 }
204 String seetext = "";
205 String labeltext = "";
207 if (commentstart > 0) {
208 seetext = text.substring(start, commentstart);
209 labeltext = text.substring(commentstart + 1);
210 // strip off the white space which can be between seetext and the
211 // actual label.
212 for (int i = 0; i < labeltext.length(); i++) {
213 char ch2 = labeltext.charAt(i);
214 if (!(ch2 == ' ' || ch2 == '\t' || ch2 == '\n')) {
215 label = labeltext.substring(i);
216 break;
217 }
218 }
219 } else {
220 seetext = text;
221 label = "";
222 }
224 int sharp = seetext.indexOf('#');
225 if (sharp >= 0) {
226 // class#member
227 where = seetext.substring(0, sharp);
228 what = seetext.substring(sharp + 1);
229 } else {
230 if (seetext.indexOf('(') >= 0) {
231 docenv().warning(holder,
232 "tag.see.missing_sharp",
233 name, text);
234 where = "";
235 what = seetext;
236 }
237 else {
238 // no member specified, text names class
239 where = seetext;
240 what = null;
241 }
242 }
243 }
245 /**
246 * Find what is referenced by the see also. If possible, sets
247 * referencedClass and referencedMember.
248 *
249 * @param containingClass the class containing the comment containing
250 * the tag. May be null, if, for example, it is a package comment.
251 */
252 private void findReferenced(ClassDocImpl containingClass) {
253 if (where.length() > 0) {
254 if (containingClass != null) {
255 referencedClass = containingClass.findClass(where);
256 } else {
257 referencedClass = docenv().lookupClass(where);
258 }
259 if (referencedClass == null && holder() instanceof ProgramElementDoc) {
260 referencedClass = docenv().lookupClass(
261 ((ProgramElementDoc) holder()).containingPackage().name() + "." + where);
262 }
264 if (referencedClass == null) { /* may just not be in this run */
265 // docenv().warning(holder, "tag.see.class_not_found",
266 // where, text);
267 // check if it's a package name
268 referencedPackage = docenv().lookupPackage(where);
269 return;
270 }
271 } else {
272 if (containingClass == null) {
273 docenv().warning(holder,
274 "tag.see.class_not_specified",
275 name, text);
276 return;
277 } else {
278 referencedClass = containingClass;
279 }
280 }
281 where = referencedClass.qualifiedName();
283 if (what == null) {
284 return;
285 } else {
286 int paren = what.indexOf('(');
287 String memName = (paren >= 0 ? what.substring(0, paren) : what);
288 String[] paramarr;
289 if (paren > 0) {
290 // has parameter list -- should be method or constructor
291 paramarr = new ParameterParseMachine(what.
292 substring(paren, what.length())).parseParameters();
293 if (paramarr != null) {
294 referencedMember = findExecutableMember(memName, paramarr,
295 referencedClass);
296 } else {
297 referencedMember = null;
298 }
299 } else {
300 // no parameter list -- should be field
301 referencedMember = findExecutableMember(memName, null,
302 referencedClass);
303 FieldDoc fd = ((ClassDocImpl)referencedClass).
304 findField(memName);
305 // when no args given, prefer fields over methods
306 if (referencedMember == null ||
307 (fd != null &&
308 fd.containingClass()
309 .subclassOf(referencedMember.containingClass()))) {
310 referencedMember = fd;
311 }
312 }
313 if (referencedMember == null) {
314 docenv().warning(holder,
315 "tag.see.can_not_find_member",
316 name, what, where);
317 }
318 }
319 }
321 private MemberDoc findReferencedMethod(String memName, String[] paramarr,
322 ClassDoc referencedClass) {
323 MemberDoc meth = findExecutableMember(memName, paramarr, referencedClass);
324 ClassDoc[] nestedclasses = referencedClass.innerClasses();
325 if (meth == null) {
326 for (int i = 0; i < nestedclasses.length; i++) {
327 meth = findReferencedMethod(memName, paramarr, nestedclasses[i]);
328 if (meth != null) {
329 return meth;
330 }
331 }
332 }
333 return null;
334 }
336 private MemberDoc findExecutableMember(String memName, String[] paramarr,
337 ClassDoc referencedClass) {
338 if (memName.equals(referencedClass.name())) {
339 return ((ClassDocImpl)referencedClass).findConstructor(memName,
340 paramarr);
341 } else { // it's a method.
342 return ((ClassDocImpl)referencedClass).findMethod(memName,
343 paramarr);
344 }
345 }
347 // separate "int, String" from "(int, String)"
348 // (int i, String s) ==> [0] = "int", [1] = String
349 // (int[][], String[]) ==> [0] = "int[][]" // [1] = "String[]"
350 class ParameterParseMachine {
351 static final int START = 0;
352 static final int TYPE = 1;
353 static final int NAME = 2;
354 static final int TNSPACE = 3; // space between type and name
355 static final int ARRAYDECORATION = 4;
356 static final int ARRAYSPACE = 5;
358 String parameters;
360 StringBuilder typeId;
362 ListBuffer<String> paramList;
364 ParameterParseMachine(String parameters) {
365 this.parameters = parameters;
366 this.paramList = new ListBuffer<String>();
367 typeId = new StringBuilder();
368 }
370 public String[] parseParameters() {
371 if (parameters.equals("()")) {
372 return new String[0];
373 } // now strip off '(' and ')'
374 int state = START;
375 int prevstate = START;
376 parameters = parameters.substring(1, parameters.length() - 1);
377 int cp;
378 for (int index = 0; index < parameters.length(); index += Character.charCount(cp)) {
379 cp = parameters.codePointAt(index);
380 switch (state) {
381 case START:
382 if (Character.isJavaIdentifierStart(cp)) {
383 typeId.append(Character.toChars(cp));
384 state = TYPE;
385 }
386 prevstate = START;
387 break;
388 case TYPE:
389 if (Character.isJavaIdentifierPart(cp) || cp == '.') {
390 typeId.append(Character.toChars(cp));
391 } else if (cp == '[') {
392 typeId.append('[');
393 state = ARRAYDECORATION;
394 } else if (Character.isWhitespace(cp)) {
395 state = TNSPACE;
396 } else if (cp == ',') { // no name, just type
397 addTypeToParamList();
398 state = START;
399 }
400 prevstate = TYPE;
401 break;
402 case TNSPACE:
403 if (Character.isJavaIdentifierStart(cp)) { // name
404 if (prevstate == ARRAYDECORATION) {
405 docenv().warning(holder,
406 "tag.missing_comma_space",
407 name,
408 "(" + parameters + ")");
409 return (String[])null;
410 }
411 addTypeToParamList();
412 state = NAME;
413 } else if (cp == '[') {
414 typeId.append('[');
415 state = ARRAYDECORATION;
416 } else if (cp == ',') { // just the type
417 addTypeToParamList();
418 state = START;
419 } // consume rest all
420 prevstate = TNSPACE;
421 break;
422 case ARRAYDECORATION:
423 if (cp == ']') {
424 typeId.append(']');
425 state = TNSPACE;
426 } else if (!Character.isWhitespace(cp)) {
427 docenv().warning(holder,
428 "tag.illegal_char_in_arr_dim",
429 name,
430 "(" + parameters + ")");
431 return (String[])null;
432 }
433 prevstate = ARRAYDECORATION;
434 break;
435 case NAME:
436 if (cp == ',') { // just consume everything till ','
437 state = START;
438 }
439 prevstate = NAME;
440 break;
441 }
442 }
443 if (state == ARRAYDECORATION ||
444 (state == START && prevstate == TNSPACE)) {
445 docenv().warning(holder,
446 "tag.illegal_see_tag",
447 "(" + parameters + ")");
448 }
449 if (typeId.length() > 0) {
450 paramList.append(typeId.toString());
451 }
452 return paramList.toArray(new String[paramList.length()]);
453 }
455 void addTypeToParamList() {
456 if (typeId.length() > 0) {
457 paramList.append(typeId.toString());
458 typeId.setLength(0);
459 }
460 }
461 }
463 /**
464 * Return the kind of this tag.
465 */
466 @Override
467 public String kind() {
468 return "@see";
469 }
471 /**
472 * Return the label of the see tag.
473 */
474 public String label() {
475 return label;
476 }
477 }