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

Sat, 24 Oct 2020 16:44:00 +0800

author
aoqi
date
Sat, 24 Oct 2020 16:44:00 +0800
changeset 3932
b8a6df910f59
parent 2525
2eb010b6cb22
parent 3913
242d0ecf82e4
permissions
-rw-r--r--

Merge

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

mercurial