Tue, 06 Nov 2012 14:32:49 -0800
8000612: Discrepancy between resources provided in javadoc resource files and resources required by code
Reviewed-by: bpatel
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 // check if it's a package name
271 referencedPackage = docenv().lookupPackage(where);
272 return;
273 }
274 } else {
275 if (containingClass == null) {
276 docenv().warning(holder,
277 "tag.see.class_not_specified",
278 name, text);
279 return;
280 } else {
281 referencedClass = containingClass;
282 }
283 }
284 where = referencedClass.qualifiedName();
286 if (what == null) {
287 return;
288 } else {
289 int paren = what.indexOf('(');
290 String memName = (paren >= 0 ? what.substring(0, paren) : what);
291 String[] paramarr;
292 if (paren > 0) {
293 // has parameter list -- should be method or constructor
294 paramarr = new ParameterParseMachine(what.
295 substring(paren, what.length())).parseParameters();
296 if (paramarr != null) {
297 referencedMember = findExecutableMember(memName, paramarr,
298 referencedClass);
299 } else {
300 referencedMember = null;
301 }
302 } else {
303 // no parameter list -- should be field
304 referencedMember = findExecutableMember(memName, null,
305 referencedClass);
306 FieldDoc fd = ((ClassDocImpl)referencedClass).
307 findField(memName);
308 // when no args given, prefer fields over methods
309 if (referencedMember == null ||
310 (fd != null &&
311 fd.containingClass()
312 .subclassOf(referencedMember.containingClass()))) {
313 referencedMember = fd;
314 }
315 }
316 if (referencedMember == null) {
317 docenv().warning(holder,
318 "tag.see.can_not_find_member",
319 name, what, where);
320 }
321 }
322 }
324 private MemberDoc findReferencedMethod(String memName, String[] paramarr,
325 ClassDoc referencedClass) {
326 MemberDoc meth = findExecutableMember(memName, paramarr, referencedClass);
327 ClassDoc[] nestedclasses = referencedClass.innerClasses();
328 if (meth == null) {
329 for (int i = 0; i < nestedclasses.length; i++) {
330 meth = findReferencedMethod(memName, paramarr, nestedclasses[i]);
331 if (meth != null) {
332 return meth;
333 }
334 }
335 }
336 return null;
337 }
339 private MemberDoc findExecutableMember(String memName, String[] paramarr,
340 ClassDoc referencedClass) {
341 if (memName.equals(referencedClass.name())) {
342 return ((ClassDocImpl)referencedClass).findConstructor(memName,
343 paramarr);
344 } else { // it's a method.
345 return ((ClassDocImpl)referencedClass).findMethod(memName,
346 paramarr);
347 }
348 }
350 // separate "int, String" from "(int, String)"
351 // (int i, String s) ==> [0] = "int", [1] = String
352 // (int[][], String[]) ==> [0] = "int[][]" // [1] = "String[]"
353 class ParameterParseMachine {
354 static final int START = 0;
355 static final int TYPE = 1;
356 static final int NAME = 2;
357 static final int TNSPACE = 3; // space between type and name
358 static final int ARRAYDECORATION = 4;
359 static final int ARRAYSPACE = 5;
361 String parameters;
363 StringBuilder typeId;
365 ListBuffer<String> paramList;
367 ParameterParseMachine(String parameters) {
368 this.parameters = parameters;
369 this.paramList = new ListBuffer<String>();
370 typeId = new StringBuilder();
371 }
373 public String[] parseParameters() {
374 if (parameters.equals("()")) {
375 return new String[0];
376 } // now strip off '(' and ')'
377 int state = START;
378 int prevstate = START;
379 parameters = parameters.substring(1, parameters.length() - 1);
380 int cp;
381 for (int index = 0; index < parameters.length(); index += Character.charCount(cp)) {
382 cp = parameters.codePointAt(index);
383 switch (state) {
384 case START:
385 if (Character.isJavaIdentifierStart(cp)) {
386 typeId.append(Character.toChars(cp));
387 state = TYPE;
388 }
389 prevstate = START;
390 break;
391 case TYPE:
392 if (Character.isJavaIdentifierPart(cp) || cp == '.') {
393 typeId.append(Character.toChars(cp));
394 } else if (cp == '[') {
395 typeId.append('[');
396 state = ARRAYDECORATION;
397 } else if (Character.isWhitespace(cp)) {
398 state = TNSPACE;
399 } else if (cp == ',') { // no name, just type
400 addTypeToParamList();
401 state = START;
402 }
403 prevstate = TYPE;
404 break;
405 case TNSPACE:
406 if (Character.isJavaIdentifierStart(cp)) { // name
407 if (prevstate == ARRAYDECORATION) {
408 docenv().warning(holder,
409 "tag.missing_comma_space",
410 name,
411 "(" + parameters + ")");
412 return (String[])null;
413 }
414 addTypeToParamList();
415 state = NAME;
416 } else if (cp == '[') {
417 typeId.append('[');
418 state = ARRAYDECORATION;
419 } else if (cp == ',') { // just the type
420 addTypeToParamList();
421 state = START;
422 } // consume rest all
423 prevstate = TNSPACE;
424 break;
425 case ARRAYDECORATION:
426 if (cp == ']') {
427 typeId.append(']');
428 state = TNSPACE;
429 } else if (!Character.isWhitespace(cp)) {
430 docenv().warning(holder,
431 "tag.illegal_char_in_arr_dim",
432 name,
433 "(" + parameters + ")");
434 return (String[])null;
435 }
436 prevstate = ARRAYDECORATION;
437 break;
438 case NAME:
439 if (cp == ',') { // just consume everything till ','
440 state = START;
441 }
442 prevstate = NAME;
443 break;
444 }
445 }
446 if (state == ARRAYDECORATION ||
447 (state == START && prevstate == TNSPACE)) {
448 docenv().warning(holder,
449 "tag.illegal_see_tag",
450 "(" + parameters + ")");
451 }
452 if (typeId.length() > 0) {
453 paramList.append(typeId.toString());
454 }
455 return paramList.toArray(new String[paramList.length()]);
456 }
458 void addTypeToParamList() {
459 if (typeId.length() > 0) {
460 paramList.append(typeId.toString());
461 typeId.setLength(0);
462 }
463 }
464 }
466 /**
467 * Return the kind of this tag.
468 */
469 @Override
470 public String kind() {
471 return "@see";
472 }
474 /**
475 * Return the label of the see tag.
476 */
477 public String label() {
478 return label;
479 }
480 }