duke@1: /* jjg@1722: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javadoc; duke@1: jjg@1722: import java.util.regex.Matcher; jjg@1722: import java.util.regex.Pattern; duke@1: import com.sun.javadoc.*; duke@1: import com.sun.tools.javac.util.ListBuffer; duke@1: duke@1: /** duke@1: * Comment contains all information in comment part. duke@1: * It allows users to get first sentence of this comment, get duke@1: * comment for different tags... duke@1: * jjg@1359: *
This is NOT part of any supported API.
jjg@1359: * If you write code that depends on this, you do so at your own risk.
jjg@1359: * This code and its internal interfaces are subject to change or
jjg@1359: * deletion without notice.
jjg@1359: *
duke@1: * @author Kaiyang Liu (original)
duke@1: * @author Robert Field (rewrite)
duke@1: * @author Atul M Dambalkar
duke@1: * @author Neal Gafter (rewrite)
duke@1: */
duke@1: class Comment {
duke@1:
duke@1: /**
duke@1: * sorted comments with different tags.
duke@1: */
duke@1: private final ListBuffer
duke@1: * IN_TEXT: parsing the comment text or tag text.
duke@1: * TAG_NAME: parsing the name of a tag.
duke@1: * TAG_GAP: skipping through the gap between the tag name and
duke@1: * the tag text.
duke@1: *
duke@1: */
jjg@198: @SuppressWarnings("fallthrough")
duke@1: class CommentStringParser {
duke@1: /**
duke@1: * The entry point to the comment string parser
duke@1: */
duke@1: void parseCommentStateMachine() {
duke@1: final int IN_TEXT = 1;
duke@1: final int TAG_GAP = 2;
duke@1: final int TAG_NAME = 3;
duke@1: int state = TAG_GAP;
duke@1: boolean newLine = true;
duke@1: String tagName = null;
duke@1: int tagStart = 0;
duke@1: int textStart = 0;
duke@1: int lastNonWhite = -1;
duke@1: int len = commentString.length();
duke@1: for (int inx = 0; inx < len; ++inx) {
duke@1: char ch = commentString.charAt(inx);
duke@1: boolean isWhite = Character.isWhitespace(ch);
duke@1: switch (state) {
duke@1: case TAG_NAME:
duke@1: if (isWhite) {
duke@1: tagName = commentString.substring(tagStart, inx);
duke@1: state = TAG_GAP;
duke@1: }
duke@1: break;
duke@1: case TAG_GAP:
duke@1: if (isWhite) {
duke@1: break;
duke@1: }
duke@1: textStart = inx;
duke@1: state = IN_TEXT;
duke@1: /* fall thru */
duke@1: case IN_TEXT:
duke@1: if (newLine && ch == '@') {
duke@1: parseCommentComponent(tagName, textStart,
duke@1: lastNonWhite+1);
duke@1: tagStart = inx;
duke@1: state = TAG_NAME;
duke@1: }
duke@1: break;
ksrini@1065: }
duke@1: if (ch == '\n') {
duke@1: newLine = true;
duke@1: } else if (!isWhite) {
duke@1: lastNonWhite = inx;
duke@1: newLine = false;
duke@1: }
duke@1: }
duke@1: // Finish what's currently being processed
duke@1: switch (state) {
duke@1: case TAG_NAME:
duke@1: tagName = commentString.substring(tagStart, len);
duke@1: /* fall thru */
duke@1: case TAG_GAP:
duke@1: textStart = len;
duke@1: /* fall thru */
duke@1: case IN_TEXT:
duke@1: parseCommentComponent(tagName, textStart, lastNonWhite+1);
duke@1: break;
ksrini@1065: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Save away the last parsed item.
duke@1: */
duke@1: void parseCommentComponent(String tagName,
duke@1: int from, int upto) {
duke@1: String tx = upto <= from ? "" : commentString.substring(from, upto);
duke@1: if (tagName == null) {
duke@1: text = tx;
duke@1: } else {
duke@1: TagImpl tag;
duke@1: if (tagName.equals("@exception") || tagName.equals("@throws")) {
duke@1: warnIfEmpty(tagName, tx);
duke@1: tag = new ThrowsTagImpl(holder, tagName, tx);
duke@1: } else if (tagName.equals("@param")) {
duke@1: warnIfEmpty(tagName, tx);
duke@1: tag = new ParamTagImpl(holder, tagName, tx);
duke@1: } else if (tagName.equals("@see")) {
duke@1: warnIfEmpty(tagName, tx);
duke@1: tag = new SeeTagImpl(holder, tagName, tx);
duke@1: } else if (tagName.equals("@serialField")) {
duke@1: warnIfEmpty(tagName, tx);
duke@1: tag = new SerialFieldTagImpl(holder, tagName, tx);
duke@1: } else if (tagName.equals("@return")) {
duke@1: warnIfEmpty(tagName, tx);
duke@1: tag = new TagImpl(holder, tagName, tx);
duke@1: } else if (tagName.equals("@author")) {
duke@1: warnIfEmpty(tagName, tx);
duke@1: tag = new TagImpl(holder, tagName, tx);
duke@1: } else if (tagName.equals("@version")) {
duke@1: warnIfEmpty(tagName, tx);
duke@1: tag = new TagImpl(holder, tagName, tx);
duke@1: } else {
duke@1: tag = new TagImpl(holder, tagName, tx);
duke@1: }
duke@1: tagList.append(tag);
duke@1: }
duke@1: }
duke@1:
duke@1: void warnIfEmpty(String tagName, String tx) {
duke@1: if (tx.length() == 0) {
duke@1: docenv.warning(holder, "tag.tag_has_no_arguments", tagName);
duke@1: }
duke@1: }
duke@1:
duke@1: }
duke@1:
duke@1: new CommentStringParser().parseCommentStateMachine();
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the text of the comment.
duke@1: */
duke@1: String commentText() {
duke@1: return text;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return all tags in this comment.
duke@1: */
duke@1: Tag[] tags() {
duke@1: return tagList.toArray(new Tag[tagList.length()]);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return tags of the specified kind in this comment.
duke@1: */
duke@1: Tag[] tags(String tagname) {
duke@1: ListBuffer } and {@literal
}. */
jjg@1722: private static final Pattern prePat = Pattern.compile("(?i)<(/?)pre>");
jjg@1722:
jjg@1722: private static boolean scanForPre(String inlinetext, int start, int end, boolean inPre) {
jjg@1722: Matcher m = prePat.matcher(inlinetext).region(start, end);
jjg@1722: while (m.find()) {
jjg@1722: inPre = m.group(1).isEmpty();
jjg@1722: }
jjg@1722: return inPre;
jjg@1722: }
jjg@1722:
duke@1: /**
duke@1: * Recursively find the index of the closing '}' character for an inline tag
duke@1: * and return it. If it can't be found, return -1.
duke@1: * @param inlineText the text to search in.
duke@1: * @param searchStart the index of the place to start searching at.
duke@1: * @return the index of the closing '}' character for an inline tag.
duke@1: * If it can't be found, return -1.
duke@1: */
duke@1: private static int findInlineTagDelim(String inlineText, int searchStart) {
duke@1: int delimEnd, nestedOpenBrace;
duke@1: if ((delimEnd = inlineText.indexOf("}", searchStart)) == -1) {
duke@1: return -1;
duke@1: } else if (((nestedOpenBrace = inlineText.indexOf("{", searchStart)) != -1) &&
duke@1: nestedOpenBrace < delimEnd){
duke@1: //Found a nested open brace.
duke@1: int nestedCloseBrace = findInlineTagDelim(inlineText, nestedOpenBrace + 1);
duke@1: return (nestedCloseBrace != -1) ?
duke@1: findInlineTagDelim(inlineText, nestedCloseBrace + 1) :
duke@1: -1;
duke@1: } else {
duke@1: return delimEnd;
duke@1: }
duke@1: }
duke@1:
duke@1: /**
jjg@1326: * Recursively search for the characters '{', '@', followed by
duke@1: * name of inline tag and white space,
duke@1: * if found
duke@1: * return the index of the text following the white space.
duke@1: * else
duke@1: * return -1.
duke@1: */
ksrini@1065: private static int inlineTagFound(DocImpl holder, String inlinetext, int start) {
duke@1: DocEnv docenv = holder.env;
ksrini@1065: int linkstart = inlinetext.indexOf("{@", start);
ksrini@1065: if (start == inlinetext.length() || linkstart == -1) {
duke@1: return -1;
ksrini@1065: } else if (inlinetext.indexOf('}', linkstart) == -1) {
duke@1: //Missing '}'.
duke@1: docenv.warning(holder, "tag.Improper_Use_Of_Link_Tag",
ksrini@1065: inlinetext.substring(linkstart, inlinetext.length()));
duke@1: return -1;
duke@1: } else {
duke@1: return linkstart;
duke@1: }
duke@1: }
duke@1:
duke@1:
duke@1: /**
duke@1: * Return array of tags for the locale specific first sentence in the text.
duke@1: */
duke@1: static Tag[] firstSentenceTags(DocImpl holder, String text) {
duke@1: DocLocale doclocale = holder.env.doclocale;
duke@1: return getInlineTags(holder,
duke@1: doclocale.localeSpecificFirstSentence(holder, text));
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return text for this Doc comment.
duke@1: */
ksrini@1065: @Override
duke@1: public String toString() {
duke@1: return text;
duke@1: }
duke@1: }