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