Fri, 30 Nov 2012 15:14:48 +0000
8002099: Add support for intersection types in cast expression
Summary: Add parser and type-checking support for intersection types in cast expressions
Reviewed-by: jjg
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 java.io.File;
29 import java.util.Locale;
31 import com.sun.javadoc.*;
32 import com.sun.tools.javac.code.Kinds;
33 import com.sun.tools.javac.code.Printer;
34 import com.sun.tools.javac.code.Symbol;
35 import com.sun.tools.javac.code.Type.CapturedType;
36 import com.sun.tools.javac.util.*;
38 /**
39 * Represents a see also documentation tag.
40 * The @see tag can be plain text, or reference a class or member.
41 *
42 * <p><b>This is NOT part of any supported API.
43 * If you write code that depends on this, you do so at your own risk.
44 * This code and its internal interfaces are subject to change or
45 * deletion without notice.</b>
46 *
47 * @author Kaiyang Liu (original)
48 * @author Robert Field (rewrite)
49 * @author Atul M Dambalkar
50 *
51 */
52 class SeeTagImpl extends TagImpl implements SeeTag, LayoutCharacters {
54 //### TODO: Searching for classes, fields, and methods
55 //### should follow the normal rules applied by the compiler.
57 /**
58 * where of where#what - i.e. the class name (may be empty)
59 */
60 private String where;
62 /**
63 * what of where#what - i.e. the member (may be null)
64 */
65 private String what;
67 private PackageDoc referencedPackage;
68 private ClassDoc referencedClass;
69 private MemberDoc referencedMember;
71 String label = "";
73 SeeTagImpl(DocImpl holder, String name, String text) {
74 super(holder, name, text);
75 parseSeeString();
76 if (where != null) {
77 ClassDocImpl container = null;
78 if (holder instanceof MemberDoc) {
79 container =
80 (ClassDocImpl)((ProgramElementDoc)holder).containingClass();
81 } else if (holder instanceof ClassDoc) {
82 container = (ClassDocImpl)holder;
83 }
84 findReferenced(container);
85 if (showRef) showRef();
86 }
87 }
89 private static final boolean showRef = false;
91 private void showRef() {
92 Symbol sym;
93 if (referencedMember != null) {
94 if (referencedMember instanceof MethodDocImpl)
95 sym = ((MethodDocImpl) referencedMember).sym;
96 else if (referencedMember instanceof FieldDocImpl)
97 sym = ((FieldDocImpl) referencedMember).sym;
98 else
99 sym = ((ConstructorDocImpl) referencedMember).sym;
100 } else if (referencedClass != null) {
101 sym = ((ClassDocImpl) referencedClass).tsym;
102 } else if (referencedPackage != null) {
103 sym = ((PackageDocImpl) referencedPackage).sym;
104 } else
105 return;
107 final JavacMessages messages = JavacMessages.instance(docenv().context);
108 Locale locale = Locale.getDefault();
109 Printer printer = new Printer() {
110 int count;
111 @Override
112 protected String localize(Locale locale, String key, Object... args) {
113 return messages.getLocalizedString(locale, key, args);
114 }
115 @Override
116 protected String capturedVarId(CapturedType t, Locale locale) {
117 return "CAP#" + (++count);
118 }
119 };
121 String s = text.replaceAll("\\s+", " "); // normalize white space
122 int sp = s.indexOf(" ");
123 int lparen = s.indexOf("(");
124 int rparen = s.indexOf(")");
125 String seetext = (sp == -1) ? s
126 : (lparen == -1 || sp < lparen) ? s.substring(0, sp)
127 : s.substring(0, rparen + 1);
129 File file = new File(holder.position().file().getAbsoluteFile().toURI().normalize());
131 StringBuilder sb = new StringBuilder();
132 sb.append("+++ ").append(file).append(": ")
133 .append(name()).append(" ").append(seetext).append(": ");
134 sb.append(sym.getKind()).append(" ");
135 if (sym.kind == Kinds.MTH || sym.kind == Kinds.VAR)
136 sb.append(printer.visit(sym.owner, locale)).append(".");
137 sb.append(printer.visit(sym, locale));
139 System.err.println(sb);
140 }
142 /**
143 * get the class name part of @see, For instance,
144 * if the comment is @see String#startsWith(java.lang.String) .
145 * This function returns String.
146 * Returns null if format was not that of java reference.
147 * Return empty string if class name was not specified..
148 */
149 public String referencedClassName() {
150 return where;
151 }
153 /**
154 * get the package referenced by @see. For instance,
155 * if the comment is @see java.lang
156 * This function returns a PackageDocImpl for java.lang
157 * Returns null if no known package found.
158 */
159 public PackageDoc referencedPackage() {
160 return referencedPackage;
161 }
163 /**
164 * get the class referenced by the class name part of @see, For instance,
165 * if the comment is @see String#startsWith(java.lang.String) .
166 * This function returns a ClassDocImpl for java.lang.String.
167 * Returns null if class is not a class specified on the javadoc command line..
168 */
169 public ClassDoc referencedClass() {
170 return referencedClass;
171 }
173 /**
174 * get the name of the member referenced by the prototype part of @see,
175 * For instance,
176 * if the comment is @see String#startsWith(java.lang.String) .
177 * This function returns "startsWith(java.lang.String)"
178 * Returns null if format was not that of java reference.
179 * Return empty string if member name was not specified..
180 */
181 public String referencedMemberName() {
182 return what;
183 }
185 /**
186 * get the member referenced by the prototype part of @see,
187 * For instance,
188 * if the comment is @see String#startsWith(java.lang.String) .
189 * This function returns a MethodDocImpl for startsWith.
190 * Returns null if member could not be determined.
191 */
192 public MemberDoc referencedMember() {
193 return referencedMember;
194 }
197 /**
198 * parse @see part of comment. Determine 'where' and 'what'
199 */
200 private void parseSeeString() {
201 int len = text.length();
202 if (len == 0) {
203 return;
204 }
205 switch (text.charAt(0)) {
206 case '<':
207 if (text.charAt(len-1) != '>') {
208 docenv().warning(holder,
209 "tag.see.no_close_bracket_on_url",
210 name, text);
211 }
212 return;
213 case '"':
214 if (len == 1 || text.charAt(len-1) != '"') {
215 docenv().warning(holder,
216 "tag.see.no_close_quote",
217 name, text);
218 } else {
219 // text = text.substring(1,len-1); // strip quotes
220 }
221 return;
222 }
224 // check that the text is one word, with possible parentheses
225 // this part of code doesn't allow
226 // @see <a href=.....>asfd</a>
227 // comment it.
229 // the code assumes that there is no initial white space.
230 int parens = 0;
231 int commentstart = 0;
232 int start = 0;
233 int cp;
234 for (int i = start; i < len ; i += Character.charCount(cp)) {
235 cp = text.codePointAt(i);
236 switch (cp) {
237 case '(': parens++; break;
238 case ')': parens--; break;
239 case '[': case ']': case '.': case '#': break;
240 case ',':
241 if (parens <= 0) {
242 docenv().warning(holder,
243 "tag.see.malformed_see_tag",
244 name, text);
245 return;
246 }
247 break;
248 case ' ': case '\t': case '\n': case CR:
249 if (parens == 0) { //here onwards the comment starts.
250 commentstart = i;
251 i = len;
252 }
253 break;
254 default:
255 if (!Character.isJavaIdentifierPart(cp)) {
256 docenv().warning(holder,
257 "tag.see.illegal_character",
258 name, ""+cp, text);
259 }
260 break;
261 }
262 }
263 if (parens != 0) {
264 docenv().warning(holder,
265 "tag.see.malformed_see_tag",
266 name, text);
267 return;
268 }
270 String seetext = "";
271 String labeltext = "";
273 if (commentstart > 0) {
274 seetext = text.substring(start, commentstart);
275 labeltext = text.substring(commentstart + 1);
276 // strip off the white space which can be between seetext and the
277 // actual label.
278 for (int i = 0; i < labeltext.length(); i++) {
279 char ch2 = labeltext.charAt(i);
280 if (!(ch2 == ' ' || ch2 == '\t' || ch2 == '\n')) {
281 label = labeltext.substring(i);
282 break;
283 }
284 }
285 } else {
286 seetext = text;
287 label = "";
288 }
290 int sharp = seetext.indexOf('#');
291 if (sharp >= 0) {
292 // class#member
293 where = seetext.substring(0, sharp);
294 what = seetext.substring(sharp + 1);
295 } else {
296 if (seetext.indexOf('(') >= 0) {
297 docenv().warning(holder,
298 "tag.see.missing_sharp",
299 name, text);
300 where = "";
301 what = seetext;
302 }
303 else {
304 // no member specified, text names class
305 where = seetext;
306 what = null;
307 }
308 }
309 }
311 /**
312 * Find what is referenced by the see also. If possible, sets
313 * referencedClass and referencedMember.
314 *
315 * @param containingClass the class containing the comment containing
316 * the tag. May be null, if, for example, it is a package comment.
317 */
318 private void findReferenced(ClassDocImpl containingClass) {
319 if (where.length() > 0) {
320 if (containingClass != null) {
321 referencedClass = containingClass.findClass(where);
322 } else {
323 referencedClass = docenv().lookupClass(where);
324 }
325 if (referencedClass == null && holder() instanceof ProgramElementDoc) {
326 referencedClass = docenv().lookupClass(
327 ((ProgramElementDoc) holder()).containingPackage().name() + "." + where);
328 }
330 if (referencedClass == null) { /* may just not be in this run */
331 // check if it's a package name
332 referencedPackage = docenv().lookupPackage(where);
333 return;
334 }
335 } else {
336 if (containingClass == null) {
337 docenv().warning(holder,
338 "tag.see.class_not_specified",
339 name, text);
340 return;
341 } else {
342 referencedClass = containingClass;
343 }
344 }
345 where = referencedClass.qualifiedName();
347 if (what == null) {
348 return;
349 } else {
350 int paren = what.indexOf('(');
351 String memName = (paren >= 0 ? what.substring(0, paren) : what);
352 String[] paramarr;
353 if (paren > 0) {
354 // has parameter list -- should be method or constructor
355 paramarr = new ParameterParseMachine(what.
356 substring(paren, what.length())).parseParameters();
357 if (paramarr != null) {
358 referencedMember = findExecutableMember(memName, paramarr,
359 referencedClass);
360 } else {
361 referencedMember = null;
362 }
363 } else {
364 // no parameter list -- should be field
365 referencedMember = findExecutableMember(memName, null,
366 referencedClass);
367 FieldDoc fd = ((ClassDocImpl)referencedClass).
368 findField(memName);
369 // when no args given, prefer fields over methods
370 if (referencedMember == null ||
371 (fd != null &&
372 fd.containingClass()
373 .subclassOf(referencedMember.containingClass()))) {
374 referencedMember = fd;
375 }
376 }
377 if (referencedMember == null) {
378 docenv().warning(holder,
379 "tag.see.can_not_find_member",
380 name, what, where);
381 }
382 }
383 }
385 private MemberDoc findReferencedMethod(String memName, String[] paramarr,
386 ClassDoc referencedClass) {
387 MemberDoc meth = findExecutableMember(memName, paramarr, referencedClass);
388 ClassDoc[] nestedclasses = referencedClass.innerClasses();
389 if (meth == null) {
390 for (int i = 0; i < nestedclasses.length; i++) {
391 meth = findReferencedMethod(memName, paramarr, nestedclasses[i]);
392 if (meth != null) {
393 return meth;
394 }
395 }
396 }
397 return null;
398 }
400 private MemberDoc findExecutableMember(String memName, String[] paramarr,
401 ClassDoc referencedClass) {
402 if (memName.equals(referencedClass.name())) {
403 return ((ClassDocImpl)referencedClass).findConstructor(memName,
404 paramarr);
405 } else { // it's a method.
406 return ((ClassDocImpl)referencedClass).findMethod(memName,
407 paramarr);
408 }
409 }
411 // separate "int, String" from "(int, String)"
412 // (int i, String s) ==> [0] = "int", [1] = String
413 // (int[][], String[]) ==> [0] = "int[][]" // [1] = "String[]"
414 class ParameterParseMachine {
415 static final int START = 0;
416 static final int TYPE = 1;
417 static final int NAME = 2;
418 static final int TNSPACE = 3; // space between type and name
419 static final int ARRAYDECORATION = 4;
420 static final int ARRAYSPACE = 5;
422 String parameters;
424 StringBuilder typeId;
426 ListBuffer<String> paramList;
428 ParameterParseMachine(String parameters) {
429 this.parameters = parameters;
430 this.paramList = new ListBuffer<String>();
431 typeId = new StringBuilder();
432 }
434 public String[] parseParameters() {
435 if (parameters.equals("()")) {
436 return new String[0];
437 } // now strip off '(' and ')'
438 int state = START;
439 int prevstate = START;
440 parameters = parameters.substring(1, parameters.length() - 1);
441 int cp;
442 for (int index = 0; index < parameters.length(); index += Character.charCount(cp)) {
443 cp = parameters.codePointAt(index);
444 switch (state) {
445 case START:
446 if (Character.isJavaIdentifierStart(cp)) {
447 typeId.append(Character.toChars(cp));
448 state = TYPE;
449 }
450 prevstate = START;
451 break;
452 case TYPE:
453 if (Character.isJavaIdentifierPart(cp) || cp == '.') {
454 typeId.append(Character.toChars(cp));
455 } else if (cp == '[') {
456 typeId.append('[');
457 state = ARRAYDECORATION;
458 } else if (Character.isWhitespace(cp)) {
459 state = TNSPACE;
460 } else if (cp == ',') { // no name, just type
461 addTypeToParamList();
462 state = START;
463 }
464 prevstate = TYPE;
465 break;
466 case TNSPACE:
467 if (Character.isJavaIdentifierStart(cp)) { // name
468 if (prevstate == ARRAYDECORATION) {
469 docenv().warning(holder,
470 "tag.missing_comma_space",
471 name,
472 "(" + parameters + ")");
473 return (String[])null;
474 }
475 addTypeToParamList();
476 state = NAME;
477 } else if (cp == '[') {
478 typeId.append('[');
479 state = ARRAYDECORATION;
480 } else if (cp == ',') { // just the type
481 addTypeToParamList();
482 state = START;
483 } // consume rest all
484 prevstate = TNSPACE;
485 break;
486 case ARRAYDECORATION:
487 if (cp == ']') {
488 typeId.append(']');
489 state = TNSPACE;
490 } else if (!Character.isWhitespace(cp)) {
491 docenv().warning(holder,
492 "tag.illegal_char_in_arr_dim",
493 name,
494 "(" + parameters + ")");
495 return (String[])null;
496 }
497 prevstate = ARRAYDECORATION;
498 break;
499 case NAME:
500 if (cp == ',') { // just consume everything till ','
501 state = START;
502 }
503 prevstate = NAME;
504 break;
505 }
506 }
507 if (state == ARRAYDECORATION ||
508 (state == START && prevstate == TNSPACE)) {
509 docenv().warning(holder,
510 "tag.illegal_see_tag",
511 "(" + parameters + ")");
512 }
513 if (typeId.length() > 0) {
514 paramList.append(typeId.toString());
515 }
516 return paramList.toArray(new String[paramList.length()]);
517 }
519 void addTypeToParamList() {
520 if (typeId.length() > 0) {
521 paramList.append(typeId.toString());
522 typeId.setLength(0);
523 }
524 }
525 }
527 /**
528 * Return the kind of this tag.
529 */
530 @Override
531 public String kind() {
532 return "@see";
533 }
535 /**
536 * Return the label of the see tag.
537 */
538 public String label() {
539 return label;
540 }
541 }