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

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

mercurial