Offer to emit exact APIs instead of superset.
am: ef8f3ec4f2
Change-Id: I730e5214a9c9755be07ebfe567d00fd46690ca15
diff --git a/src/com/google/doclava/ClassInfo.java b/src/com/google/doclava/ClassInfo.java
index b549167..b2e3344 100644
--- a/src/com/google/doclava/ClassInfo.java
+++ b/src/com/google/doclava/ClassInfo.java
@@ -764,78 +764,20 @@
mRemovedMethods = Collections.unmodifiableList(removedMethods);
}
- /**
- * @param allMethods all methods regardless of access levels. Selects the
- * removed, public/protected ones and store them. If a class is removed, all its members
- * are removed, even if the member may not have a @removed tag.
- */
- public void setRemovedSelfMethods(List<MethodInfo> allMethods) {
- List<MethodInfo> removedSelfMethods = new ArrayList<MethodInfo>();
- for (MethodInfo method : allMethods) {
- if ((this.isRemoved() || method.isRemoved()) && (method.isPublic() || method.isProtected()) &&
- (this.isPublic() || this.isProtected()) &&
- (method.findOverriddenMethod(method.name(), method.signature()) == null)) {
- removedSelfMethods.add(method);
- }
- }
-
- Collections.sort(removedSelfMethods, MethodInfo.comparator);
- mRemovedSelfMethods = Collections.unmodifiableList(removedSelfMethods);
+ public void setExhaustiveConstructors(List<MethodInfo> constructors) {
+ mExhaustiveConstructors = constructors;
}
- /**
- * @param allCtors all constructors regardless of access levels.
- * But only the public/protected removed constructors will be stored by the method.
- * Removed constructors should never be deleted from source code because
- * they were once public API.
- */
- public void setRemovedConstructors(List<MethodInfo> allCtors) {
- List<MethodInfo> ctors = new ArrayList<MethodInfo>();
- for (MethodInfo ctor : allCtors) {
- if ((this.isRemoved() || ctor.isRemoved()) && (ctor.isPublic() || ctor.isProtected()) &&
- (this.isPublic() || this.isProtected())) {
- ctors.add(ctor);
- }
- }
-
- Collections.sort(ctors, MethodInfo.comparator);
- mRemovedConstructors = Collections.unmodifiableList(ctors);
+ public void setExhaustiveMethods(List<MethodInfo> methods) {
+ mExhaustiveMethods = methods;
}
- /**
- * @param allFields all fields regardless of access levels. Selects the
- * removed, public/protected ones and store them. If a class is removed, all its members
- * are removed, even if the member may not have a @removed tag.
- */
- public void setRemovedSelfFields(List<FieldInfo> allFields) {
- List<FieldInfo> fields = new ArrayList<FieldInfo>();
- for (FieldInfo field : allFields) {
- if ((this.isRemoved() || field.isRemoved()) && (field.isPublic() || field.isProtected()) &&
- (this.isPublic() || this.isProtected())) {
- fields.add(field);
- }
- }
-
- Collections.sort(fields, FieldInfo.comparator);
- mRemovedSelfFields = Collections.unmodifiableList(fields);
+ public void setExhaustiveEnumConstants(List<FieldInfo> enumConstants) {
+ mExhaustiveEnumConstants = enumConstants;
}
- /**
- * @param allEnumConstants all enum constants regardless of access levels. Selects the
- * removed, public/protected ones and store them. If a class is removed, all its members
- * are removed, even if the member may not have a @removed tag.
- */
- public void setRemovedEnumConstants(List<FieldInfo> allEnumConstants) {
- List<FieldInfo> enums = new ArrayList<FieldInfo>();
- for (FieldInfo field : allEnumConstants) {
- if ((this.isRemoved() || field.isRemoved()) && (field.isPublic() || field.isProtected()) &&
- (this.isPublic() || this.isProtected())) {
- enums.add(field);
- }
- }
-
- Collections.sort(enums, FieldInfo.comparator);
- mRemovedEnumConstants = Collections.unmodifiableList(enums);
+ public void setExhaustiveFields(List<FieldInfo> fields) {
+ mExhaustiveFields = fields;
}
/**
@@ -846,58 +788,20 @@
return mRemovedMethods;
}
- /**
- * @return all public/protected methods that are removed. @removed methods should never be
- * deleted from source code because they were once public API. Methods that override
- * a parent method will not be included, because deleting them does not break the API.
- */
- public List<MethodInfo> getRemovedSelfMethods() {
- return mRemovedSelfMethods;
+ public List<MethodInfo> getExhaustiveConstructors() {
+ return mExhaustiveConstructors;
}
- /**
- * @return all public constructors that are removed.
- * removed constructors should never be deleted from source code because they
- * were once public API.
- * The returned list is sorted and unmodifiable.
- */
- public List<MethodInfo> getRemovedConstructors() {
- return mRemovedConstructors;
+ public List<MethodInfo> getExhaustiveMethods() {
+ return mExhaustiveMethods;
}
- /**
- * @return all public/protected fields that are removed.
- * removed members should never be deleted from source code because they were once public API.
- * The returned list is sorted and unmodifiable.
- */
- public List<FieldInfo> getRemovedSelfFields() {
- return mRemovedSelfFields;
+ public List<FieldInfo> getExhaustiveEnumConstants() {
+ return mExhaustiveEnumConstants;
}
- /**
- * @return all public/protected enumConstants that are removed.
- * removed members should never be deleted from source code
- * because they were once public API.
- * The returned list is sorted and unmodifiable.
- */
- public List<FieldInfo> getRemovedSelfEnumConstants() {
- return mRemovedEnumConstants;
- }
-
- /**
- * @return true if this class contains any self members that are removed
- */
- public boolean hasRemovedSelfMembers() {
- List<FieldInfo> removedSelfFields = getRemovedSelfFields();
- List<FieldInfo> removedSelfEnumConstants = getRemovedSelfEnumConstants();
- List<MethodInfo> removedSelfMethods = getRemovedSelfMethods();
- List<MethodInfo> removedConstructors = getRemovedConstructors();
- if (removedSelfFields.size() + removedSelfEnumConstants.size()
- + removedSelfMethods.size() + removedConstructors.size() == 0) {
- return false;
- } else {
- return true;
- }
+ public List<FieldInfo> getExhaustiveFields() {
+ return mExhaustiveFields;
}
public void addMethod(MethodInfo method) {
@@ -1950,12 +1854,12 @@
// Resolutions
private ArrayList<Resolution> mResolutions;
- private List<MethodInfo> mRemovedConstructors; // immutable after you set its value.
- // @removed self methods that do not override any parent methods
- private List<MethodInfo> mRemovedSelfMethods; // immutable after you set its value.
private List<MethodInfo> mRemovedMethods; // immutable after you set its value.
- private List<FieldInfo> mRemovedSelfFields; // immutable after you set its value.
- private List<FieldInfo> mRemovedEnumConstants; // immutable after you set its value.
+
+ private List<MethodInfo> mExhaustiveConstructors; // immutable after you set its value.
+ private List<MethodInfo> mExhaustiveMethods; // immutable after you set its value.
+ private List<FieldInfo> mExhaustiveEnumConstants; // immutable after you set its value.
+ private List<FieldInfo> mExhaustiveFields; // immutable after you set its value.
/**
* Returns true if {@code cl} implements the interface {@code iface} either by either being that
diff --git a/src/com/google/doclava/Converter.java b/src/com/google/doclava/Converter.java
index 1bc3563..ee91960 100644
--- a/src/com/google/doclava/Converter.java
+++ b/src/com/google/doclava/Converter.java
@@ -137,14 +137,14 @@
cl.setRemovedMethods(
new ArrayList<MethodInfo>(Arrays.asList(Converter.getRemovedMethods(c.methods(false)))));
- cl.setRemovedSelfMethods(
- new ArrayList<MethodInfo>(Converter.convertAllMethods(c.methods(false))));
- cl.setRemovedConstructors(
+ cl.setExhaustiveConstructors(
new ArrayList<MethodInfo>(Converter.convertAllMethods(c.constructors(false))));
- cl.setRemovedSelfFields(
- new ArrayList<FieldInfo>(Converter.convertAllFields(c.fields(false))));
- cl.setRemovedEnumConstants(
+ cl.setExhaustiveMethods(
+ new ArrayList<MethodInfo>(Converter.convertAllMethods(c.methods(false))));
+ cl.setExhaustiveEnumConstants(
new ArrayList<FieldInfo>(Converter.convertAllFields(c.enumConstants())));
+ cl.setExhaustiveFields(
+ new ArrayList<FieldInfo>(Converter.convertAllFields(c.fields(false))));
cl.setNonWrittenConstructors(
new ArrayList<MethodInfo>(Arrays.asList(Converter.convertNonWrittenConstructors(
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 8c29ac7..9359930 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -176,6 +176,7 @@
boolean offlineMode = false;
String apiFile = null;
String removedApiFile = null;
+ String exactApiFile = null;
String debugStubsFile = "";
HashSet<String> stubPackages = null;
ArrayList<String> knownTagsFiles = new ArrayList<String>();
@@ -280,8 +281,9 @@
apiFile = a[1];
} else if (a[0].equals("-removedApi")) {
removedApiFile = a[1];
- }
- else if (a[0].equals("-nodocs")) {
+ } else if (a[0].equals("-exactApi")) {
+ exactApiFile = a[1];
+ } else if (a[0].equals("-nodocs")) {
generateDocs = false;
} else if (a[0].equals("-noassets")) {
includeAssets = false;
@@ -496,8 +498,10 @@
}
// Stubs
- if (stubsDir != null || apiFile != null || proguardFile != null || removedApiFile != null) {
- Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, removedApiFile, stubPackages);
+ if (stubsDir != null || apiFile != null || proguardFile != null || removedApiFile != null
+ || exactApiFile != null) {
+ Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, removedApiFile, exactApiFile,
+ stubPackages);
}
Errors.printErrors();
@@ -751,6 +755,9 @@
if (option.equals("-removedApi")) {
return 2;
}
+ if (option.equals("-exactApi")) {
+ return 2;
+ }
if (option.equals("-nodocs")) {
return 1;
}
diff --git a/src/com/google/doclava/MemberInfo.java b/src/com/google/doclava/MemberInfo.java
index 2d4a682..6c5aad3 100644
--- a/src/com/google/doclava/MemberInfo.java
+++ b/src/com/google/doclava/MemberInfo.java
@@ -169,6 +169,10 @@
return mAnnotations;
}
+ public boolean hasShowAnnotation() {
+ return mShowAnnotations != null && mShowAnnotations.size() > 0;
+ }
+
public ArrayList<AnnotationInstanceInfo> showAnnotations() {
return mShowAnnotations;
}
diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java
index e1ada09..2010315 100644
--- a/src/com/google/doclava/Stubs.java
+++ b/src/com/google/doclava/Stubs.java
@@ -37,17 +37,20 @@
import java.util.List;
import java.util.Scanner;
import java.util.Set;
+import java.util.function.Predicate;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
public class Stubs {
public static void writeStubsAndApi(String stubsDir, String apiFile, String keepListFile,
- String removedApiFile, HashSet<String> stubPackages) {
+ String removedApiFile, String exactApiFile, HashSet<String> stubPackages) {
// figure out which classes we need
final HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
ClassInfo[] all = Converter.allClasses();
PrintStream apiWriter = null;
PrintStream keepListWriter = null;
PrintStream removedApiWriter = null;
+ PrintStream exactApiWriter = null;
if (apiFile != null) {
try {
@@ -80,6 +83,17 @@
"Cannot open file for write");
}
}
+ if (exactApiFile != null) {
+ try {
+ File exactApi = new File(exactApiFile);
+ exactApi.getParentFile().mkdirs();
+ exactApiWriter = new PrintStream(
+ new BufferedOutputStream(new FileOutputStream(exactApi)));
+ } catch (FileNotFoundException e) {
+ Errors.error(Errors.IO_ERROR, new SourcePositionInfo(exactApiFile, 0, 0),
+ "Cannot open file for write");
+ }
+ }
// If a class is public or protected, not hidden, and marked as included,
// then we can't strip it
for (ClassInfo cl : all) {
@@ -214,11 +228,18 @@
allPackageClassMap.put(cl.containingPackage(), classes);
}
}
- // write out the removed Api
+ // Write out the removed API
if (removedApiWriter != null) {
- writeRemovedApi(removedApiWriter, allPackageClassMap, notStrippable);
+ writePredicateApi(removedApiWriter, allPackageClassMap, notStrippable,
+ new RemovedPredicate());
removedApiWriter.close();
}
+ // Write out the exact API
+ if (exactApiWriter != null) {
+ writePredicateApi(exactApiWriter, allPackageClassMap, notStrippable,
+ new ExactPredicate());
+ exactApiWriter.close();
+ }
}
private static boolean shouldWriteStub(final String packageName,
@@ -1175,8 +1196,52 @@
return returnString;
}
- static void writeRemovedApi(PrintStream apiWriter, HashMap<PackageInfo,
- List<ClassInfo>> allPackageClassMap, Set<ClassInfo> notStrippable) {
+ public static class RemovedPredicate implements Predicate<MemberInfo> {
+ @Override
+ public boolean test(MemberInfo member) {
+ final ClassInfo clazz = member.containingClass();
+ final boolean removed = clazz.isRemoved() || member.isRemoved();
+ final boolean clazzVisible = clazz.isPublic() || clazz.isProtected();
+ final boolean memberVisible = member.isPublic() || member.isProtected();
+
+ if (removed && clazzVisible && memberVisible) {
+ if (member instanceof MethodInfo) {
+ final MethodInfo method = (MethodInfo) member;
+ return (method.findOverriddenMethod(method.name(), method.signature()) == null);
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public static class ExactPredicate implements Predicate<MemberInfo> {
+ @Override
+ public boolean test(MemberInfo member) {
+ final ClassInfo clazz = member.containingClass();
+ final boolean hasShowAnnotation = member.hasShowAnnotation()
+ || member.containingClass().hasShowAnnotation();
+ final boolean clazzVisible = clazz.isPublic() || clazz.isProtected();
+ final boolean memberVisible = member.isPublic() || member.isProtected();
+
+ if (hasShowAnnotation && !member.isHiddenOrRemoved() && clazzVisible && memberVisible) {
+ if (member instanceof MethodInfo) {
+ final MethodInfo method = (MethodInfo) member;
+ return (method.findOverriddenMethod(method.name(), method.signature()) == null);
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ static void writePredicateApi(PrintStream apiWriter,
+ HashMap<PackageInfo, List<ClassInfo>> allPackageClassMap, Set<ClassInfo> notStrippable,
+ Predicate<MemberInfo> predicate) {
final PackageInfo[] packages = allPackageClassMap.keySet().toArray(new PackageInfo[0]);
Arrays.sort(packages, PackageInfo.comparator);
for (PackageInfo pkg : packages) {
@@ -1185,15 +1250,8 @@
Collections.sort(classes, ClassInfo.comparator);
boolean hasWrittenPackageHead = false;
for (ClassInfo cl : classes) {
- if (cl.hasRemovedSelfMembers()) {
- if (!hasWrittenPackageHead) {
- hasWrittenPackageHead = true;
- apiWriter.print("package ");
- apiWriter.print(pkg.qualifiedName());
- apiWriter.print(" {\n\n");
- }
- writeClassRemovedSelfMembers(apiWriter, cl, notStrippable);
- }
+ hasWrittenPackageHead = writeClassPredicateSelfMembers(apiWriter, cl, notStrippable,
+ predicate, hasWrittenPackageHead);
}
// the package contains some classes with some removed members
@@ -1206,8 +1264,30 @@
/**
* Write the removed members of the class to removed.txt
*/
- private static void writeClassRemovedSelfMembers(PrintStream apiWriter, ClassInfo cl,
- Set<ClassInfo> notStrippable) {
+ private static boolean writeClassPredicateSelfMembers(PrintStream apiWriter, ClassInfo cl,
+ Set<ClassInfo> notStrippable, Predicate<MemberInfo> predicate,
+ boolean hasWrittenPackageHead) {
+
+ List<MethodInfo> constructors = cl.getExhaustiveConstructors().stream().filter(predicate)
+ .sorted(MethodInfo.comparator).collect(Collectors.toList());
+ List<MethodInfo> methods = cl.getExhaustiveMethods().stream().filter(predicate)
+ .sorted(MethodInfo.comparator).collect(Collectors.toList());
+ List<FieldInfo> enums = cl.getExhaustiveEnumConstants().stream().filter(predicate)
+ .sorted(FieldInfo.comparator).collect(Collectors.toList());
+ List<FieldInfo> fields = cl.getExhaustiveFields().stream().filter(predicate)
+ .sorted(FieldInfo.comparator).collect(Collectors.toList());
+
+ if (constructors.isEmpty() && methods.isEmpty() && enums.isEmpty() && fields.isEmpty()) {
+ return hasWrittenPackageHead;
+ }
+
+ if (!hasWrittenPackageHead) {
+ hasWrittenPackageHead = true;
+ apiWriter.print("package ");
+ apiWriter.print(cl.containingPackage().qualifiedName());
+ apiWriter.print(" {\n\n");
+ }
+
apiWriter.print(" ");
apiWriter.print(cl.scope());
if (cl.isStatic()) {
@@ -1251,27 +1331,21 @@
apiWriter.print(" {\n");
- List<MethodInfo> constructors = cl.getRemovedConstructors();
for (MethodInfo mi : constructors) {
writeConstructorApi(apiWriter, mi);
}
-
- List<MethodInfo> methods = cl.getRemovedSelfMethods();
for (MethodInfo mi : methods) {
writeMethodApi(apiWriter, mi);
}
-
- List<FieldInfo> enums = cl.getRemovedSelfEnumConstants();
for (FieldInfo fi : enums) {
writeFieldApi(apiWriter, fi, "enum_constant");
}
-
- List<FieldInfo> fields = cl.getRemovedSelfFields();
for (FieldInfo fi : fields) {
writeFieldApi(apiWriter, fi, "field");
}
apiWriter.print(" }\n\n");
+ return hasWrittenPackageHead;
}
public static void writeApi(PrintStream apiWriter, Collection<PackageInfo> pkgs) {
diff --git a/src/com/google/doclava/TypeInfo.java b/src/com/google/doclava/TypeInfo.java
index 689ff88..7bba560 100644
--- a/src/com/google/doclava/TypeInfo.java
+++ b/src/com/google/doclava/TypeInfo.java
@@ -524,9 +524,11 @@
*/
public static Map<String, TypeInfo> getTypeArgumentMapping(TypeInfo generic, TypeInfo typed) {
Map<String, TypeInfo> map = new HashMap<String, TypeInfo>();
- for (int i = 0; i < generic.typeArguments().size(); i++) {
- if (typed.typeArguments() != null && typed.typeArguments().size() > i) {
- map.put(generic.typeArguments().get(i).simpleTypeName(), typed.typeArguments().get(i));
+ if (generic != null && generic.typeArguments() != null) {
+ for (int i = 0; i < generic.typeArguments().size(); i++) {
+ if (typed.typeArguments() != null && typed.typeArguments().size() > i) {
+ map.put(generic.typeArguments().get(i).simpleTypeName(), typed.typeArguments().get(i));
+ }
}
}
return map;