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

Tue, 24 Dec 2013 09:17:37 -0800

author
ksrini
date
Tue, 24 Dec 2013 09:17:37 -0800
changeset 2227
998b10c43157
parent 1722
38c4bade0ec1
child 2525
2eb010b6cb22
permissions
-rw-r--r--

8029230: Update copyright year to match last edit in jdk8 langtools repository for 2013
Reviewed-by: ksrini
Contributed-by: steve.sides@oracle.com

     1 /*
     2  * Copyright (c) 1997, 2013, 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.util.regex.Matcher;
    29 import java.util.regex.Pattern;
    30 import com.sun.javadoc.*;
    31 import com.sun.tools.javac.util.ListBuffer;
    33 /**
    34  * Comment contains all information in comment part.
    35  *      It allows users to get first sentence of this comment, get
    36  *      comment for different tags...
    37  *
    38  *  <p><b>This is NOT part of any supported API.
    39  *  If you write code that depends on this, you do so at your own risk.
    40  *  This code and its internal interfaces are subject to change or
    41  *  deletion without notice.</b>
    42  *
    43  * @author Kaiyang Liu (original)
    44  * @author Robert Field (rewrite)
    45  * @author Atul M Dambalkar
    46  * @author Neal Gafter (rewrite)
    47  */
    48 class Comment {
    50     /**
    51      * sorted comments with different tags.
    52      */
    53     private final ListBuffer<Tag> tagList = new ListBuffer<Tag>();
    55     /**
    56      * text minus any tags.
    57      */
    58     private String text;
    60     /**
    61      * Doc environment
    62      */
    63     private final DocEnv docenv;
    65     /**
    66      * constructor of Comment.
    67      */
    68     Comment(final DocImpl holder, final String commentString) {
    69         this.docenv = holder.env;
    71         /**
    72          * Separate the comment into the text part and zero to N tags.
    73          * Simple state machine is in one of three states:
    74          * <pre>
    75          * IN_TEXT: parsing the comment text or tag text.
    76          * TAG_NAME: parsing the name of a tag.
    77          * TAG_GAP: skipping through the gap between the tag name and
    78          * the tag text.
    79          * </pre>
    80          */
    81         @SuppressWarnings("fallthrough")
    82         class CommentStringParser {
    83             /**
    84              * The entry point to the comment string parser
    85              */
    86             void parseCommentStateMachine() {
    87                 final int IN_TEXT = 1;
    88                 final int TAG_GAP = 2;
    89                 final int TAG_NAME = 3;
    90                 int state = TAG_GAP;
    91                 boolean newLine = true;
    92                 String tagName = null;
    93                 int tagStart = 0;
    94                 int textStart = 0;
    95                 int lastNonWhite = -1;
    96                 int len = commentString.length();
    97                 for (int inx = 0; inx < len; ++inx) {
    98                     char ch = commentString.charAt(inx);
    99                     boolean isWhite = Character.isWhitespace(ch);
   100                     switch (state)  {
   101                         case TAG_NAME:
   102                             if (isWhite) {
   103                                 tagName = commentString.substring(tagStart, inx);
   104                                 state = TAG_GAP;
   105                             }
   106                             break;
   107                         case TAG_GAP:
   108                             if (isWhite) {
   109                                 break;
   110                             }
   111                             textStart = inx;
   112                             state = IN_TEXT;
   113                             /* fall thru */
   114                         case IN_TEXT:
   115                             if (newLine && ch == '@') {
   116                                 parseCommentComponent(tagName, textStart,
   117                                                       lastNonWhite+1);
   118                                 tagStart = inx;
   119                                 state = TAG_NAME;
   120                             }
   121                             break;
   122                     }
   123                     if (ch == '\n') {
   124                         newLine = true;
   125                     } else if (!isWhite) {
   126                         lastNonWhite = inx;
   127                         newLine = false;
   128                     }
   129                 }
   130                 // Finish what's currently being processed
   131                 switch (state)  {
   132                     case TAG_NAME:
   133                         tagName = commentString.substring(tagStart, len);
   134                         /* fall thru */
   135                     case TAG_GAP:
   136                         textStart = len;
   137                         /* fall thru */
   138                     case IN_TEXT:
   139                         parseCommentComponent(tagName, textStart, lastNonWhite+1);
   140                         break;
   141                 }
   142             }
   144             /**
   145              * Save away the last parsed item.
   146              */
   147             void parseCommentComponent(String tagName,
   148                                        int from, int upto) {
   149                 String tx = upto <= from ? "" : commentString.substring(from, upto);
   150                 if (tagName == null) {
   151                     text = tx;
   152                 } else {
   153                     TagImpl tag;
   154                     if (tagName.equals("@exception") || tagName.equals("@throws")) {
   155                         warnIfEmpty(tagName, tx);
   156                         tag = new ThrowsTagImpl(holder, tagName, tx);
   157                     } else if (tagName.equals("@param")) {
   158                         warnIfEmpty(tagName, tx);
   159                         tag = new ParamTagImpl(holder, tagName, tx);
   160                     } else if (tagName.equals("@see")) {
   161                         warnIfEmpty(tagName, tx);
   162                         tag = new SeeTagImpl(holder, tagName, tx);
   163                     } else if (tagName.equals("@serialField")) {
   164                         warnIfEmpty(tagName, tx);
   165                         tag = new SerialFieldTagImpl(holder, tagName, tx);
   166                     } else if (tagName.equals("@return")) {
   167                         warnIfEmpty(tagName, tx);
   168                         tag = new TagImpl(holder, tagName, tx);
   169                     } else if (tagName.equals("@author")) {
   170                         warnIfEmpty(tagName, tx);
   171                         tag = new TagImpl(holder, tagName, tx);
   172                     } else if (tagName.equals("@version")) {
   173                         warnIfEmpty(tagName, tx);
   174                         tag = new TagImpl(holder, tagName, tx);
   175                     } else {
   176                         tag = new TagImpl(holder, tagName, tx);
   177                     }
   178                     tagList.append(tag);
   179                 }
   180             }
   182             void warnIfEmpty(String tagName, String tx) {
   183                 if (tx.length() == 0) {
   184                     docenv.warning(holder, "tag.tag_has_no_arguments", tagName);
   185                 }
   186             }
   188         }
   190         new CommentStringParser().parseCommentStateMachine();
   191     }
   193     /**
   194      * Return the text of the comment.
   195      */
   196     String commentText() {
   197         return text;
   198     }
   200     /**
   201      * Return all tags in this comment.
   202      */
   203     Tag[] tags() {
   204         return tagList.toArray(new Tag[tagList.length()]);
   205     }
   207     /**
   208      * Return tags of the specified kind in this comment.
   209      */
   210     Tag[] tags(String tagname) {
   211         ListBuffer<Tag> found = new ListBuffer<Tag>();
   212         String target = tagname;
   213         if (target.charAt(0) != '@') {
   214             target = "@" + target;
   215         }
   216         for (Tag tag : tagList) {
   217             if (tag.kind().equals(target)) {
   218                 found.append(tag);
   219             }
   220         }
   221         return found.toArray(new Tag[found.length()]);
   222     }
   224     /**
   225      * Return throws tags in this comment.
   226      */
   227     ThrowsTag[] throwsTags() {
   228         ListBuffer<ThrowsTag> found = new ListBuffer<ThrowsTag>();
   229         for (Tag next : tagList) {
   230             if (next instanceof ThrowsTag) {
   231                 found.append((ThrowsTag)next);
   232             }
   233         }
   234         return found.toArray(new ThrowsTag[found.length()]);
   235     }
   237     /**
   238      * Return param tags (excluding type param tags) in this comment.
   239      */
   240     ParamTag[] paramTags() {
   241         return paramTags(false);
   242     }
   244     /**
   245      * Return type param tags in this comment.
   246      */
   247     ParamTag[] typeParamTags() {
   248         return paramTags(true);
   249     }
   251     /**
   252      * Return param tags in this comment.  If typeParams is true
   253      * include only type param tags, otherwise include only ordinary
   254      * param tags.
   255      */
   256     private ParamTag[] paramTags(boolean typeParams) {
   257         ListBuffer<ParamTag> found = new ListBuffer<ParamTag>();
   258         for (Tag next : tagList) {
   259             if (next instanceof ParamTag) {
   260                 ParamTag p = (ParamTag)next;
   261                 if (typeParams == p.isTypeParameter()) {
   262                     found.append(p);
   263                 }
   264             }
   265         }
   266         return found.toArray(new ParamTag[found.length()]);
   267     }
   269     /**
   270      * Return see also tags in this comment.
   271      */
   272     SeeTag[] seeTags() {
   273         ListBuffer<SeeTag> found = new ListBuffer<SeeTag>();
   274         for (Tag next : tagList) {
   275             if (next instanceof SeeTag) {
   276                 found.append((SeeTag)next);
   277             }
   278         }
   279         return found.toArray(new SeeTag[found.length()]);
   280     }
   282     /**
   283      * Return serialField tags in this comment.
   284      */
   285     SerialFieldTag[] serialFieldTags() {
   286         ListBuffer<SerialFieldTag> found = new ListBuffer<SerialFieldTag>();
   287         for (Tag next : tagList) {
   288             if (next instanceof SerialFieldTag) {
   289                 found.append((SerialFieldTag)next);
   290             }
   291         }
   292         return found.toArray(new SerialFieldTag[found.length()]);
   293     }
   295     /**
   296      * Return array of tags with text and inline See Tags for a Doc comment.
   297      */
   298     static Tag[] getInlineTags(DocImpl holder, String inlinetext) {
   299         ListBuffer<Tag> taglist = new ListBuffer<Tag>();
   300         int delimend = 0, textstart = 0, len = inlinetext.length();
   301         boolean inPre = false;
   302         DocEnv docenv = holder.env;
   304         if (len == 0) {
   305             return taglist.toArray(new Tag[taglist.length()]);
   306         }
   307         while (true) {
   308             int linkstart;
   309             if ((linkstart = inlineTagFound(holder, inlinetext,
   310                                             textstart)) == -1) {
   311                 taglist.append(new TagImpl(holder, "Text",
   312                                            inlinetext.substring(textstart)));
   313                 break;
   314             } else {
   315                 inPre = scanForPre(inlinetext, textstart, linkstart, inPre);
   316                 int seetextstart = linkstart;
   317                 for (int i = linkstart; i < inlinetext.length(); i++) {
   318                     char c = inlinetext.charAt(i);
   319                     if (Character.isWhitespace(c) ||
   320                         c == '}') {
   321                         seetextstart = i;
   322                         break;
   323                      }
   324                 }
   325                 String linkName = inlinetext.substring(linkstart+2, seetextstart);
   326                 if (!(inPre && (linkName.equals("code") || linkName.equals("literal")))) {
   327                     //Move past the white space after the inline tag name.
   328                     while (Character.isWhitespace(inlinetext.
   329                                                       charAt(seetextstart))) {
   330                         if (inlinetext.length() <= seetextstart) {
   331                             taglist.append(new TagImpl(holder, "Text",
   332                                                        inlinetext.substring(textstart, seetextstart)));
   333                             docenv.warning(holder,
   334                                            "tag.Improper_Use_Of_Link_Tag",
   335                                            inlinetext);
   336                             return taglist.toArray(new Tag[taglist.length()]);
   337                         } else {
   338                             seetextstart++;
   339                         }
   340                     }
   341                 }
   342                 taglist.append(new TagImpl(holder, "Text",
   343                                            inlinetext.substring(textstart, linkstart)));
   344                 textstart = seetextstart;   // this text is actually seetag
   345                 if ((delimend = findInlineTagDelim(inlinetext, textstart)) == -1) {
   346                     //Missing closing '}' character.
   347                     // store the text as it is with the {@link.
   348                     taglist.append(new TagImpl(holder, "Text",
   349                                                inlinetext.substring(textstart)));
   350                     docenv.warning(holder,
   351                                    "tag.End_delimiter_missing_for_possible_SeeTag",
   352                                    inlinetext);
   353                     return taglist.toArray(new Tag[taglist.length()]);
   354                 } else {
   355                     //Found closing '}' character.
   356                     if (linkName.equals("see")
   357                            || linkName.equals("link")
   358                            || linkName.equals("linkplain")) {
   359                         taglist.append( new SeeTagImpl(holder, "@" + linkName,
   360                               inlinetext.substring(textstart, delimend)));
   361                     } else {
   362                         taglist.append( new TagImpl(holder, "@" + linkName,
   363                               inlinetext.substring(textstart, delimend)));
   364                     }
   365                     textstart = delimend + 1;
   366                 }
   367             }
   368             if (textstart == inlinetext.length()) {
   369                 break;
   370             }
   371         }
   372         return taglist.toArray(new Tag[taglist.length()]);
   373     }
   375     /** regex for case-insensitive match for {@literal <pre> } and  {@literal </pre> }. */
   376     private static final Pattern prePat = Pattern.compile("(?i)<(/?)pre>");
   378     private static boolean scanForPre(String inlinetext, int start, int end, boolean inPre) {
   379         Matcher m = prePat.matcher(inlinetext).region(start, end);
   380         while (m.find()) {
   381             inPre = m.group(1).isEmpty();
   382         }
   383         return inPre;
   384     }
   386     /**
   387      * Recursively find the index of the closing '}' character for an inline tag
   388      * and return it.  If it can't be found, return -1.
   389      * @param inlineText the text to search in.
   390      * @param searchStart the index of the place to start searching at.
   391      * @return the index of the closing '}' character for an inline tag.
   392      * If it can't be found, return -1.
   393      */
   394     private static int findInlineTagDelim(String inlineText, int searchStart) {
   395         int delimEnd, nestedOpenBrace;
   396         if ((delimEnd = inlineText.indexOf("}", searchStart)) == -1) {
   397             return -1;
   398         } else if (((nestedOpenBrace = inlineText.indexOf("{", searchStart)) != -1) &&
   399             nestedOpenBrace < delimEnd){
   400             //Found a nested open brace.
   401             int nestedCloseBrace = findInlineTagDelim(inlineText, nestedOpenBrace + 1);
   402             return (nestedCloseBrace != -1) ?
   403                 findInlineTagDelim(inlineText, nestedCloseBrace + 1) :
   404                 -1;
   405         } else {
   406             return delimEnd;
   407         }
   408     }
   410     /**
   411      * Recursively search for the characters '{', '@', followed by
   412      * name of inline tag and white space,
   413      * if found
   414      *    return the index of the text following the white space.
   415      * else
   416      *    return -1.
   417      */
   418     private static int inlineTagFound(DocImpl holder, String inlinetext, int start) {
   419         DocEnv docenv = holder.env;
   420         int linkstart = inlinetext.indexOf("{@", start);
   421         if (start == inlinetext.length() || linkstart == -1) {
   422             return -1;
   423         } else if (inlinetext.indexOf('}', linkstart) == -1) {
   424             //Missing '}'.
   425             docenv.warning(holder, "tag.Improper_Use_Of_Link_Tag",
   426                     inlinetext.substring(linkstart, inlinetext.length()));
   427             return -1;
   428         } else {
   429             return linkstart;
   430         }
   431     }
   434     /**
   435      * Return array of tags for the locale specific first sentence in the text.
   436      */
   437     static Tag[] firstSentenceTags(DocImpl holder, String text) {
   438         DocLocale doclocale = holder.env.doclocale;
   439         return getInlineTags(holder,
   440                              doclocale.localeSpecificFirstSentence(holder, text));
   441     }
   443     /**
   444      * Return text for this Doc comment.
   445      */
   446     @Override
   447     public String toString() {
   448         return text;
   449     }
   450 }

mercurial