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