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();
    }