Auto-generate docs based on annotations. am: d85b5c88af
am: 361cf77394
Change-Id: I1aeb80c6769094995055e9c5790ab9cccbd21432
diff --git a/res/assets/templates/macros.cs b/res/assets/templates/macros.cs
index 3c8b4bd..ade9d2e 100644
--- a/res/assets/templates/macros.cs
+++ b/res/assets/templates/macros.cs
@@ -168,6 +168,41 @@
/each ?><?cs
/def ?><?cs
+# Print output for aux tags that are not "standard" javadoc tags ?><?cs
+def:aux_tag_list(tags) ?><?cs
+ each:tag = tags ?><p><?cs
+ if:tag.kind == "@memberDoc" ?><?cs call:tag_list(tag.commentTags) ?><?cs
+ elif:tag.kind == "@paramDoc" ?><?cs call:tag_list(tag.commentTags) ?><?cs
+ elif:tag.kind == "@returnDoc" ?><?cs call:tag_list(tag.commentTags) ?><?cs
+ elif:tag.kind == "@range" ?><?cs call:dump_range(tag) ?><?cs
+ elif:tag.kind == "@intDef" ?><?cs call:dump_int_def(tag) ?><?cs
+ /if ?><?cs
+ /each ?></p><?cs
+/def ?><?cs
+
+# Print output for @range tags ?><?cs
+def:dump_range(tag) ?><?cs
+ if:tag.from && tag.to ?>Value is between <?cs var:tag.from ?> and <?cs var:tag.to ?> inclusive.<?cs
+ elif:tag.from ?>Value is <?cs var:tag.from ?> or greater.<?cs
+ elif:tag.to ?>Value is <?cs var:tag.to ?> or less.<?cs
+ /if ?><?cs
+/def ?><?cs
+
+# Print output for @intDef tags ?><?cs
+def:dump_int_def(tag) ?><?cs
+ if:tag.flag ?>Value is either <code>0</code> or combination of <?cs
+ else ?>Value is <?cs
+ /if ?><?cs
+ loop:i = #0, subcount(tag.values), #1 ?><?cs
+ with:val = tag.values[i] ?><?cs
+ call:tag_list(val.commentTags) ?><?cs
+ if i == subcount(tag.values) - 2 ?>, or <?cs
+ elif:i < subcount(tag.values) - 2 ?>, <?cs
+ /if ?><?cs
+ /with ?><?cs
+ /loop ?>.<?cs
+/def ?><?cs
+
# Show the short-form description of something. These come from shortDescr and deprecated ?><?cs
def:short_descr(obj) ?><?cs
if:subcount(obj.deprecated) ?><em><?cs
@@ -290,6 +325,7 @@
def:description(obj) ?><?cs
call:deprecated_warning(obj) ?>
<p><?cs call:tag_list(obj.descr) ?></p><?cs
+ call:aux_tag_list(obj.descrAux) ?><?cs
if:subcount(obj.annotationdocumentation)?><?cs
each:annodoc=obj.annotationdocumentation ?>
<div style="display:block"><?cs var:annodoc.text?></div><?cs
@@ -323,8 +359,9 @@
<code><?cs var:param.kind ?></code><?cs
if:string.find(param.comment.0.text, "<!--") != 0
?>:<?cs # Do not print if param comment is an HTML comment ?><?cs
- /if ?>
- <?cs call:tag_list(param.comment) ?></td>
+ /if ?><?cs
+ call:tag_list(param.comment) ?><?cs
+ call:aux_tag_list(param.commentAux) ?></td>
</tr><?cs
/each ?>
</table><?cs
@@ -333,24 +370,17 @@
# Print the @return value
#
?><?cs
- if:subcount(obj.returns) ?>
+ if:subcount(obj.returns) || (subcount(method.returnType) && method.returnType.label != 'void') ?>
<table class="responsive">
<tr><th colspan=2>Returns</th></tr>
<tr>
<td><code><?cs call:type_link(method.returnType) ?></code></td>
- <td width="100%"><?cs call:tag_list(obj.returns) ?></td>
- </tr>
- </table><?cs
- #
- # If no return tag found, but there is a return type not 'void', print it
- #
- ?><?cs
- elif:subcount(method.returnType) && method.returnType.label != 'void' ?>
- <table class="responsive">
- <tr><th colspan=2>Returns</th></tr>
- <tr>
- <td><code><?cs call:type_link(method.returnType) ?></code></td>
- <td width="100%"><!-- no returns description in source --></td>
+ <td width="100%"><?cs
+ if:subcount(obj.returns) ?><?cs
+ call:tag_list(obj.returns) ?><?cs
+ else ?><!-- no returns description in source --><?cs
+ /if ?><?cs
+ call:aux_tag_list(obj.returnsAux) ?></td>
</tr>
</table><?cs
/if ?><?cs
diff --git a/src/com/google/doclava/AuxUtils.java b/src/com/google/doclava/AuxUtils.java
new file mode 100644
index 0000000..ebed942
--- /dev/null
+++ b/src/com/google/doclava/AuxUtils.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class AuxUtils {
+ public static final int TYPE_METHOD = 0;
+ public static final int TYPE_FIELD = 1;
+ public static final int TYPE_PARAM = 2;
+ public static final int TYPE_RETURN = 3;
+
+ public static TagInfo[] tags(int type, List<AnnotationInstanceInfo> annotations) {
+ ArrayList<TagInfo> tags = new ArrayList<>();
+ for (AnnotationInstanceInfo annotation : annotations) {
+ if (annotation.type().qualifiedName().equals("android.annotation.SuppressAutoDoc")) {
+ return TagInfo.EMPTY_ARRAY;
+ }
+
+ ParsedTagInfo[] docTags = ParsedTagInfo.EMPTY_ARRAY;
+ switch (type) {
+ case TYPE_METHOD:
+ case TYPE_FIELD:
+ docTags = annotation.type().comment().memberDocTags();
+ break;
+ case TYPE_PARAM:
+ docTags = annotation.type().comment().paramDocTags();
+ break;
+ case TYPE_RETURN:
+ docTags = annotation.type().comment().returnDocTags();
+ break;
+ }
+ for (ParsedTagInfo docTag : docTags) {
+ tags.add(docTag);
+ }
+
+ // IntDef below belongs in return docs, not in method body
+ if (type == TYPE_METHOD) continue;
+
+ if (annotation.type().qualifiedName().equals("android.annotation.IntRange")
+ || annotation.type().qualifiedName().equals("android.annotation.FloatRange")) {
+ String from = null;
+ String to = null;
+ for (AnnotationValueInfo val : annotation.elementValues()) {
+ switch (val.element().name()) {
+ case "from": from = String.valueOf(val.value()); break;
+ case "to": to = String.valueOf(val.value()); break;
+ }
+ }
+ if (from != null || to != null) {
+ tags.add(new RangeTagInfo(SourcePositionInfo.UNKNOWN, from, to));
+ }
+ }
+
+ for (AnnotationInstanceInfo inner : annotation.type().annotations()) {
+ if (inner.type().qualifiedName().equals("android.annotation.IntDef")) {
+ ArrayList<AnnotationValueInfo> prefixes = null;
+ ArrayList<AnnotationValueInfo> values = null;
+ boolean flag = false;
+
+ for (AnnotationValueInfo val : inner.elementValues()) {
+ switch (val.element().name()) {
+ case "prefix": prefixes = (ArrayList<AnnotationValueInfo>) val.value(); break;
+ case "value": values = (ArrayList<AnnotationValueInfo>) val.value(); break;
+ case "flag": flag = Boolean.parseBoolean(String.valueOf(val.value())); break;
+ }
+ }
+
+ // Sadly we can only generate docs when told about a prefix
+ if (prefixes == null || prefixes.isEmpty()) continue;
+
+ final ClassInfo clazz = annotation.type().containingClass();
+ final HashMap<String, FieldInfo> candidates = new HashMap<>();
+ for (FieldInfo field : clazz.fields()) {
+ if (field.isHiddenOrRemoved())
+ continue;
+ for (AnnotationValueInfo prefix : prefixes) {
+ if (field.name().startsWith(String.valueOf(prefix.value()))) {
+ candidates.put(String.valueOf(field.constantValue()), field);
+ }
+ }
+ }
+
+ ArrayList<TagInfo> valueTags = new ArrayList<>();
+ for (AnnotationValueInfo value : values) {
+ final String expected = String.valueOf(value.value());
+ final FieldInfo field = candidates.get(expected);
+ if (field != null) {
+ valueTags.add(new ParsedTagInfo("", "",
+ "{@link " + clazz.qualifiedName() + "#" + field.name() + "}", null,
+ SourcePositionInfo.UNKNOWN));
+ }
+ }
+
+ if (!valueTags.isEmpty()) {
+ tags.add(new IntDefTagInfo(SourcePositionInfo.UNKNOWN, flag,
+ valueTags.toArray(TagInfo.getArray(valueTags.size()))));
+ }
+ }
+ }
+ }
+ return tags.toArray(TagInfo.getArray(tags.size()));
+ }
+}
diff --git a/src/com/google/doclava/Comment.java b/src/com/google/doclava/Comment.java
index 6abfaad..4ea6027 100644
--- a/src/com/google/doclava/Comment.java
+++ b/src/com/google/doclava/Comment.java
@@ -373,6 +373,12 @@
mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos));
} else if (name.equals("@apiNote") || name.equals("@implSpec") || name.equals("@implNote")) {
mTagsList.add(new ParsedTagInfo(name, name, text, mBase, pos));
+ } else if (name.equals("@memberDoc")) {
+ mMemberDocTagsList.add(new ParsedTagInfo("@memberDoc", "@memberDoc", text, mBase, pos));
+ } else if (name.equals("@paramDoc")) {
+ mParamDocTagsList.add(new ParsedTagInfo("@paramDoc", "@paramDoc", text, mBase, pos));
+ } else if (name.equals("@returnDoc")) {
+ mReturnDocTagsList.add(new ParsedTagInfo("@returnDoc", "@returnDoc", text, mBase, pos));
} else {
boolean known = KNOWN_TAGS.contains(name);
if (!known) {
@@ -486,6 +492,21 @@
return mBriefTags;
}
+ public ParsedTagInfo[] memberDocTags() {
+ init();
+ return mMemberDocTags;
+ }
+
+ public ParsedTagInfo[] paramDocTags() {
+ init();
+ return mParamDocTags;
+ }
+
+ public ParsedTagInfo[] returnDocTags() {
+ init();
+ return mReturnDocTags;
+ }
+
public boolean isHidden() {
if (mHidden == null) {
mHidden = !Doclava.checkLevel(Doclava.SHOW_HIDDEN) &&
@@ -555,6 +576,9 @@
mUndeprecateTags = mUndeprecateTagsList.toArray(TagInfo.getArray(mUndeprecateTagsList.size()));
mAttrTags = mAttrTagsList.toArray(AttrTagInfo.getArray(mAttrTagsList.size()));
mBriefTags = mBriefTagsList.toArray(TagInfo.getArray(mBriefTagsList.size()));
+ mMemberDocTags = mMemberDocTagsList.toArray(ParsedTagInfo.getArray(mMemberDocTagsList.size()));
+ mParamDocTags = mParamDocTagsList.toArray(ParsedTagInfo.getArray(mParamDocTagsList.size()));
+ mReturnDocTags = mReturnDocTagsList.toArray(ParsedTagInfo.getArray(mReturnDocTagsList.size()));
mTagsList = null;
mParamTagsList = null;
@@ -565,6 +589,9 @@
mUndeprecateTagsList = null;
mAttrTagsList = null;
mBriefTagsList = null;
+ mMemberDocTagsList = null;
+ mParamDocTagsList = null;
+ mReturnDocTagsList = null;
}
boolean mInitialized;
@@ -587,6 +614,9 @@
TagInfo[] mDeprecatedTags;
TagInfo[] mUndeprecateTags;
AttrTagInfo[] mAttrTags;
+ ParsedTagInfo[] mMemberDocTags;
+ ParsedTagInfo[] mParamDocTags;
+ ParsedTagInfo[] mReturnDocTags;
ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>();
ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>();
@@ -598,6 +628,8 @@
ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>();
ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>();
ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>();
-
+ ArrayList<ParsedTagInfo> mMemberDocTagsList = new ArrayList<ParsedTagInfo>();
+ ArrayList<ParsedTagInfo> mParamDocTagsList = new ArrayList<ParsedTagInfo>();
+ ArrayList<ParsedTagInfo> mReturnDocTagsList = new ArrayList<ParsedTagInfo>();
}
diff --git a/src/com/google/doclava/FieldInfo.java b/src/com/google/doclava/FieldInfo.java
index 85c1a85..3b8c676 100644
--- a/src/com/google/doclava/FieldInfo.java
+++ b/src/com/google/doclava/FieldInfo.java
@@ -16,10 +16,11 @@
package com.google.doclava;
-import com.google.doclava.apicheck.ApiParseException;
import com.google.clearsilver.jsilver.data.Data;
-import java.util.Comparator;
+import com.google.doclava.apicheck.ApiParseException;
+
import java.util.ArrayList;
+import java.util.Comparator;
public class FieldInfo extends MemberInfo {
public static final Comparator<FieldInfo> comparator = new Comparator<FieldInfo>() {
@@ -330,6 +331,7 @@
data.setValue(base + ".anchor", anchor());
TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
TagInfo.makeHDF(data, base + ".descr", inlineTags());
+ TagInfo.makeHDF(data, base + ".descrAux", AuxUtils.tags(AuxUtils.TYPE_FIELD, annotations()));
TagInfo.makeHDF(data, base + ".deprecated", comment().deprecatedTags());
TagInfo.makeHDF(data, base + ".seeAlso", comment().seeTags());
data.setValue(base + ".since", getSince());
diff --git a/src/com/google/doclava/IntDefTagInfo.java b/src/com/google/doclava/IntDefTagInfo.java
new file mode 100644
index 0000000..3044557
--- /dev/null
+++ b/src/com/google/doclava/IntDefTagInfo.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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 com.google.clearsilver.jsilver.data.Data;
+
+public class IntDefTagInfo extends TagInfo {
+ public static final IntDefTagInfo[] EMPTY_ARRAY = new IntDefTagInfo[0];
+
+ public static IntDefTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new IntDefTagInfo[size];
+ }
+
+ private boolean mFlag;
+ private TagInfo[] mValues;
+
+ IntDefTagInfo(SourcePositionInfo position, boolean flag, TagInfo[] values) {
+ super("@intDef", "@intDef", "", position);
+ mFlag = flag;
+ mValues = values;
+ }
+
+ private TagInfo[] valuesTags() {
+ return mValues;
+ }
+
+ @Override
+ public void makeHDF(Data data, String base) {
+ super.makeHDF(data, base);
+ if (mFlag) {
+ data.setValue(base + ".flag", "true");
+ }
+ TagInfo.makeHDF(data, base + ".values", valuesTags());
+ }
+}
diff --git a/src/com/google/doclava/MethodInfo.java b/src/com/google/doclava/MethodInfo.java
index f45dff4..1e92dff 100644
--- a/src/com/google/doclava/MethodInfo.java
+++ b/src/com/google/doclava/MethodInfo.java
@@ -504,10 +504,13 @@
}
}
+ // Collect all docs requested by annotations
+ TagInfo[] auxTags = AuxUtils.tags(AuxUtils.TYPE_PARAM, param.annotations());
+
// Okay, now add the collected parameter information to the method data
mParamTags[i] =
new ParamTagInfo("@param", type, name + " " + comment, parent(),
- position);
+ position, auxTags);
// while we're here, if we find any parameters that are still
// undocumented at this point, complain. This warning is off by
@@ -613,6 +616,7 @@
TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
TagInfo.makeHDF(data, base + ".descr", inlineTags());
+ TagInfo.makeHDF(data, base + ".descrAux", AuxUtils.tags(AuxUtils.TYPE_METHOD, annotations()));
TagInfo.makeHDF(data, base + ".blockTags", blockTags());
TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
@@ -631,6 +635,7 @@
data.setValue(base + ".scope", "public");
}
TagInfo.makeHDF(data, base + ".returns", returnTags());
+ TagInfo.makeHDF(data, base + ".returnsAux", AuxUtils.tags(AuxUtils.TYPE_RETURN, annotations()));
if (mTypeParameters != null) {
TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
@@ -654,6 +659,7 @@
showAnnotations().toArray(new AnnotationInstanceInfo[showAnnotations().size()]));
setFederatedReferences(data, base);
+
}
public HashSet<String> typeVariables() {
diff --git a/src/com/google/doclava/ParamTagInfo.java b/src/com/google/doclava/ParamTagInfo.java
index d9353c5..c71ce64 100644
--- a/src/com/google/doclava/ParamTagInfo.java
+++ b/src/com/google/doclava/ParamTagInfo.java
@@ -18,8 +18,8 @@
import com.google.clearsilver.jsilver.data.Data;
-import java.util.regex.Pattern;
import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class ParamTagInfo extends ParsedTagInfo {
public static final ParamTagInfo[] EMPTY_ARRAY = new ParamTagInfo[0];
@@ -33,9 +33,16 @@
private boolean mIsTypeParameter;
private String mParameterComment;
private String mParameterName;
+ private TagInfo[] mAuxTags;
ParamTagInfo(String name, String kind, String text, ContainerInfo base, SourcePositionInfo sp) {
+ this(name, kind, text, base, sp, null);
+ }
+
+ ParamTagInfo(String name, String kind, String text, ContainerInfo base, SourcePositionInfo sp,
+ TagInfo[] auxTags) {
super(name, kind, text, base, sp);
+ mAuxTags = auxTags;
Matcher m = PATTERN.matcher(text);
if (m.matches()) {
@@ -72,12 +79,17 @@
return mParameterName;
}
+ public TagInfo[] auxTags() {
+ return mAuxTags;
+ }
+
@Override
public void makeHDF(Data data, String base) {
data.setValue(base + ".name", parameterName());
data.setValue(base + ".kind", kind());
data.setValue(base + ".isTypeParameter", isTypeParameter() ? "1" : "0");
TagInfo.makeHDF(data, base + ".comment", commentTags());
+ TagInfo.makeHDF(data, base + ".commentAux", auxTags());
}
public static void makeHDF(Data data, String base, ParamTagInfo[] tags) {
diff --git a/src/com/google/doclava/RangeTagInfo.java b/src/com/google/doclava/RangeTagInfo.java
new file mode 100644
index 0000000..8f3be30
--- /dev/null
+++ b/src/com/google/doclava/RangeTagInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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 com.google.clearsilver.jsilver.data.Data;
+
+public class RangeTagInfo extends TagInfo {
+ public static final RangeTagInfo[] EMPTY_ARRAY = new RangeTagInfo[0];
+
+ public static RangeTagInfo[] getArray(int size) {
+ return size == 0 ? EMPTY_ARRAY : new RangeTagInfo[size];
+ }
+
+ private String mFrom;
+ private String mTo;
+
+ RangeTagInfo(SourcePositionInfo position, String from, String to) {
+ super("@range", "@range", "", position);
+ mFrom = from;
+ mTo = to;
+ }
+
+ @Override
+ public void makeHDF(Data data, String base) {
+ super.makeHDF(data, base);
+ if (mFrom != null) {
+ data.setValue(base + ".from", mFrom);
+ }
+ if (mTo != null) {
+ data.setValue(base + ".to", mTo);
+ }
+ }
+}
diff --git a/src/com/google/doclava/TagInfo.java b/src/com/google/doclava/TagInfo.java
index 7bb640e..f321fa8 100644
--- a/src/com/google/doclava/TagInfo.java
+++ b/src/com/google/doclava/TagInfo.java
@@ -18,6 +18,8 @@
import com.google.clearsilver.jsilver.data.Data;
+import java.util.Arrays;
+
public class TagInfo {
public static final TagInfo[] EMPTY_ARRAY = new TagInfo[0];
@@ -25,6 +27,18 @@
return size == 0 ? EMPTY_ARRAY : new TagInfo[size];
}
+ public static TagInfo[] append(TagInfo[] list, TagInfo item) {
+ if (item == null) {
+ return list;
+ } if (list == null) {
+ return new TagInfo[] { item };
+ } else {
+ list = Arrays.copyOf(list, list.length + 1);
+ list[list.length - 1] = item;
+ return list;
+ }
+ }
+
private String mName;
private String mText;
private String mKind;