blob: 178c17a97a4ff9a4350d52238adf73cf5c262cee [file] [log] [blame]
/*
* Copyright (C) 2010 Google Inc.
*
* 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 com.google.doclava;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.ArrayList;
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", "@samplecode", "@sample", "@include",
"@serial", "@com.intel.drl.spec_ref", "@ar.org.fitc.spec_ref",};
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, m.group(1), 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("@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("@include") || name.equals("@sample")) {
mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos));
} else {
boolean known = false;
for (String s : KNOWN_TAGS) {
if (s.equals(name)) {
known = true;
break;
}
}
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[] 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.generatingDocs()) {
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()]));
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;
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;
TagInfo[] mDeprecatedTags;
TagInfo[] mUndeprecateTags;
AttrTagInfo[] mAttrTags;
ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>();
ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>();
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>();
}