/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mule.devkit.doclet; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Comment { static final Pattern LEADING_WHITESPACE = Pattern.compile("^[ \t\n\r]*(.*)$", Pattern.DOTALL); static final Pattern TAG_BEGIN = Pattern.compile("[\r\n][\r\n \t]*@", Pattern.DOTALL); static final Pattern TAG = Pattern.compile("(@[^ \t\r\n]+)[ \t\r\n]+(.*)", Pattern.DOTALL); static final Pattern INLINE_TAG = Pattern.compile("(.*?)\\{(@[^ \t\r\n\\}]+)[ \t\r\n]*(.*?)\\}", Pattern.DOTALL); static final Pattern FIRST_SENTENCE = Pattern.compile("((.*?)\\.)[ \t\r\n\\<](.*)", Pattern.DOTALL); private static final String[] KNOWN_TAGS = new String[]{ "@author", "@since", "@version", "@deprecated", "@undeprecate", "@docRoot", "@sdkCurrent", "@inheritDoc", "@more", "@sample.xml", "@sample.java", "@include", "@serial", }; public Comment(String text, ContainerInfo base, SourcePositionInfo sp) { mText = text; mBase = base; // sp now points to the end of the text, not the beginning! mPosition = SourcePositionInfo.findBeginning(sp, text); } private void parseRegex(String text) { Matcher m; m = LEADING_WHITESPACE.matcher(text); m.matches(); text = m.group(1); m = TAG_BEGIN.matcher(text); int start = 0; int end = 0; while (m.find()) { end = m.start(); tag(text, start, end); start = m.end() - 1; // -1 is the @ } end = text.length(); tag(text, start, end); } private void tag(String text, int start, int end) { SourcePositionInfo pos = SourcePositionInfo.add(mPosition, mText, start); if (start >= 0 && end > 0 && (end - start) > 0) { text = text.substring(start, end); Matcher m = TAG.matcher(text); if (m.matches()) { // out of line tag tag(m.group(1), m.group(2), false, pos); } else { // look for inline tags m = INLINE_TAG.matcher(text); start = 0; while (m.find()) { String str = m.group(1); String tagname = m.group(2); String tagvalue = m.group(3); tag(null, str, true, pos); tag(tagname, tagvalue, true, pos); start = m.end(); } int len = text.length(); if (start != len) { tag(null, text.substring(start), true, pos); } } } } private void tag(String name, String text, boolean isInline, SourcePositionInfo pos) { /* * String s = isInline ? "inline" : "outofline"; System.out.println("---> " + s + " name=[" + * name + "] text=[" + text + "]"); */ if (name == null) { mInlineTagsList.add(new TextTagInfo("Text", "Text", text, pos)); } else if (name.equals("@param")) { mParamTagsList.add(new ParamTagInfo("@param", "@param", text, mBase, pos)); } else if (name.equals("@see")) { mSeeTagsList.add(new SeeTagInfo("@see", "@see", text, mBase, pos)); } else if (name.equals("@link") || name.equals("@linkplain")) { mInlineTagsList.add(new SeeTagInfo(name, "@see", text, mBase, pos)); } else if (name.equals("@api.doc")) { mAPITagsList.add(new SeeTagInfo(name, "@api.doc", text, mBase, pos)); } else if (name.equals("@throws") || name.equals("@exception")) { mThrowsTagsList.add(new ThrowsTagInfo("@throws", "@throws", text, mBase, pos)); } else if (name.equals("@return")) { mReturnTagsList.add(new ParsedTagInfo("@return", "@return", text, mBase, pos)); } else if (name.equals("@deprecated")) { if (text.length() == 0) { Errors.error(Errors.MISSING_COMMENT, pos, "@deprecated tag with no explanatory comment"); text = "No replacement."; } mDeprecatedTagsList.add(new ParsedTagInfo("@deprecated", "@deprecated", text, mBase, pos)); } else if (name.equals("@literal")) { mInlineTagsList.add(new LiteralTagInfo(text, pos)); } else if (name.equals("@code")) { mInlineTagsList.add(new CodeTagInfo(text, pos)); } else if (name.equals("@hide") || name.equals("@pending") || name.equals("@doconly")) { // nothing } else if (name.equals("@attr")) { AttrTagInfo tag = new AttrTagInfo("@attr", "@attr", text, mBase, pos); mAttrTagsList.add(tag); Comment c = tag.description(); if (c != null) { for (TagInfo t : c.tags()) { mInlineTagsList.add(t); } } } else if (name.equals("@undeprecate")) { mUndeprecateTagsList.add(new TextTagInfo("@undeprecate", "@undeprecate", text, pos)); } else if (name.equals("@sample.java")) { mInlineTagsList.add(new SampleTagInfo(name, "@include", text, SampleTagInfo.SampleLanguage.JAVA, mBase, pos)); } else if (name.equals("@sample.xml")) { mInlineTagsList.add(new SampleTagInfo(name, "@include", text, SampleTagInfo.SampleLanguage.XML, mBase, pos)); } else if (name.equals("@sample.config")) { mInlineTagsList.add(new SampleTagInfo(name, "@include", text, SampleTagInfo.SampleLanguage.XML, mBase, pos)); } else { boolean known = false; for (String s : KNOWN_TAGS) { if (s.equals(name)) { known = true; break; } } if (!known) { known = Doclava.knownTags.contains(name); } if (!known) { Errors.error(Errors.UNKNOWN_TAG, pos == null ? null : new SourcePositionInfo(pos), "Unknown tag: " + name); } TagInfo t = new TextTagInfo(name, name, text, pos); if (isInline) { mInlineTagsList.add(t); } else { mTagsList.add(t); } } } private void parseBriefTags() { int N = mInlineTagsList.size(); // look for "@more" tag, which means that we might go past the first sentence. int more = -1; for (int i = 0; i < N; i++) { if (mInlineTagsList.get(i).name().equals("@more")) { more = i; } } if (more >= 0) { for (int i = 0; i < more; i++) { mBriefTagsList.add(mInlineTagsList.get(i)); } } else { for (int i = 0; i < N; i++) { TagInfo t = mInlineTagsList.get(i); if (t.name().equals("Text")) { Matcher m = FIRST_SENTENCE.matcher(t.text()); if (m.matches()) { String text = m.group(1); TagInfo firstSentenceTag = new TagInfo(t.name(), t.kind(), text, t.position()); mBriefTagsList.add(firstSentenceTag); break; } } mBriefTagsList.add(t); } } } public TagInfo[] tags() { init(); return mInlineTags; } public TagInfo[] tags(String name) { init(); ArrayList<TagInfo> results = new ArrayList<TagInfo>(); int N = mInlineTagsList.size(); for (int i = 0; i < N; i++) { TagInfo t = mInlineTagsList.get(i); if (t.name().equals(name)) { results.add(t); } } return results.toArray(new TagInfo[results.size()]); } public ParamTagInfo[] paramTags() { init(); return mParamTags; } public SeeTagInfo[] seeTags() { init(); return mSeeTags; } public ThrowsTagInfo[] throwsTags() { init(); return mThrowsTags; } public TagInfo[] returnTags() { init(); return mReturnTags; } public TagInfo[] apiTags() { init(); return mAPITags; } public TagInfo[] deprecatedTags() { init(); return mDeprecatedTags; } public TagInfo[] undeprecateTags() { init(); return mUndeprecateTags; } public AttrTagInfo[] attrTags() { init(); return mAttrTags; } public TagInfo[] briefTags() { init(); return mBriefTags; } public boolean isHidden() { if (mHidden != -1) { return mHidden != 0; } else { if (Doclava.checkLevel(Doclava.SHOW_HIDDEN)) { mHidden = 0; return false; } boolean b = mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0; mHidden = b ? 1 : 0; return b; } } public boolean isDocOnly() { if (mDocOnly != -1) { return mDocOnly != 0; } else { boolean b = (mText != null) && (mText.indexOf("@doconly") >= 0); mDocOnly = b ? 1 : 0; return b; } } public boolean isDeprecated() { if (mDeprecated != -1) { return mDeprecated != 0; } else { boolean b = (mText != null) && (mText.indexOf("@deprecated") >= 0); mDeprecated = b ? 1 : 0; return b; } } private void init() { if (!mInitialized) { initImpl(); } } private void initImpl() { isHidden(); isDocOnly(); isDeprecated(); // Don't bother parsing text if we aren't generating documentation. if (Doclava.parseComments()) { parseRegex(mText); parseBriefTags(); } else { // Forces methods to be recognized by findOverriddenMethods in MethodInfo. mInlineTagsList.add(new TextTagInfo("Text", "Text", mText, SourcePositionInfo.add(mPosition, mText, 0))); } mText = null; mInitialized = true; mInlineTags = mInlineTagsList.toArray(new TagInfo[mInlineTagsList.size()]); mParamTags = mParamTagsList.toArray(new ParamTagInfo[mParamTagsList.size()]); mSeeTags = mSeeTagsList.toArray(new SeeTagInfo[mSeeTagsList.size()]); mThrowsTags = mThrowsTagsList.toArray(new ThrowsTagInfo[mThrowsTagsList.size()]); mReturnTags = ParsedTagInfo.joinTags(mReturnTagsList.toArray(new ParsedTagInfo[mReturnTagsList.size()])); mAPITags = mAPITagsList.toArray(new SeeTagInfo[mAPITagsList.size()]); mDeprecatedTags = ParsedTagInfo.joinTags(mDeprecatedTagsList.toArray(new ParsedTagInfo[mDeprecatedTagsList .size()])); mUndeprecateTags = mUndeprecateTagsList.toArray(new TagInfo[mUndeprecateTagsList.size()]); mAttrTags = mAttrTagsList.toArray(new AttrTagInfo[mAttrTagsList.size()]); mBriefTags = mBriefTagsList.toArray(new TagInfo[mBriefTagsList.size()]); mParamTagsList = null; mSeeTagsList = null; mThrowsTagsList = null; mAPITagsList = null; mReturnTagsList = null; mDeprecatedTagsList = null; mUndeprecateTagsList = null; mAttrTagsList = null; mBriefTagsList = null; } boolean mInitialized; int mHidden = -1; int mDocOnly = -1; int mDeprecated = -1; String mText; ContainerInfo mBase; SourcePositionInfo mPosition; int mLine = 1; TagInfo[] mInlineTags; TagInfo[] mTags; ParamTagInfo[] mParamTags; SeeTagInfo[] mSeeTags; ThrowsTagInfo[] mThrowsTags; TagInfo[] mBriefTags; TagInfo[] mReturnTags; SeeTagInfo[] mAPITags; TagInfo[] mDeprecatedTags; TagInfo[] mUndeprecateTags; AttrTagInfo[] mAttrTags; ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>(); ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>(); ArrayList<SeeTagInfo> mAPITagsList = new ArrayList<SeeTagInfo>(); ArrayList<ParamTagInfo> mParamTagsList = new ArrayList<ParamTagInfo>(); ArrayList<SeeTagInfo> mSeeTagsList = new ArrayList<SeeTagInfo>(); ArrayList<ThrowsTagInfo> mThrowsTagsList = new ArrayList<ThrowsTagInfo>(); ArrayList<TagInfo> mBriefTagsList = new ArrayList<TagInfo>(); ArrayList<ParsedTagInfo> mReturnTagsList = new ArrayList<ParsedTagInfo>(); ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>(); ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>(); ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>(); }