0N/A/*
1045N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
553N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
553N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
553N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
553N/A * or visit www.oracle.com if you need additional information or have any
553N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.tools.javadoc;
0N/A
0N/Aimport com.sun.javadoc.*;
0N/Aimport com.sun.tools.javac.util.ListBuffer;
0N/A
0N/A/**
0N/A * Comment contains all information in comment part.
0N/A * It allows users to get first sentence of this comment, get
0N/A * comment for different tags...
0N/A *
0N/A * @author Kaiyang Liu (original)
0N/A * @author Robert Field (rewrite)
0N/A * @author Atul M Dambalkar
0N/A * @author Neal Gafter (rewrite)
0N/A */
0N/Aclass Comment {
0N/A
0N/A /**
0N/A * sorted comments with different tags.
0N/A */
0N/A private final ListBuffer<Tag> tagList = new ListBuffer<Tag>();
0N/A
0N/A /**
0N/A * text minus any tags.
0N/A */
0N/A private String text;
0N/A
0N/A /**
0N/A * Doc environment
0N/A */
0N/A private final DocEnv docenv;
0N/A
0N/A /**
0N/A * constructor of Comment.
0N/A */
0N/A Comment(final DocImpl holder, final String commentString) {
0N/A this.docenv = holder.env;
0N/A
0N/A /**
0N/A * Separate the comment into the text part and zero to N tags.
0N/A * Simple state machine is in one of three states:
0N/A * <pre>
0N/A * IN_TEXT: parsing the comment text or tag text.
0N/A * TAG_NAME: parsing the name of a tag.
0N/A * TAG_GAP: skipping through the gap between the tag name and
0N/A * the tag text.
0N/A * </pre>
0N/A */
197N/A @SuppressWarnings("fallthrough")
0N/A class CommentStringParser {
0N/A /**
0N/A * The entry point to the comment string parser
0N/A */
0N/A void parseCommentStateMachine() {
0N/A final int IN_TEXT = 1;
0N/A final int TAG_GAP = 2;
0N/A final int TAG_NAME = 3;
0N/A int state = TAG_GAP;
0N/A boolean newLine = true;
0N/A String tagName = null;
0N/A int tagStart = 0;
0N/A int textStart = 0;
0N/A int lastNonWhite = -1;
0N/A int len = commentString.length();
0N/A for (int inx = 0; inx < len; ++inx) {
0N/A char ch = commentString.charAt(inx);
0N/A boolean isWhite = Character.isWhitespace(ch);
0N/A switch (state) {
0N/A case TAG_NAME:
0N/A if (isWhite) {
0N/A tagName = commentString.substring(tagStart, inx);
0N/A state = TAG_GAP;
0N/A }
0N/A break;
0N/A case TAG_GAP:
0N/A if (isWhite) {
0N/A break;
0N/A }
0N/A textStart = inx;
0N/A state = IN_TEXT;
0N/A /* fall thru */
0N/A case IN_TEXT:
0N/A if (newLine && ch == '@') {
0N/A parseCommentComponent(tagName, textStart,
0N/A lastNonWhite+1);
0N/A tagStart = inx;
0N/A state = TAG_NAME;
0N/A }
0N/A break;
1045N/A }
0N/A if (ch == '\n') {
0N/A newLine = true;
0N/A } else if (!isWhite) {
0N/A lastNonWhite = inx;
0N/A newLine = false;
0N/A }
0N/A }
0N/A // Finish what's currently being processed
0N/A switch (state) {
0N/A case TAG_NAME:
0N/A tagName = commentString.substring(tagStart, len);
0N/A /* fall thru */
0N/A case TAG_GAP:
0N/A textStart = len;
0N/A /* fall thru */
0N/A case IN_TEXT:
0N/A parseCommentComponent(tagName, textStart, lastNonWhite+1);
0N/A break;
1045N/A }
0N/A }
0N/A
0N/A /**
0N/A * Save away the last parsed item.
0N/A */
0N/A void parseCommentComponent(String tagName,
0N/A int from, int upto) {
0N/A String tx = upto <= from ? "" : commentString.substring(from, upto);
0N/A if (tagName == null) {
0N/A text = tx;
0N/A } else {
0N/A TagImpl tag;
0N/A if (tagName.equals("@exception") || tagName.equals("@throws")) {
0N/A warnIfEmpty(tagName, tx);
0N/A tag = new ThrowsTagImpl(holder, tagName, tx);
0N/A } else if (tagName.equals("@param")) {
0N/A warnIfEmpty(tagName, tx);
0N/A tag = new ParamTagImpl(holder, tagName, tx);
0N/A } else if (tagName.equals("@see")) {
0N/A warnIfEmpty(tagName, tx);
0N/A tag = new SeeTagImpl(holder, tagName, tx);
0N/A } else if (tagName.equals("@serialField")) {
0N/A warnIfEmpty(tagName, tx);
0N/A tag = new SerialFieldTagImpl(holder, tagName, tx);
0N/A } else if (tagName.equals("@return")) {
0N/A warnIfEmpty(tagName, tx);
0N/A tag = new TagImpl(holder, tagName, tx);
0N/A } else if (tagName.equals("@author")) {
0N/A warnIfEmpty(tagName, tx);
0N/A tag = new TagImpl(holder, tagName, tx);
0N/A } else if (tagName.equals("@version")) {
0N/A warnIfEmpty(tagName, tx);
0N/A tag = new TagImpl(holder, tagName, tx);
0N/A } else {
0N/A tag = new TagImpl(holder, tagName, tx);
0N/A }
0N/A tagList.append(tag);
0N/A }
0N/A }
0N/A
0N/A void warnIfEmpty(String tagName, String tx) {
0N/A if (tx.length() == 0) {
0N/A docenv.warning(holder, "tag.tag_has_no_arguments", tagName);
0N/A }
0N/A }
0N/A
0N/A }
0N/A
0N/A new CommentStringParser().parseCommentStateMachine();
0N/A }
0N/A
0N/A /**
0N/A * Return the text of the comment.
0N/A */
0N/A String commentText() {
0N/A return text;
0N/A }
0N/A
0N/A /**
0N/A * Return all tags in this comment.
0N/A */
0N/A Tag[] tags() {
0N/A return tagList.toArray(new Tag[tagList.length()]);
0N/A }
0N/A
0N/A /**
0N/A * Return tags of the specified kind in this comment.
0N/A */
0N/A Tag[] tags(String tagname) {
0N/A ListBuffer<Tag> found = new ListBuffer<Tag>();
0N/A String target = tagname;
0N/A if (target.charAt(0) != '@') {
0N/A target = "@" + target;
0N/A }
0N/A for (Tag tag : tagList) {
0N/A if (tag.kind().equals(target)) {
0N/A found.append(tag);
0N/A }
0N/A }
0N/A return found.toArray(new Tag[found.length()]);
0N/A }
0N/A
0N/A /**
0N/A * Return throws tags in this comment.
0N/A */
0N/A ThrowsTag[] throwsTags() {
0N/A ListBuffer<ThrowsTag> found = new ListBuffer<ThrowsTag>();
0N/A for (Tag next : tagList) {
0N/A if (next instanceof ThrowsTag) {
0N/A found.append((ThrowsTag)next);
0N/A }
0N/A }
0N/A return found.toArray(new ThrowsTag[found.length()]);
0N/A }
0N/A
0N/A /**
0N/A * Return param tags (excluding type param tags) in this comment.
0N/A */
0N/A ParamTag[] paramTags() {
0N/A return paramTags(false);
0N/A }
0N/A
0N/A /**
0N/A * Return type param tags in this comment.
0N/A */
0N/A ParamTag[] typeParamTags() {
0N/A return paramTags(true);
0N/A }
0N/A
0N/A /**
0N/A * Return param tags in this comment. If typeParams is true
0N/A * include only type param tags, otherwise include only ordinary
0N/A * param tags.
0N/A */
0N/A private ParamTag[] paramTags(boolean typeParams) {
0N/A ListBuffer<ParamTag> found = new ListBuffer<ParamTag>();
0N/A for (Tag next : tagList) {
0N/A if (next instanceof ParamTag) {
0N/A ParamTag p = (ParamTag)next;
0N/A if (typeParams == p.isTypeParameter()) {
0N/A found.append(p);
0N/A }
0N/A }
0N/A }
0N/A return found.toArray(new ParamTag[found.length()]);
0N/A }
0N/A
0N/A /**
0N/A * Return see also tags in this comment.
0N/A */
0N/A SeeTag[] seeTags() {
0N/A ListBuffer<SeeTag> found = new ListBuffer<SeeTag>();
0N/A for (Tag next : tagList) {
0N/A if (next instanceof SeeTag) {
0N/A found.append((SeeTag)next);
0N/A }
0N/A }
0N/A return found.toArray(new SeeTag[found.length()]);
0N/A }
0N/A
0N/A /**
0N/A * Return serialField tags in this comment.
0N/A */
0N/A SerialFieldTag[] serialFieldTags() {
0N/A ListBuffer<SerialFieldTag> found = new ListBuffer<SerialFieldTag>();
0N/A for (Tag next : tagList) {
0N/A if (next instanceof SerialFieldTag) {
0N/A found.append((SerialFieldTag)next);
0N/A }
0N/A }
0N/A return found.toArray(new SerialFieldTag[found.length()]);
0N/A }
0N/A
0N/A /**
0N/A * Return array of tags with text and inline See Tags for a Doc comment.
0N/A */
0N/A static Tag[] getInlineTags(DocImpl holder, String inlinetext) {
0N/A ListBuffer<Tag> taglist = new ListBuffer<Tag>();
0N/A int delimend = 0, textstart = 0, len = inlinetext.length();
0N/A DocEnv docenv = holder.env;
0N/A
0N/A if (len == 0) {
0N/A return taglist.toArray(new Tag[taglist.length()]);
0N/A }
0N/A while (true) {
0N/A int linkstart;
0N/A if ((linkstart = inlineTagFound(holder, inlinetext,
0N/A textstart)) == -1) {
0N/A taglist.append(new TagImpl(holder, "Text",
0N/A inlinetext.substring(textstart)));
0N/A break;
0N/A } else {
0N/A int seetextstart = linkstart;
0N/A for (int i = linkstart; i < inlinetext.length(); i++) {
0N/A char c = inlinetext.charAt(i);
0N/A if (Character.isWhitespace(c) ||
0N/A c == '}') {
0N/A seetextstart = i;
0N/A break;
0N/A }
0N/A }
0N/A String linkName = inlinetext.substring(linkstart+2, seetextstart);
0N/A //Move past the white space after the inline tag name.
0N/A while (Character.isWhitespace(inlinetext.
0N/A charAt(seetextstart))) {
0N/A if (inlinetext.length() <= seetextstart) {
0N/A taglist.append(new TagImpl(holder, "Text",
0N/A inlinetext.substring(textstart, seetextstart)));
0N/A docenv.warning(holder,
0N/A "tag.Improper_Use_Of_Link_Tag",
0N/A inlinetext);
0N/A return taglist.toArray(new Tag[taglist.length()]);
0N/A } else {
0N/A seetextstart++;
0N/A }
0N/A }
0N/A taglist.append(new TagImpl(holder, "Text",
0N/A inlinetext.substring(textstart, linkstart)));
0N/A textstart = seetextstart; // this text is actually seetag
0N/A if ((delimend = findInlineTagDelim(inlinetext, textstart)) == -1) {
0N/A //Missing closing '}' character.
0N/A // store the text as it is with the {@link.
0N/A taglist.append(new TagImpl(holder, "Text",
0N/A inlinetext.substring(textstart)));
0N/A docenv.warning(holder,
0N/A "tag.End_delimiter_missing_for_possible_SeeTag",
0N/A inlinetext);
0N/A return taglist.toArray(new Tag[taglist.length()]);
0N/A } else {
0N/A //Found closing '}' character.
0N/A if (linkName.equals("see")
0N/A || linkName.equals("link")
0N/A || linkName.equals("linkplain")) {
0N/A taglist.append( new SeeTagImpl(holder, "@" + linkName,
0N/A inlinetext.substring(textstart, delimend)));
0N/A } else {
0N/A taglist.append( new TagImpl(holder, "@" + linkName,
0N/A inlinetext.substring(textstart, delimend)));
0N/A }
0N/A textstart = delimend + 1;
0N/A }
0N/A }
0N/A if (textstart == inlinetext.length()) {
0N/A break;
0N/A }
0N/A }
0N/A return taglist.toArray(new Tag[taglist.length()]);
0N/A }
0N/A
0N/A /**
0N/A * Recursively find the index of the closing '}' character for an inline tag
0N/A * and return it. If it can't be found, return -1.
0N/A * @param inlineText the text to search in.
0N/A * @param searchStart the index of the place to start searching at.
0N/A * @return the index of the closing '}' character for an inline tag.
0N/A * If it can't be found, return -1.
0N/A */
0N/A private static int findInlineTagDelim(String inlineText, int searchStart) {
0N/A int delimEnd, nestedOpenBrace;
0N/A if ((delimEnd = inlineText.indexOf("}", searchStart)) == -1) {
0N/A return -1;
0N/A } else if (((nestedOpenBrace = inlineText.indexOf("{", searchStart)) != -1) &&
0N/A nestedOpenBrace < delimEnd){
0N/A //Found a nested open brace.
0N/A int nestedCloseBrace = findInlineTagDelim(inlineText, nestedOpenBrace + 1);
0N/A return (nestedCloseBrace != -1) ?
0N/A findInlineTagDelim(inlineText, nestedCloseBrace + 1) :
0N/A -1;
0N/A } else {
0N/A return delimEnd;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Recursively search for the string "{@" followed by
0N/A * name of inline tag and white space,
0N/A * if found
0N/A * return the index of the text following the white space.
0N/A * else
0N/A * return -1.
0N/A */
1045N/A private static int inlineTagFound(DocImpl holder, String inlinetext, int start) {
0N/A DocEnv docenv = holder.env;
1045N/A int linkstart = inlinetext.indexOf("{@", start);
1045N/A if (start == inlinetext.length() || linkstart == -1) {
0N/A return -1;
1045N/A } else if (inlinetext.indexOf('}', linkstart) == -1) {
0N/A //Missing '}'.
0N/A docenv.warning(holder, "tag.Improper_Use_Of_Link_Tag",
1045N/A inlinetext.substring(linkstart, inlinetext.length()));
0N/A return -1;
0N/A } else {
0N/A return linkstart;
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Return array of tags for the locale specific first sentence in the text.
0N/A */
0N/A static Tag[] firstSentenceTags(DocImpl holder, String text) {
0N/A DocLocale doclocale = holder.env.doclocale;
0N/A return getInlineTags(holder,
0N/A doclocale.localeSpecificFirstSentence(holder, text));
0N/A }
0N/A
0N/A /**
0N/A * Return text for this Doc comment.
0N/A */
1045N/A @Override
0N/A public String toString() {
0N/A return text;
0N/A }
0N/A}