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

     1 /*
     2  * Copyright (c) 1997, 2014, 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         String className = referencedClass.name();
   403         if (memName.equals(className.substring(className.lastIndexOf(".") + 1))) {
   404             return ((ClassDocImpl)referencedClass).findConstructor(memName,
   405                                                                    paramarr);
   406         } else {   // it's a method.
   407             return ((ClassDocImpl)referencedClass).findMethod(memName,
   408                                                               paramarr);
   409         }
   410     }
   412     // separate "int, String" from "(int, String)"
   413     // (int i, String s) ==> [0] = "int",  [1] = String
   414     // (int[][], String[]) ==> [0] = "int[][]" // [1] = "String[]"
   415     class ParameterParseMachine {
   416         static final int START = 0;
   417         static final int TYPE = 1;
   418         static final int NAME = 2;
   419         static final int TNSPACE = 3;  // space between type and name
   420         static final int ARRAYDECORATION = 4;
   421         static final int ARRAYSPACE = 5;
   423         String parameters;
   425         StringBuilder typeId;
   427         ListBuffer<String> paramList;
   429         ParameterParseMachine(String parameters) {
   430             this.parameters = parameters;
   431             this.paramList = new ListBuffer<String>();
   432             typeId = new StringBuilder();
   433         }
   435         public String[] parseParameters() {
   436             if (parameters.equals("()")) {
   437                 return new String[0];
   438             }   // now strip off '(' and ')'
   439             int state = START;
   440             int prevstate = START;
   441             parameters = parameters.substring(1, parameters.length() - 1);
   442             int cp;
   443             for (int index = 0; index < parameters.length(); index += Character.charCount(cp)) {
   444                 cp = parameters.codePointAt(index);
   445                 switch (state) {
   446                     case START:
   447                         if (Character.isJavaIdentifierStart(cp)) {
   448                             typeId.append(Character.toChars(cp));
   449                             state = TYPE;
   450                         }
   451                         prevstate = START;
   452                         break;
   453                     case TYPE:
   454                         if (Character.isJavaIdentifierPart(cp) || cp == '.') {
   455                             typeId.append(Character.toChars(cp));
   456                         } else if (cp == '[') {
   457                             typeId.append('[');
   458                             state = ARRAYDECORATION;
   459                         } else if (Character.isWhitespace(cp)) {
   460                             state = TNSPACE;
   461                         } else if (cp == ',') {  // no name, just type
   462                             addTypeToParamList();
   463                             state = START;
   464                         }
   465                         prevstate = TYPE;
   466                         break;
   467                     case TNSPACE:
   468                         if (Character.isJavaIdentifierStart(cp)) { // name
   469                             if (prevstate == ARRAYDECORATION) {
   470                                 docenv().warning(holder,
   471                                                  "tag.missing_comma_space",
   472                                                  name,
   473                                                  "(" + parameters + ")");
   474                                 return (String[])null;
   475                             }
   476                             addTypeToParamList();
   477                             state = NAME;
   478                         } else if (cp == '[') {
   479                             typeId.append('[');
   480                             state = ARRAYDECORATION;
   481                         } else if (cp == ',') {   // just the type
   482                             addTypeToParamList();
   483                             state = START;
   484                         } // consume rest all
   485                         prevstate = TNSPACE;
   486                         break;
   487                     case ARRAYDECORATION:
   488                         if (cp == ']') {
   489                             typeId.append(']');
   490                             state = TNSPACE;
   491                         } else if (!Character.isWhitespace(cp)) {
   492                             docenv().warning(holder,
   493                                              "tag.illegal_char_in_arr_dim",
   494                                              name,
   495                                              "(" + parameters + ")");
   496                             return (String[])null;
   497                         }
   498                         prevstate = ARRAYDECORATION;
   499                         break;
   500                     case NAME:
   501                         if (cp == ',') {  // just consume everything till ','
   502                             state = START;
   503                         }
   504                         prevstate = NAME;
   505                         break;
   506                 }
   507             }
   508             if (state == ARRAYDECORATION ||
   509                 (state == START && prevstate == TNSPACE)) {
   510                 docenv().warning(holder,
   511                                  "tag.illegal_see_tag",
   512                                  "(" + parameters + ")");
   513             }
   514             if (typeId.length() > 0) {
   515                 paramList.append(typeId.toString());
   516             }
   517             return paramList.toArray(new String[paramList.length()]);
   518         }
   520         void addTypeToParamList() {
   521             if (typeId.length() > 0) {
   522                 paramList.append(typeId.toString());
   523                 typeId.setLength(0);
   524             }
   525         }
   526     }
   528     /**
   529      * Return the kind of this tag.
   530      */
   531     @Override
   532     public String kind() {
   533         return "@see";
   534     }
   536     /**
   537      * Return the label of the see tag.
   538      */
   539     public String label() {
   540         return label;
   541     }
   542 }

mercurial