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