Merge "Add option to print @removed API as DEX signatures"
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 1f2891a..d3516f7 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -183,6 +183,7 @@
     boolean offlineMode = false;
     String apiFile = null;
     String removedApiFile = null;
+    String removedDexApiFile = null;
     String exactApiFile = null;
     String privateApiFile = null;
     String privateDexApiFile = null;
@@ -304,6 +305,8 @@
         apiFile = a[1];
       } else if (a[0].equals("-removedApi")) {
         removedApiFile = a[1];
+      } else if (a[0].equals("-removedDexApi")) {
+        removedDexApiFile = a[1];
       } else if (a[0].equals("-exactApi")) {
         exactApiFile = a[1];
       } else if (a[0].equals("-privateApi")) {
@@ -547,9 +550,11 @@
 
     // Stubs
     if (stubsDir != null || apiFile != null || proguardFile != null || removedApiFile != null
-        || exactApiFile != null || privateApiFile != null || privateDexApiFile != null) {
-      Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, removedApiFile, exactApiFile,
-          privateApiFile, privateDexApiFile, stubPackages, stubImportPackages, stubSourceOnly);
+        || removedDexApiFile != null || exactApiFile != null || privateApiFile != null
+        || privateDexApiFile != null) {
+      Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, removedApiFile, removedDexApiFile,
+          exactApiFile, privateApiFile, privateDexApiFile, stubPackages, stubImportPackages,
+          stubSourceOnly);
     }
 
     Errors.printErrors();
@@ -841,6 +846,9 @@
     if (option.equals("-removedApi")) {
       return 2;
     }
+    if (option.equals("-removedDexApi")) {
+      return 2;
+    }
     if (option.equals("-exactApi")) {
       return 2;
     }
diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java
index 394c6fd..a194fba 100644
--- a/src/com/google/doclava/Stubs.java
+++ b/src/com/google/doclava/Stubs.java
@@ -47,8 +47,9 @@
 
 public class Stubs {
   public static void writeStubsAndApi(String stubsDir, String apiFile, String keepListFile,
-      String removedApiFile, String exactApiFile, String privateApiFile, String privateDexApiFile,
-      HashSet<String> stubPackages, HashSet<String> stubImportPackages, boolean stubSourceOnly) {
+      String removedApiFile, String removedDexApiFile, String exactApiFile, String privateApiFile,
+      String privateDexApiFile, HashSet<String> stubPackages, HashSet<String> stubImportPackages,
+      boolean stubSourceOnly) {
     // figure out which classes we need
     final HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
     Collection<ClassInfo> all = Converter.allClasses();
@@ -56,6 +57,7 @@
     PrintStream apiWriter = null;
     PrintStream keepListWriter = null;
     PrintStream removedApiWriter = null;
+    PrintStream removedDexApiWriter = null;
     PrintStream exactApiWriter = null;
     PrintStream privateApiWriter = null;
     PrintStream privateDexApiWriter = null;
@@ -91,6 +93,17 @@
             "Cannot open file for write");
       }
     }
+    if (removedDexApiFile != null) {
+      try {
+        File removedDexApi = new File(removedDexApiFile);
+        removedDexApi.getParentFile().mkdirs();
+        removedDexApiWriter = new PrintStream(
+            new BufferedOutputStream(new FileOutputStream(removedDexApi)));
+      } catch (FileNotFoundException e) {
+        Errors.error(Errors.IO_ERROR, new SourcePositionInfo(removedDexApiFile, 0, 0),
+            "Cannot open file for write");
+      }
+    }
     if (exactApiFile != null) {
       try {
         File exactApi = new File(exactApiFile);
@@ -239,7 +252,8 @@
       }
     }
 
-    if (privateApiWriter != null || privateDexApiWriter != null || removedApiWriter != null) {
+    if (privateApiWriter != null || privateDexApiWriter != null || removedApiWriter != null
+            || removedDexApiWriter != null) {
       allClassesByPackage = Converter.allClasses().stream()
           // Make sure that the files only contains information from the required packages.
           .filter(ci -> stubPackages == null
@@ -249,11 +263,12 @@
 
     final boolean ignoreShown = Doclava.showUnannotated;
 
+    Predicate<MemberInfo> memberIsNotCloned = (x -> !x.isCloned());
+
     FilterPredicate apiFilter = new FilterPredicate(new ApiPredicate().setIgnoreShown(ignoreShown));
     ApiPredicate apiReference = new ApiPredicate().setIgnoreShown(true);
     Predicate<MemberInfo> apiEmit = apiFilter.and(new ElidingPredicate(apiReference));
 
-    Predicate<MemberInfo> memberIsNotCloned = (x -> !x.isCloned());
     Predicate<MemberInfo> privateEmit = memberIsNotCloned.and(apiFilter.negate());
     Predicate<MemberInfo> privateReference = (x -> true);
 
@@ -261,6 +276,7 @@
         new FilterPredicate(new ApiPredicate().setIgnoreShown(ignoreShown).setMatchRemoved(true));
     ApiPredicate removedReference = new ApiPredicate().setIgnoreShown(true).setIgnoreRemoved(true);
     Predicate<MemberInfo> removedEmit = removedFilter.and(new ElidingPredicate(removedReference));
+    Predicate<MemberInfo> removedDexEmit = memberIsNotCloned.and(removedFilter);
 
     // Write out the current API
     if (apiWriter != null) {
@@ -291,6 +307,12 @@
       writeApi(removedApiWriter, allClassesByPackage, removedEmit, removedReference);
       removedApiWriter.close();
     }
+
+    // Write out the removed DEX API
+    if (removedDexApiWriter != null) {
+      writeDexApi(removedDexApiWriter, allClassesByPackage, removedDexEmit);
+      removedDexApiWriter.close();
+    }
   }
 
   private static boolean shouldWriteStub(final String packageName,