Fix android hierarchy ref page template. am: bbac6c38c5 am: 302b861409
am: c6a9a703ea
* commit 'c6a9a703ea158fb2311d7dfcbd6db2a7e49a8523':
Fix android hierarchy ref page template.
Change-Id: Ic3b001c175b31f74500e56726adbc7d154ad4cc2
diff --git a/README.version b/README.version
new file mode 100644
index 0000000..f7c1fa0
--- /dev/null
+++ b/README.version
@@ -0,0 +1,3 @@
+URL: https://code.google.com/p/doclava/downloads/detail?name=doclava-1.0.6-bundle.zip&can=2&q=
+Version: 1.0.6
+BugComponent: 27745
diff --git a/res/assets/templates/class.cs b/res/assets/templates/class.cs
index e35e938..8854eaf 100644
--- a/res/assets/templates/class.cs
+++ b/res/assets/templates/class.cs
@@ -177,8 +177,9 @@
<tr class="<?cs if:count % #2 ?>alt-color<?cs /if ?> api apilevel-<?cs var:method.since ?>" >
<td class="jd-typecol"><nobr>
<?cs var:method.abstract ?>
- <?cs var:method.final ?>
+ <?cs var:method.default ?>
<?cs var:method.static ?>
+ <?cs var:method.final ?>
<?cs call:type_link(method.generic) ?>
<?cs call:type_link(method.returnType) ?></nobr>
</td>
@@ -523,10 +524,11 @@
<div class="jd-details api apilevel-<?cs var:method.since ?>">
<h4 class="jd-details-title">
<span class="normal">
- <?cs var:method.scope ?>
- <?cs var:method.static ?>
- <?cs var:method.final ?>
- <?cs var:method.abstract ?>
+ <?cs var:method.scope ?>
+ <?cs var:method.abstract ?>
+ <?cs var:method.default ?>
+ <?cs var:method.static ?>
+ <?cs var:method.final ?>
<?cs call:type_link(method.returnType) ?>
</span>
<span class="sympad"><?cs var:method.name ?></span>
diff --git a/res/assets/templates/macros.cs b/res/assets/templates/macros.cs
index a462e0d..750a31d 100644
--- a/res/assets/templates/macros.cs
+++ b/res/assets/templates/macros.cs
@@ -132,6 +132,30 @@
/each ?><?cs
/def ?>
+<?cs # Print output for block tags that are not "standard" javadoc tags ?><?cs
+def:block_tag_list(tags) ?><?cs
+ each:tag = tags ?><?cs
+ if:tag.kind == "@apiNote" ?>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">API Note:</h5>
+ <ul class="nolist"><li><?cs call:tag_list(tag.commentTags) ?></li></ul>
+ </div><?cs
+ /if ?><?cs
+ if:tag.kind == "@implSpec" ?>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Implementation Requirements:</h5>
+ <ul class="nolist"><li><?cs call:tag_list(tag.commentTags) ?></li></ul>
+ </div><?cs
+ /if ?><?cs
+ if:tag.kind == "@implNote" ?>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Implementation Note:</h5>
+ <ul class="nolist"><li><?cs call:tag_list(tag.commentTags) ?></li></ul>
+ </div><?cs
+ /if ?><?cs
+ /each ?><?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
@@ -255,8 +279,10 @@
each:attr=obj.attrRefs ?>
<li><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs var:attr.name ?></a></li><?cs
/each ?>
- </ul>
- <?cs
+ </ul><?cs
+ /if ?><?cs
+ if:subcount(obj.blockTags) ?>
+ <?cs call:block_tag_list(obj.blockTags) ?><?cs
/if ?><?cs
#
# Print the @param tags
@@ -414,4 +440,3 @@
/def ?>
<?cs include:"components.cs" ?>
-
diff --git a/src/com/google/doclava/CodeTagInfo.java b/src/com/google/doclava/CodeTagInfo.java
index 56ffd13..ac69733 100644
--- a/src/com/google/doclava/CodeTagInfo.java
+++ b/src/com/google/doclava/CodeTagInfo.java
@@ -31,9 +31,6 @@
}
public CodeTagInfo(String text, SourcePositionInfo sp) {
- // TODO: the correct behavior is to escape the text,
- // but we'll have to update the Android sources before making the switch.
- //super("@code", "@code", encode(text), sp);
- super("@code", "@code", text, sp);
+ super("@code", "@code", encode(text), sp);
}
}
diff --git a/src/com/google/doclava/Comment.java b/src/com/google/doclava/Comment.java
index 616ccc4..1a4498f 100644
--- a/src/com/google/doclava/Comment.java
+++ b/src/com/google/doclava/Comment.java
@@ -28,6 +28,7 @@
Pattern.compile("((.*?)\\.)[ \t\r\n\\<](.*)", Pattern.DOTALL);
private static final Set<String> KNOWN_TAGS = new HashSet<String>(Arrays.asList(new String[] {
+ "@apiNote",
"@author",
"@since",
"@version",
@@ -41,6 +42,8 @@
"@sample",
"@include",
"@serial",
+ "@implNote",
+ "@implSpec",
}));
public Comment(String text, ContainerInfo base, SourcePositionInfo sp) {
@@ -164,9 +167,15 @@
}
private int findEndIndexOfInlineTag(String text, int fromIndex, int toIndex) {
+ int braceDepth = 0;
for (int i = fromIndex; i < toIndex; i++) {
- if (text.charAt(i) == '}') {
- return i;
+ if (text.charAt(i) == '{') {
+ braceDepth++;
+ } else if (text.charAt(i) == '}') {
+ braceDepth--;
+ if (braceDepth == 0) {
+ return i;
+ }
}
}
@@ -356,6 +365,8 @@
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 if (name.equals("@apiNote") || name.equals("@implSpec") || name.equals("@implNote")) {
+ mTagsList.add(new ParsedTagInfo(name, name, text, mBase, pos));
} else {
boolean known = KNOWN_TAGS.contains(name);
if (!known) {
@@ -424,6 +435,11 @@
return results.toArray(TagInfo.getArray(results.size()));
}
+ public TagInfo[] blockTags() {
+ init();
+ return mTags;
+ }
+
public ParamTagInfo[] paramTags() {
init();
return mParamTags;
@@ -522,6 +538,7 @@
mInitialized = true;
mInlineTags = mInlineTagsList.toArray(TagInfo.getArray(mInlineTagsList.size()));
+ mTags = mTagsList.toArray(TagInfo.getArray(mTagsList.size()));
mParamTags = mParamTagsList.toArray(ParamTagInfo.getArray(mParamTagsList.size()));
mSeeTags = mSeeTagsList.toArray(SeeTagInfo.getArray(mSeeTagsList.size()));
mThrowsTags = mThrowsTagsList.toArray(ThrowsTagInfo.getArray(mThrowsTagsList.size()));
@@ -533,6 +550,7 @@
mAttrTags = mAttrTagsList.toArray(AttrTagInfo.getArray(mAttrTagsList.size()));
mBriefTags = mBriefTagsList.toArray(TagInfo.getArray(mBriefTagsList.size()));
+ mTagsList = null;
mParamTagsList = null;
mSeeTagsList = null;
mThrowsTagsList = null;
diff --git a/src/com/google/doclava/Converter.java b/src/com/google/doclava/Converter.java
index 3153b41..1bc3563 100644
--- a/src/com/google/doclava/Converter.java
+++ b/src/com/google/doclava/Converter.java
@@ -227,7 +227,8 @@
if (p == null) return null;
ParameterInfo pi =
new ParameterInfo(p.name(), p.typeName(), Converter.obtainType(p.type()), isVarArg,
- Converter.convertSourcePosition(pos));
+ Converter.convertSourcePosition(pos),
+ Arrays.asList(Converter.convertAnnotationInstances(p.annotations())));
return pi;
}
@@ -403,8 +404,9 @@
m.name(), m.signature(), Converter.obtainClass(m.containingClass()),
Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
.isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
- m.isAbstract(), m.isSynchronized(), m.isNative(), true, "annotationElement",
- m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()),
+ m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), true,
+ "annotationElement", m.flatSignature(),
+ Converter.obtainMethod(m.overriddenMethod()),
Converter.obtainType(m.returnType()),
new ArrayList<ParameterInfo>(Arrays.asList(
Converter.convertParameters(m.parameters(), m))),
@@ -424,8 +426,8 @@
Converter.obtainClass(m.containingClass()),
Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(),
m.isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
- m.isAbstract(), m.isSynchronized(), m.isNative(), false, "method",
- m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()),
+ m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), false,
+ "method", m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()),
Converter.obtainType(m.returnType()),
new ArrayList<ParameterInfo>(Arrays.asList(
Converter.convertParameters(m.parameters(), m))),
@@ -439,12 +441,26 @@
return result;
} else {
ConstructorDoc m = (ConstructorDoc) o;
+ // Workaround for a JavaDoc behavior change introduced in OpenJDK 8 that breaks
+ // links in documentation and the content of API files like current.txt.
+ // http://b/18051133.
+ String name = m.name();
+ ClassDoc containingClass = m.containingClass();
+ if (containingClass.containingClass() != null) {
+ // This should detect the new behavior and be bypassed otherwise.
+ if (!name.contains(".")) {
+ // Constructors of inner classes do not contain the name of the enclosing class
+ // with OpenJDK 8. This simulates the old behavior:
+ name = containingClass.name();
+ }
+ }
+ // End of workaround.
MethodInfo result =
- new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))), m
- .name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter
+ new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))),
+ name, m.signature(), Converter.obtainClass(m.containingClass()), Converter
.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
.isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
- false, m.isSynchronized(), m.isNative(), false, "constructor", m.flatSignature(),
+ false, m.isSynchronized(), m.isNative(), false/*isDefault*/, false, "constructor", m.flatSignature(),
null, null, new ArrayList<ParameterInfo>(Arrays.asList(Converter.convertParameters(m.parameters(), m))),
new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(m.thrownExceptions()))), Converter.convertSourcePosition(m
.position()), new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter.convertAnnotationInstances(m.annotations()))));
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 4ca7e6b..c1d29ee 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -305,6 +305,7 @@
documentAnnotationsPath = a[1];
} else if (a[0].equals("-referenceonly")) {
referenceOnly = true;
+ mHDFData.add(new String[] {"referenceonly", "1"});
}
}
@@ -850,11 +851,11 @@
String templ = relative + f.getName();
int len = templ.length();
if (len > 3 && ".cs".equals(templ.substring(len - 3))) {
- Data data = makeHDF();
+ Data data = makePackageHDF();
String filename = templ.substring(0, len - 3) + htmlExtension;
ClearPage.write(data, templ, filename, js);
} else if (len > 3 && ".jd".equals(templ.substring(len - 3))) {
- Data data = makeHDF();
+ Data data = makePackageHDF();
String filename = templ.substring(0, len - 3) + htmlExtension;
DocFile.writePage(f.getAbsolutePath(), relative, filename, data);
} else if(!f.getName().equals(".DS_Store")){
diff --git a/src/com/google/doclava/InfoBuilder.java b/src/com/google/doclava/InfoBuilder.java
index a07f990..895f5b6 100644
--- a/src/com/google/doclava/InfoBuilder.java
+++ b/src/com/google/doclava/InfoBuilder.java
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -1069,7 +1070,7 @@
modifiers.isPrivate(), modifiers.isFinal(),
modifiers.isStatic(), modifiers.isSynthetic(),
modifiers.isAbstract(), modifiers.isSynchronized(),
- false, isAnnotationElement, kind, flatSignature.toString(),
+ false, modifiers.isDefault(), isAnnotationElement, kind, flatSignature.toString(),
null, returnType, parameters, thrownExceptions,
commentAndPosition.getPosition(), modifiers.getAnnotations());
@@ -1142,7 +1143,8 @@
commentAndPosition.setPosition(paramPart);
parameters.add(new ParameterInfo(name, type.qualifiedTypeName(), type,
- isVarArg, commentAndPosition.getPosition()));
+ isVarArg, commentAndPosition.getPosition(),
+ Collections.<AnnotationInstanceInfo>emptyList()));
}
}
}
@@ -1852,6 +1854,7 @@
private boolean mIsSynthetic = false;
private boolean mIsSynchronized = false;
private boolean mIsStrictfp = false;
+ private boolean mIsDefault = false;
private InfoBuilder mBuilder;
private ArrayList<AnnotationInstanceInfo> mAnnotations;
@@ -1893,6 +1896,8 @@
mIsSynchronized = true;
} else if ("strictfp".equals(modifier)) {
mIsStrictfp = true;
+ } else if ("default".equals(modifier)) {
+ mIsDefault = true;
} else if ("annotation".equals(modifier)) {
mAnnotations.add(buildAnnotationInstance((ParseTree) child, mBuilder));
}
@@ -1948,6 +1953,10 @@
return mIsStrictfp;
}
+ public boolean isDefault() {
+ return mIsDefault;
+ }
+
public ArrayList<AnnotationInstanceInfo> getAnnotations() {
return mAnnotations;
}
diff --git a/src/com/google/doclava/MethodInfo.java b/src/com/google/doclava/MethodInfo.java
index f2f040e..5d6188a 100644
--- a/src/com/google/doclava/MethodInfo.java
+++ b/src/com/google/doclava/MethodInfo.java
@@ -252,7 +252,7 @@
new MethodInfo(getRawCommentText(), mTypeParameters, name(), signature(),
newContainingClass, realContainingClass(), isPublic(), isProtected(),
isPackagePrivate(), isPrivate(), isFinal(), isStatic(), isSynthetic(), mIsAbstract,
- mIsSynchronized, mIsNative, mIsAnnotationElement, kind(), mFlatSignature,
+ mIsSynchronized, mIsNative, mIsDefault, mIsAnnotationElement, kind(), mFlatSignature,
mOverriddenMethod, returnType, mParameters, mThrownExceptions, position(),
annotations());
result.init(mDefaultAnnotationElementValue);
@@ -263,10 +263,10 @@
String signature, ClassInfo containingClass, ClassInfo realContainingClass, boolean isPublic,
boolean isProtected, boolean isPackagePrivate, boolean isPrivate, boolean isFinal,
boolean isStatic, boolean isSynthetic, boolean isAbstract, boolean isSynchronized,
- boolean isNative, boolean isAnnotationElement, String kind, String flatSignature,
- MethodInfo overriddenMethod, TypeInfo returnType, ArrayList<ParameterInfo> parameters,
- ArrayList<ClassInfo> thrownExceptions, SourcePositionInfo position,
- ArrayList<AnnotationInstanceInfo> annotations) {
+ boolean isNative, boolean isDefault, boolean isAnnotationElement, String kind,
+ String flatSignature, MethodInfo overriddenMethod, TypeInfo returnType,
+ ArrayList<ParameterInfo> parameters, ArrayList<ClassInfo> thrownExceptions,
+ SourcePositionInfo position, ArrayList<AnnotationInstanceInfo> annotations) {
// Explicitly coerce 'final' state of Java6-compiled enum values() method, to match
// the Java5-emitted base API description.
super(rawCommentText, name, signature, containingClass, realContainingClass, isPublic,
@@ -274,19 +274,13 @@
((name.equals("values") && containingClass.isEnum()) ? true : isFinal),
isStatic, isSynthetic, kind, position, annotations);
- // The underlying MethodDoc for an interface's declared methods winds up being marked
- // non-abstract. Correct that here by looking at the immediate-parent class, and marking
- // this method abstract if it is an unimplemented interface method.
- if (containingClass.isInterface()) {
- isAbstract = true;
- }
-
mReasonOpened = "0:0";
mIsAnnotationElement = isAnnotationElement;
mTypeParameters = typeParameters;
mIsAbstract = isAbstract;
mIsSynchronized = isSynchronized;
mIsNative = isNative;
+ mIsDefault = isDefault;
mFlatSignature = flatSignature;
mOverriddenMethod = overriddenMethod;
mReturnType = returnType;
@@ -310,6 +304,10 @@
return mIsNative;
}
+ public boolean isDefault() {
+ return mIsDefault;
+ }
+
public String flatSignature() {
return mFlatSignature;
}
@@ -318,6 +316,10 @@
return new InlineTags();
}
+ public TagInfo[] blockTags() {
+ return comment().blockTags();
+ }
+
public InheritedTags firstSentenceTags() {
return new FirstSentenceTags();
}
@@ -454,7 +456,7 @@
// Complain about misnamed @param tags
for (ParamTagInfo tag : paramTags) {
- if (!isParamTagInMethod(tag)){
+ if (!isParamTagInMethod(tag) && !tag.isTypeParameter()){
Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
"@param tag with name that doesn't match the parameter list: '"
+ tag.parameterName() + "'");
@@ -545,15 +547,15 @@
}
int i = 0;
for (ParameterInfo mine : mParameters) {
- if (!mine.matchesDimension(dimensions[i], varargs)) {
+ // If the method we're matching against is a varargs method (varargs == true), then
+ // only its last parameter is varargs.
+ if (!mine.matchesDimension(dimensions[i], (i == params.length - 1) ? varargs : false)) {
return false;
}
TypeInfo myType = mine.type();
String qualifiedName = myType.qualifiedTypeName();
String realType = myType.isPrimitive() ? "" : myType.asClassInfo().qualifiedName();
String s = params[i];
- int slen = s.length();
- int qnlen = qualifiedName.length();
// Check for a matching generic name or best known type
if (!matchesType(qualifiedName, s) && !matchesType(realType, s)) {
@@ -593,12 +595,14 @@
data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
}
+ data.setValue(base + ".default", mIsDefault ? "default" : "");
data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
data.setValue(base + ".final", isFinal() ? "final" : "");
data.setValue(base + ".static", isStatic() ? "static" : "");
TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
TagInfo.makeHDF(data, base + ".descr", inlineTags());
+ TagInfo.makeHDF(data, base + ".blockTags", blockTags());
TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
data.setValue(base + ".since", getSince());
@@ -736,6 +740,7 @@
private boolean mIsVarargs;
private boolean mDeprecatedKnown;
private boolean mIsDeprecated;
+ private boolean mIsDefault;
private ArrayList<ParameterInfo> mParameters;
private ArrayList<ClassInfo> mThrownExceptions;
private String[] mParamStrings;
diff --git a/src/com/google/doclava/ParameterInfo.java b/src/com/google/doclava/ParameterInfo.java
index b911283..10000df 100644
--- a/src/com/google/doclava/ParameterInfo.java
+++ b/src/com/google/doclava/ParameterInfo.java
@@ -20,16 +20,18 @@
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
public class ParameterInfo {
public ParameterInfo(String name, String typeName, TypeInfo type, boolean isVarArg,
- SourcePositionInfo position) {
+ SourcePositionInfo position, List<AnnotationInstanceInfo> annotationInstanceInfos) {
mName = name;
mTypeName = typeName;
mType = type;
mIsVarArg = isVarArg;
mPosition = position;
+ mAnnotations = annotationInstanceInfos;
}
/**
@@ -37,7 +39,8 @@
*/
public ParameterInfo cloneWithTypeArguments(Map<String, TypeInfo> typeArgumentMapping) {
return new ParameterInfo(
- mName, mTypeName, mType.getTypeWithArguments(typeArgumentMapping), mIsVarArg, mPosition);
+ mName, mTypeName, mType.getTypeWithArguments(typeArgumentMapping),
+ mIsVarArg, mPosition, mAnnotations);
}
TypeInfo type() {
@@ -60,6 +63,10 @@
return mIsVarArg;
}
+ List<AnnotationInstanceInfo> annotations() {
+ return mAnnotations;
+ }
+
public void makeHDF(Data data, String base, boolean isLastVararg, HashSet<String> typeVariables) {
makeHDF(data, base, isLastVararg, typeVariables, Collections.<String, TypeInfo>emptyMap());
}
@@ -100,4 +107,5 @@
TypeInfo mType;
boolean mIsVarArg;
SourcePositionInfo mPosition;
+ List<AnnotationInstanceInfo> mAnnotations;
}
diff --git a/src/com/google/doclava/ParsedTagInfo.java b/src/com/google/doclava/ParsedTagInfo.java
index aad3767..ded9522 100755
--- a/src/com/google/doclava/ParsedTagInfo.java
+++ b/src/com/google/doclava/ParsedTagInfo.java
@@ -16,6 +16,7 @@
package com.google.doclava;
+import com.google.clearsilver.jsilver.data.Data;
import java.util.ArrayList;
public class ParsedTagInfo extends TagInfo {
@@ -46,6 +47,12 @@
mCommentText = comment;
}
+ @Override
+ public void makeHDF(Data data, String base) {
+ super.makeHDF(data, base);
+ TagInfo.makeHDF(data, base + ".commentTags", commentTags());
+ }
+
public static <T extends ParsedTagInfo> TagInfo[] joinTags(T[] tags) {
ArrayList<TagInfo> list = new ArrayList<TagInfo>();
final int N = tags.length;
diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java
index efcf89b..6de86a1 100644
--- a/src/com/google/doclava/Stubs.java
+++ b/src/com/google/doclava/Stubs.java
@@ -17,10 +17,16 @@
package com.google.doclava;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -29,6 +35,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Scanner;
import java.util.Set;
import java.util.regex.Pattern;
@@ -445,12 +452,63 @@
static void writeClassFile(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
PackageInfo pkg = cl.containingPackage();
+ if (cl.containingClass() == null) {
+ stream.print(parseLicenseHeader(cl.position()));
+ }
if (pkg != null) {
stream.println("package " + pkg.name() + ";");
}
writeClass(stream, notStrippable, cl);
}
+ private static String parseLicenseHeader(/* @Nonnull */ SourcePositionInfo positionInfo) {
+ if (positionInfo == null) {
+ throw new NullPointerException("positionInfo == null");
+ }
+
+ try {
+ final File sourceFile = new File(positionInfo.file);
+ if (!sourceFile.exists()) {
+ throw new IllegalArgumentException("Unable to find " + sourceFile +
+ ". This is usually because doclava has been asked to generate stubs for a file " +
+ "that isn't present in the list of input source files but exists in the input " +
+ "classpath.");
+ }
+ return parseLicenseHeader(new FileInputStream(sourceFile));
+ } catch (IOException ioe) {
+ throw new RuntimeException("Unable to parse license header for: " + positionInfo.file, ioe);
+ }
+ }
+
+ /* @VisibleForTesting */
+ static String parseLicenseHeader(InputStream input) throws IOException {
+ StringBuilder builder = new StringBuilder(8192);
+ try (Scanner scanner = new Scanner(
+ new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)))) {
+ String line;
+ while (scanner.hasNextLine()) {
+ line = scanner.nextLine().trim();
+ // Use an extremely simple strategy for parsing license headers : assume that
+ // all file content before the first "package " or "import " directive is a license
+ // header. In some cases this might contain more than just the license header, but we
+ // don't care.
+ if (line.startsWith("package ") || line.startsWith("import ")) {
+ break;
+ }
+ builder.append(line);
+ builder.append("\n");
+ }
+
+ // We've reached the end of the file without reaching any package or import
+ // directives.
+ if (!scanner.hasNextLine()) {
+ throw new IOException("Unable to parse license header");
+ }
+ }
+
+ return builder.toString();
+ }
+
static void writeClass(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
writeAnnotations(stream, cl.annotations(), cl.isDeprecated());
@@ -632,6 +690,9 @@
writeAnnotations(stream, method.annotations(), method.isDeprecated());
+ if (method.isDefault()) {
+ stream.print("default ");
+ }
stream.print(method.scope() + " ");
if (method.isStatic()) {
stream.print("static ");
@@ -667,7 +728,9 @@
int count = 1;
int size = method.parameters().size();
for (ParameterInfo param : method.parameters()) {
- stream.print(comma + fullParameterTypeName(method, param.type(), count == size) + " "
+ stream.print(comma);
+ writeAnnotations(stream, param.annotations(), false);
+ stream.print(fullParameterTypeName(method, param.type(), count == size) + " "
+ param.name());
comma = ", ";
count++;
@@ -682,7 +745,7 @@
comma = ", ";
}
}
- if (method.isAbstract() || method.isNative() || method.containingClass().isInterface()) {
+ if (method.isAbstract() || method.isNative() || (method.containingClass().isInterface() && (!method.isDefault() && !method.isStatic()))) {
stream.println(";");
} else {
stream.print(" { ");
@@ -1353,6 +1416,9 @@
static void writeMethodApi(PrintStream apiWriter, MethodInfo mi) {
apiWriter.print(" method ");
apiWriter.print(mi.scope());
+ if (mi.isDefault()) {
+ apiWriter.print(" default");
+ }
if (mi.isStatic()) {
apiWriter.print(" static");
}
@@ -1543,9 +1609,7 @@
static void writeConstructorKeepList(PrintStream keepListWriter, MethodInfo mi) {
keepListWriter.print(" ");
- String name = mi.name();
- name = name.replace(".", "$");
- keepListWriter.print(name);
+ keepListWriter.print("<init>");
writeParametersKeepList(keepListWriter, mi, mi.parameters());
keepListWriter.print(";\n");
diff --git a/src/com/google/doclava/apicheck/ApiFile.java b/src/com/google/doclava/apicheck/ApiFile.java
index 3f96943..2d958d4 100644
--- a/src/com/google/doclava/apicheck/ApiFile.java
+++ b/src/com/google/doclava/apicheck/ApiFile.java
@@ -29,7 +29,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.LinkedList;
+import java.util.List;
class ApiFile {
@@ -252,7 +254,7 @@
method = new MethodInfo(""/*rawCommentText*/, new ArrayList<TypeInfo>()/*typeParameters*/,
name, null/*signature*/, cl, cl, pub, prot, pkgpriv, false/*isPrivate*/, false/*isFinal*/,
false/*isStatic*/, false/*isSynthetic*/, false/*isAbstract*/, false/*isSynthetic*/,
- false/*isNative*/,
+ false/*isNative*/, false/* isDefault */,
false /*isAnnotationElement*/, "constructor", null/*flatSignature*/,
null/*overriddenMethod*/, cl.asTypeInfo(), new ArrayList<ParameterInfo>(),
new ArrayList<ClassInfo>()/*thrownExceptions*/, tokenizer.pos(),
@@ -280,6 +282,7 @@
boolean abs = false;
boolean dep = false;
boolean syn = false;
+ boolean def = false;
String type;
String name;
String ext = null;
@@ -294,6 +297,10 @@
} else {
pkgpriv = true;
}
+ if ("default".equals(token)) {
+ def = true;
+ token = tokenizer.requireToken();
+ }
if ("static".equals(token)) {
stat = true;
token = tokenizer.requireToken();
@@ -321,7 +328,7 @@
name = token;
method = new MethodInfo(""/*rawCommentText*/, new ArrayList<TypeInfo>()/*typeParameters*/,
name, null/*signature*/, cl, cl, pub, prot, pkgpriv, false/*isPrivate*/, fin,
- stat, false/*isSynthetic*/, abs/*isAbstract*/, syn, false/*isNative*/,
+ stat, false/*isSynthetic*/, abs/*isAbstract*/, syn, false/*isNative*/, def/*isDefault*/,
false /*isAnnotationElement*/, "method", null/*flatSignature*/, null/*overriddenMethod*/,
Converter.obtainTypeFromString(type), new ArrayList<ParameterInfo>(),
new ArrayList<ClassInfo>()/*thrownExceptions*/, tokenizer.pos(),
@@ -488,10 +495,13 @@
} else {
throw new ApiParseException("expected , found " + token, tokenizer.getLine());
}
+ // api file does not preserve annotations.
+ List<AnnotationInstanceInfo> annotations = Collections.emptyList();
method.addParameter(new ParameterInfo(name, type,
Converter.obtainTypeFromString(type),
type.endsWith("..."),
- tokenizer.pos()));
+ tokenizer.pos(),
+ annotations));
if (type.endsWith("...")) {
method.setVarargs(true);
}
diff --git a/src/com/google/doclava/apicheck/XmlApiFile.java b/src/com/google/doclava/apicheck/XmlApiFile.java
index 8b15d09..d36766b 100644
--- a/src/com/google/doclava/apicheck/XmlApiFile.java
+++ b/src/com/google/doclava/apicheck/XmlApiFile.java
@@ -35,6 +35,8 @@
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Stack;
class XmlApiFile extends DefaultHandler {
@@ -137,6 +139,7 @@
boolean isAbstract = Boolean.valueOf(attributes.getValue("abstract"));
boolean isSynchronized = Boolean.valueOf(attributes.getValue("synchronized"));
boolean isNative = Boolean.valueOf(attributes.getValue("native"));
+ boolean isDefault = Boolean.valueOf(attributes.getValue("default"));
boolean isAnnotationElement = false; // TODO
String kind = qName;
String flatSignature = null; // TODO
@@ -150,9 +153,9 @@
mCurrentMethod =
new MethodInfo(rawCommentText, typeParameters, name, signature, containingClass,
realContainingClass, isPublic, isProtected, isPackagePrivate, isPrivate, isFinal,
- isStatic, isSynthetic, isAbstract, isSynchronized, isNative, isAnnotationElement, kind,
- flatSignature, overriddenMethod, returnType, parameters, thrownExceptions, position,
- annotations);
+ isStatic, isSynthetic, isAbstract, isSynchronized, isNative, isDefault,
+ isAnnotationElement, kind, flatSignature, overriddenMethod, returnType, parameters,
+ thrownExceptions, position, annotations);
mCurrentMethod.setDeprecated("deprecated".equals(attributes.getValue("deprecated")));
} else if (qName.equals("constructor")) {
@@ -164,9 +167,9 @@
attributes.getValue("name"), null/*signature*/, mCurrentClass, mCurrentClass,
pub, prot, pkgpriv, false/*isPrivate*/, false/*isFinal*/, false/*isStatic*/,
false/*isSynthetic*/, false/*isAbstract*/, false/*isSynthetic*/, false/*isNative*/,
- false /*isAnnotationElement*/, "constructor", null/*flatSignature*/,
- null/*overriddenMethod*/, mCurrentClass.asTypeInfo(), new ArrayList<ParameterInfo>(),
- new ArrayList<ClassInfo>()/*thrownExceptions*/,
+ false/*isDefault*/, false/*isAnnotationElement*/, "constructor",
+ null/*flatSignature*/, null/*overriddenMethod*/, mCurrentClass.asTypeInfo(),
+ new ArrayList<ParameterInfo>(), new ArrayList<ClassInfo>()/*thrownExceptions*/,
SourcePositionInfo.fromXml(attributes.getValue("source")),
new ArrayList<AnnotationInstanceInfo>()/*annotations*/);
mCurrentMethod.setDeprecated("deprecated".equals(attributes.getValue("deprecated")));
@@ -202,8 +205,10 @@
TypeInfo type = Converter.obtainTypeFromString(typeName);
boolean isVarArg = typeName.endsWith("...");
SourcePositionInfo position = null;
-
- mCurrentMethod.addParameter(new ParameterInfo(name, typeName, type, isVarArg, position));
+ List<AnnotationInstanceInfo> annotations = Collections.emptyList();
+
+ mCurrentMethod.addParameter(
+ new ParameterInfo(name, typeName, type, isVarArg, position, annotations));
mCurrentMethod.setVarargs(isVarArg);
} else if (qName.equals("exception")) {
mCurrentMethod.addException(attributes.getValue("type"));
diff --git a/test/com/google/doclava/StubsTest.java b/test/com/google/doclava/StubsTest.java
new file mode 100644
index 0000000..c2e726c
--- /dev/null
+++ b/test/com/google/doclava/StubsTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 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 com.google.doclava;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class StubsTest extends TestCase {
+
+ public void testLicenseWithPackage() throws IOException {
+ assertEquals("// Foo\n", parseLicenseHeader(
+ "// Foo",
+ "package foo",
+ ""));
+
+ assertEquals("// Foo\n// Bar\n", parseLicenseHeader(
+ "// Foo",
+ "// Bar",
+ "package foo",
+ ""));
+
+ assertEquals("// Foo\n// Bar\n", parseLicenseHeader(
+ "// Foo",
+ "// Bar",
+ " package foo",
+ ""));
+
+ assertEquals("// Foo\n// Bar package bar food\n", parseLicenseHeader(
+ "// Foo",
+ "// Bar package bar food",
+ "package foo",
+ ""));
+ }
+
+ public void testLicenseWithImport() throws IOException {
+ assertEquals("// Foo\n", parseLicenseHeader(
+ "// Foo",
+ "import foo",
+ ""));
+
+ assertEquals("// Foo\n// Bar\n", parseLicenseHeader(
+ "// Foo",
+ "// Bar",
+ "import foo",
+ ""));
+
+ assertEquals("// Foo\n// Bar\n", parseLicenseHeader(
+ "// Foo",
+ "// Bar",
+ " import foo;",
+ ""));
+
+ assertEquals("// Foo\n// Bar import bar food\n", parseLicenseHeader(
+ "// Foo",
+ "// Bar import bar food",
+ "import foo",
+ ""));
+ }
+
+ public void testLineStripping() throws IOException {
+ assertEquals("/*\n* Foo\n*/\n//\n// Bar\n//\n", parseLicenseHeader(
+ "/*",
+ " * Foo",
+ " */",
+ "//",
+ " // Bar",
+ " //",
+ "package foo",
+ ""));
+ }
+
+ public void testMultipleComments() throws IOException {
+ assertEquals("/*\n* Foo\n*/\n/*\n* Bar\n*/\n", parseLicenseHeader(
+ "/*",
+ " * Foo",
+ " */",
+ "/*",
+ " * Bar",
+ " */",
+ "package foo",
+ ""));
+ }
+
+ private static String parseLicenseHeader(String... lines) throws IOException {
+ StringBuilder builder = new StringBuilder(8192);
+ for (String line : lines) {
+ builder.append(line);
+ builder.append('\n');
+ }
+
+ return Stubs.parseLicenseHeader(
+ new ByteArrayInputStream(builder.toString().getBytes(StandardCharsets.UTF_8)));
+ }
+}
+