Path: blob/aarch64-shenandoah-jdk8u272-b10/langtools/src/share/classes/com/sun/tools/javadoc/Comment.java
38899 views
/*1* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.tools.javadoc;2627import java.util.regex.Matcher;28import java.util.regex.Pattern;29import com.sun.javadoc.*;30import com.sun.tools.javac.util.ListBuffer;3132/**33* Comment contains all information in comment part.34* It allows users to get first sentence of this comment, get35* comment for different tags...36*37* <p><b>This is NOT part of any supported API.38* If you write code that depends on this, you do so at your own risk.39* This code and its internal interfaces are subject to change or40* deletion without notice.</b>41*42* @author Kaiyang Liu (original)43* @author Robert Field (rewrite)44* @author Atul M Dambalkar45* @author Neal Gafter (rewrite)46*/47class Comment {4849/**50* sorted comments with different tags.51*/52private final ListBuffer<Tag> tagList = new ListBuffer<Tag>();5354/**55* text minus any tags.56*/57private String text;5859/**60* Doc environment61*/62private final DocEnv docenv;6364/**65* constructor of Comment.66*/67Comment(final DocImpl holder, final String commentString) {68this.docenv = holder.env;6970/**71* Separate the comment into the text part and zero to N tags.72* Simple state machine is in one of three states:73* <pre>74* IN_TEXT: parsing the comment text or tag text.75* TAG_NAME: parsing the name of a tag.76* TAG_GAP: skipping through the gap between the tag name and77* the tag text.78* </pre>79*/80@SuppressWarnings("fallthrough")81class CommentStringParser {82/**83* The entry point to the comment string parser84*/85void parseCommentStateMachine() {86final int IN_TEXT = 1;87final int TAG_GAP = 2;88final int TAG_NAME = 3;89int state = TAG_GAP;90boolean newLine = true;91String tagName = null;92int tagStart = 0;93int textStart = 0;94int lastNonWhite = -1;95int len = commentString.length();96for (int inx = 0; inx < len; ++inx) {97char ch = commentString.charAt(inx);98boolean isWhite = Character.isWhitespace(ch);99switch (state) {100case TAG_NAME:101if (isWhite) {102tagName = commentString.substring(tagStart, inx);103state = TAG_GAP;104}105break;106case TAG_GAP:107if (isWhite) {108break;109}110textStart = inx;111state = IN_TEXT;112/* fall thru */113case IN_TEXT:114if (newLine && ch == '@') {115parseCommentComponent(tagName, textStart,116lastNonWhite+1);117tagStart = inx;118state = TAG_NAME;119}120break;121}122if (ch == '\n') {123newLine = true;124} else if (!isWhite) {125lastNonWhite = inx;126newLine = false;127}128}129// Finish what's currently being processed130switch (state) {131case TAG_NAME:132tagName = commentString.substring(tagStart, len);133/* fall thru */134case TAG_GAP:135textStart = len;136/* fall thru */137case IN_TEXT:138parseCommentComponent(tagName, textStart, lastNonWhite+1);139break;140}141}142143/**144* Save away the last parsed item.145*/146void parseCommentComponent(String tagName,147int from, int upto) {148String tx = upto <= from ? "" : commentString.substring(from, upto);149if (tagName == null) {150text = tx;151} else {152TagImpl tag;153if (tagName.equals("@exception") || tagName.equals("@throws")) {154warnIfEmpty(tagName, tx);155tag = new ThrowsTagImpl(holder, tagName, tx);156} else if (tagName.equals("@param")) {157warnIfEmpty(tagName, tx);158tag = new ParamTagImpl(holder, tagName, tx);159} else if (tagName.equals("@see")) {160warnIfEmpty(tagName, tx);161tag = new SeeTagImpl(holder, tagName, tx);162} else if (tagName.equals("@serialField")) {163warnIfEmpty(tagName, tx);164tag = new SerialFieldTagImpl(holder, tagName, tx);165} else if (tagName.equals("@return")) {166warnIfEmpty(tagName, tx);167tag = new TagImpl(holder, tagName, tx);168} else if (tagName.equals("@author")) {169warnIfEmpty(tagName, tx);170tag = new TagImpl(holder, tagName, tx);171} else if (tagName.equals("@version")) {172warnIfEmpty(tagName, tx);173tag = new TagImpl(holder, tagName, tx);174} else {175tag = new TagImpl(holder, tagName, tx);176}177tagList.append(tag);178}179}180181void warnIfEmpty(String tagName, String tx) {182if (tx.length() == 0) {183docenv.warning(holder, "tag.tag_has_no_arguments", tagName);184}185}186187}188189new CommentStringParser().parseCommentStateMachine();190}191192/**193* Return the text of the comment.194*/195String commentText() {196return text;197}198199/**200* Return all tags in this comment.201*/202Tag[] tags() {203return tagList.toArray(new Tag[tagList.length()]);204}205206/**207* Return tags of the specified kind in this comment.208*/209Tag[] tags(String tagname) {210ListBuffer<Tag> found = new ListBuffer<Tag>();211String target = tagname;212if (target.charAt(0) != '@') {213target = "@" + target;214}215for (Tag tag : tagList) {216if (tag.kind().equals(target)) {217found.append(tag);218}219}220return found.toArray(new Tag[found.length()]);221}222223/**224* Return throws tags in this comment.225*/226ThrowsTag[] throwsTags() {227ListBuffer<ThrowsTag> found = new ListBuffer<ThrowsTag>();228for (Tag next : tagList) {229if (next instanceof ThrowsTag) {230found.append((ThrowsTag)next);231}232}233return found.toArray(new ThrowsTag[found.length()]);234}235236/**237* Return param tags (excluding type param tags) in this comment.238*/239ParamTag[] paramTags() {240return paramTags(false);241}242243/**244* Return type param tags in this comment.245*/246ParamTag[] typeParamTags() {247return paramTags(true);248}249250/**251* Return param tags in this comment. If typeParams is true252* include only type param tags, otherwise include only ordinary253* param tags.254*/255private ParamTag[] paramTags(boolean typeParams) {256ListBuffer<ParamTag> found = new ListBuffer<ParamTag>();257for (Tag next : tagList) {258if (next instanceof ParamTag) {259ParamTag p = (ParamTag)next;260if (typeParams == p.isTypeParameter()) {261found.append(p);262}263}264}265return found.toArray(new ParamTag[found.length()]);266}267268/**269* Return see also tags in this comment.270*/271SeeTag[] seeTags() {272ListBuffer<SeeTag> found = new ListBuffer<SeeTag>();273for (Tag next : tagList) {274if (next instanceof SeeTag) {275found.append((SeeTag)next);276}277}278return found.toArray(new SeeTag[found.length()]);279}280281/**282* Return serialField tags in this comment.283*/284SerialFieldTag[] serialFieldTags() {285ListBuffer<SerialFieldTag> found = new ListBuffer<SerialFieldTag>();286for (Tag next : tagList) {287if (next instanceof SerialFieldTag) {288found.append((SerialFieldTag)next);289}290}291return found.toArray(new SerialFieldTag[found.length()]);292}293294/**295* Return array of tags with text and inline See Tags for a Doc comment.296*/297static Tag[] getInlineTags(DocImpl holder, String inlinetext) {298ListBuffer<Tag> taglist = new ListBuffer<Tag>();299int delimend = 0, textstart = 0, len = inlinetext.length();300boolean inPre = false;301DocEnv docenv = holder.env;302303if (len == 0) {304return taglist.toArray(new Tag[taglist.length()]);305}306while (true) {307int linkstart;308if ((linkstart = inlineTagFound(holder, inlinetext,309textstart)) == -1) {310taglist.append(new TagImpl(holder, "Text",311inlinetext.substring(textstart)));312break;313} else {314inPre = scanForPre(inlinetext, textstart, linkstart, inPre);315int seetextstart = linkstart;316for (int i = linkstart; i < inlinetext.length(); i++) {317char c = inlinetext.charAt(i);318if (Character.isWhitespace(c) ||319c == '}') {320seetextstart = i;321break;322}323}324String linkName = inlinetext.substring(linkstart+2, seetextstart);325if (!(inPre && (linkName.equals("code") || linkName.equals("literal")))) {326//Move past the white space after the inline tag name.327while (Character.isWhitespace(inlinetext.328charAt(seetextstart))) {329if (inlinetext.length() <= seetextstart) {330taglist.append(new TagImpl(holder, "Text",331inlinetext.substring(textstart, seetextstart)));332docenv.warning(holder,333"tag.Improper_Use_Of_Link_Tag",334inlinetext);335return taglist.toArray(new Tag[taglist.length()]);336} else {337seetextstart++;338}339}340}341taglist.append(new TagImpl(holder, "Text",342inlinetext.substring(textstart, linkstart)));343textstart = seetextstart; // this text is actually seetag344if ((delimend = findInlineTagDelim(inlinetext, textstart)) == -1) {345//Missing closing '}' character.346// store the text as it is with the {@link.347taglist.append(new TagImpl(holder, "Text",348inlinetext.substring(textstart)));349docenv.warning(holder,350"tag.End_delimiter_missing_for_possible_SeeTag",351inlinetext);352return taglist.toArray(new Tag[taglist.length()]);353} else {354//Found closing '}' character.355if (linkName.equals("see")356|| linkName.equals("link")357|| linkName.equals("linkplain")) {358taglist.append( new SeeTagImpl(holder, "@" + linkName,359inlinetext.substring(textstart, delimend)));360} else {361taglist.append( new TagImpl(holder, "@" + linkName,362inlinetext.substring(textstart, delimend)));363}364textstart = delimend + 1;365}366}367if (textstart == inlinetext.length()) {368break;369}370}371return taglist.toArray(new Tag[taglist.length()]);372}373374/** regex for case-insensitive match for {@literal <pre> } and {@literal </pre> }. */375private static final Pattern prePat = Pattern.compile("(?i)<(/?)pre>");376377private static boolean scanForPre(String inlinetext, int start, int end, boolean inPre) {378Matcher m = prePat.matcher(inlinetext).region(start, end);379while (m.find()) {380inPre = m.group(1).isEmpty();381}382return inPre;383}384385/**386* Recursively find the index of the closing '}' character for an inline tag387* and return it. If it can't be found, return -1.388* @param inlineText the text to search in.389* @param searchStart the index of the place to start searching at.390* @return the index of the closing '}' character for an inline tag.391* If it can't be found, return -1.392*/393private static int findInlineTagDelim(String inlineText, int searchStart) {394int delimEnd, nestedOpenBrace;395if ((delimEnd = inlineText.indexOf("}", searchStart)) == -1) {396return -1;397} else if (((nestedOpenBrace = inlineText.indexOf("{", searchStart)) != -1) &&398nestedOpenBrace < delimEnd){399//Found a nested open brace.400int nestedCloseBrace = findInlineTagDelim(inlineText, nestedOpenBrace + 1);401return (nestedCloseBrace != -1) ?402findInlineTagDelim(inlineText, nestedCloseBrace + 1) :403-1;404} else {405return delimEnd;406}407}408409/**410* Recursively search for the characters '{', '@', followed by411* name of inline tag and white space,412* if found413* return the index of the text following the white space.414* else415* return -1.416*/417private static int inlineTagFound(DocImpl holder, String inlinetext, int start) {418DocEnv docenv = holder.env;419int linkstart = inlinetext.indexOf("{@", start);420if (start == inlinetext.length() || linkstart == -1) {421return -1;422} else if (inlinetext.indexOf('}', linkstart) == -1) {423//Missing '}'.424docenv.warning(holder, "tag.Improper_Use_Of_Link_Tag",425inlinetext.substring(linkstart, inlinetext.length()));426return -1;427} else {428return linkstart;429}430}431432433/**434* Return array of tags for the locale specific first sentence in the text.435*/436static Tag[] firstSentenceTags(DocImpl holder, String text) {437DocLocale doclocale = holder.env.doclocale;438return getInlineTags(holder,439doclocale.localeSpecificFirstSentence(holder, text));440}441442/**443* Return text for this Doc comment.444*/445@Override446public String toString() {447return text;448}449}450451452