langtools/src/share/classes/com/sun/tools/javadoc/Comment.java
changeset 10 06bc494ca11e
child 1870 57a1138dffc8
--- /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;
+    }
+}