am cc6d31d2: am 3d7e7785: am d097b65e: am 4ee085a7: fix error in doclava that was causing \'toroot\' variable to show up at the beginning of federated URLs bug: 13512870

* commit 'cc6d31d22f4a1d5f2439f45a3ddc848cb02970ac':
diff --git a/src/com/google/doclava/ClassInfo.java b/src/com/google/doclava/ClassInfo.java
index a750b11..3cda7ec 100644
--- a/src/com/google/doclava/ClassInfo.java
+++ b/src/com/google/doclava/ClassInfo.java
@@ -117,10 +117,18 @@
     mRealInterfaces = new ArrayList<ClassInfo>(interfaces);
     mRealInterfaceTypes = interfaceTypes;
     mInnerClasses = innerClasses;
+    // mAllConstructors will not contain *all* constructors. Only the constructors that pass
+    // checkLevel. @see {@link Converter#convertMethods(ConstructorDoc[])}
     mAllConstructors = constructors;
+    // mAllSelfMethods will not contain *all* self methods. Only the methods that pass
+    // checkLevel. @see {@link Converter#convertMethods(MethodDoc[])}
     mAllSelfMethods = methods;
     mAnnotationElements = annotationElements;
+    // mAllSelfFields will not contain *all* self fields. Only the fields that pass
+    // checkLevel. @see {@link Converter#convetFields(FieldDoc[])}
     mAllSelfFields = fields;
+    // mEnumConstants will not contain *all* enum constants. Only the enums that pass
+    // checkLevel. @see {@link Converter#convetFields(FieldDoc[])}
     mEnumConstants = enumConstants;
     mContainingPackage = containingPackage;
     mContainingClass = containingClass;
@@ -162,16 +170,17 @@
     return mTypeParameters;
   }
 
+  /**
+   * @return true if this class needs to be shown in api txt, based on the
+   * hidden/removed status of the class and the show level setting in doclava.
+   */
   public boolean checkLevel() {
-    int val = mCheckLevel;
-    if (val >= 0) {
-      return val != 0;
-    } else {
-      boolean v =
-          Doclava.checkLevel(mIsPublic, mIsProtected, mIsPackagePrivate, mIsPrivate, isHidden());
-      mCheckLevel = v ? 1 : 0;
-      return v;
+    if (mCheckLevel == null) {
+      mCheckLevel = Doclava.checkLevel(mIsPublic, mIsProtected, mIsPackagePrivate, mIsPrivate,
+          isHiddenOrRemoved());
     }
+
+    return mCheckLevel;
   }
 
   public int compareTo(Object that) {
@@ -350,7 +359,7 @@
 
       mConstructors = new ArrayList<MethodInfo>();
       for (MethodInfo m : mAllConstructors) {
-        if (!m.isHidden()) {
+        if (!m.isHiddenOrRemoved()) {
             mConstructors.add(m);
         }
       }
@@ -462,13 +471,13 @@
       }
 
       for (FieldInfo field : selfFields()) {
-        if (!field.isHidden()) {
+        if (!field.isHiddenOrRemoved()) {
             all.put(field.name(), field);
         }
       }
 
       for (FieldInfo enumConst : mEnumConstants) {
-        if (!enumConst.isHidden()) {
+        if (!enumConst.isHiddenOrRemoved()) {
             all.put(enumConst.name(), enumConst);
         }
       }
@@ -500,7 +509,7 @@
       }
 
       for (FieldInfo f : mAllSelfFields) {
-          if (!f.isHidden()) {
+          if (!f.isHiddenOrRemoved()) {
               fields.put(f.name(), f);
           }
       }
@@ -539,7 +548,7 @@
       if (mAllSelfMethods != null) {
         for (MethodInfo m : mAllSelfMethods) {
           if (m.checkLevel()) {
-              methods.put(m.name() + m.signature(), m);
+            methods.put(m.name() + m.signature(), m);
           }
         }
       }
@@ -555,6 +564,150 @@
     return mAllSelfMethods;
   }
 
+  /**
+   * @param removedMethods the removed methods regardless of access levels.
+   */
+  public void setRemovedMethods(List<MethodInfo> removedMethods) {
+    Collections.sort(removedMethods, MethodInfo.comparator);
+    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);
+  }
+
+  /**
+   * @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);
+  }
+
+  /**
+   * @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);
+  }
+
+  /**
+   * @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);
+  }
+
+  /**
+   * @return all methods that are marked as removed, regardless of access levels.
+   * The returned list is sorted and unmodifiable.
+   */
+  public List<MethodInfo> getRemovedMethods() {
+    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;
+  }
+
+  /**
+   * @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;
+  }
+
+  /**
+   * @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;
+  }
+
+  /**
+   * @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 void addMethod(MethodInfo method) {
     mApiCheckMethods.put(method.getHashableName(), method);
 
@@ -1195,29 +1348,26 @@
 
   @Override
   public boolean isHidden() {
-    int val = mHidden;
-    if (val >= 0) {
-      return val != 0;
-    } else {
-      boolean v = isHiddenImpl();
-      mHidden = v ? 1 : 0;
-      return v;
+    if (mHidden == null) {
+      mHidden = isHiddenImpl();
     }
+
+    return mHidden;
   }
 
+  /**
+   * @return true if the containing package has @hide comment, or an ancestor
+   * class of this class is hidden, or this class has @hide comment.
+   */
   public boolean isHiddenImpl() {
     ClassInfo cl = this;
     while (cl != null) {
-      PackageInfo pkg = cl.containingPackage();
-      if (pkg != null && pkg.isHidden()) {
-        return true;
+      if (cl.hasShowAnnotation()) {
+        return false;
       }
-      if (cl.annotations() != null) {
-        for (AnnotationInstanceInfo info : cl.annotations()) {
-          if (Doclava.showAnnotations.contains(info.type().qualifiedName())) {
-            return false;
-          }
-        }
+      PackageInfo pkg = cl.containingPackage();
+      if (pkg != null && pkg.hasHideComment()) {
+        return true;
       }
       if (cl.comment().isHidden()) {
         return true;
@@ -1227,6 +1377,53 @@
     return false;
   }
 
+  @Override
+  public boolean isRemoved() {
+    if (mRemoved == null) {
+      mRemoved = isRemovedImpl();
+    }
+
+    return mRemoved;
+  }
+
+  /**
+   * @return true if the containing package has @removed comment, or an ancestor
+   * class of this class is removed, or this class has @removed comment.
+   */
+  public boolean isRemovedImpl() {
+    ClassInfo cl = this;
+    while (cl != null) {
+      if (cl.hasShowAnnotation()) {
+        return false;
+      }
+      PackageInfo pkg = cl.containingPackage();
+      if (pkg != null && pkg.hasRemovedComment()) {
+        return true;
+      }
+      if (cl.comment().isRemoved()) {
+        return true;
+      }
+      cl = cl.containingClass();
+    }
+    return false;
+  }
+
+  @Override
+  public boolean isHiddenOrRemoved() {
+    return isHidden() || isRemoved();
+  }
+
+  public boolean hasShowAnnotation() {
+    if (annotations() != null) {
+      for (AnnotationInstanceInfo info : annotations()) {
+        if (Doclava.showAnnotations.contains(info.type().qualifiedName())) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   private MethodInfo matchMethod(ArrayList<MethodInfo> methods, String name, String[] params,
       String[] dimensions, boolean varargs) {
     for (MethodInfo method : methods) {
@@ -1474,7 +1671,11 @@
   private ArrayList<ClassInfo> mInterfaces;
   private ArrayList<TypeInfo> mRealInterfaceTypes;
   private ArrayList<ClassInfo> mInnerClasses;
+  // mAllConstructors will not contain *all* constructors. Only the constructors that pass
+  // checkLevel. @see {@link Converter#convertMethods(ConstructorDoc[])}
   private ArrayList<MethodInfo> mAllConstructors = new ArrayList<MethodInfo>();
+  // mAllSelfMethods will not contain *all* self methods. Only the methods that pass
+  // checkLevel. @see {@link Converter#convertMethods(MethodDoc[])}
   private ArrayList<MethodInfo> mAllSelfMethods = new ArrayList<MethodInfo>();
   private ArrayList<MethodInfo> mAnnotationElements = new ArrayList<MethodInfo>(); // if this class is an annotation
   private ArrayList<FieldInfo> mAllSelfFields = new ArrayList<FieldInfo>();
@@ -1498,8 +1699,9 @@
   private ArrayList<FieldInfo> mFields;
   private ArrayList<TypeInfo> mTypeParameters;
   private ArrayList<MethodInfo> mHiddenMethods;
-  private int mHidden = -1;
-  private int mCheckLevel = -1;
+  private Boolean mHidden = null;
+  private Boolean mRemoved = null;
+  private Boolean mCheckLevel = null;
   private String mReasonIncluded;
   private ArrayList<MethodInfo> mNonWrittenConstructors;
   private boolean mIsDeprecated;
@@ -1513,6 +1715,13 @@
   // 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.
+
   /**
    * Returns true if {@code cl} implements the interface {@code iface} either by either being that
    * interface, implementing that interface or extending a type that implements the interface.
@@ -1798,7 +2007,7 @@
     return consistent;
   }
 
-  // Find a superclass implementation of the given method.
+  // Find a superclass implementation of the given method based on the methods in mApiCheckMethods.
   public static MethodInfo overriddenMethod(MethodInfo candidate, ClassInfo newClassObj) {
     if (newClassObj == null) {
       return null;
diff --git a/src/com/google/doclava/Comment.java b/src/com/google/doclava/Comment.java
index 70f4f30..c93cda7 100644
--- a/src/com/google/doclava/Comment.java
+++ b/src/com/google/doclava/Comment.java
@@ -458,37 +458,35 @@
   }
 
   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;
+    if (mHidden == null) {
+      mHidden = !Doclava.checkLevel(Doclava.SHOW_HIDDEN) &&
+          (mText != null) && (mText.indexOf("@hide") >= 0 || mText.indexOf("@pending") >= 0);
     }
+    return mHidden;
+  }
+
+  public boolean isRemoved() {
+    if (mRemoved == null) {
+        mRemoved = !Doclava.checkLevel(Doclava.SHOW_HIDDEN) &&
+            (mText != null) && (mText.indexOf("@removed") >= 0);
+    }
+
+    return mRemoved;
   }
 
   public boolean isDocOnly() {
-    if (mDocOnly != -1) {
-      return mDocOnly != 0;
-    } else {
-      boolean b = (mText != null) && (mText.indexOf("@doconly") >= 0);
-      mDocOnly = b ? 1 : 0;
-      return b;
+    if (mDocOnly == null) {
+      mDocOnly = (mText != null) && (mText.indexOf("@doconly") >= 0);
     }
+    return mDocOnly;
   }
-  
+
   public boolean isDeprecated() {
-    if (mDeprecated != -1) {
-      return mDeprecated != 0;
-    } else {
-      boolean b = (mText != null) && (mText.indexOf("@deprecated") >= 0);
-      mDeprecated = b ? 1 : 0;
-      return b;
+    if (mDeprecated == null) {
+      mDeprecated = (mText != null) && (mText.indexOf("@deprecated") >= 0);
     }
+
+    return mDeprecated;
   }
 
   private void init() {
@@ -499,6 +497,7 @@
 
   private void initImpl() {
     isHidden();
+    isRemoved();
     isDocOnly();
     isDeprecated();
 
@@ -539,9 +538,10 @@
   }
 
   boolean mInitialized;
-  int mHidden = -1;
-  int mDocOnly = -1;
-  int mDeprecated = -1;
+  Boolean mHidden = null;
+  Boolean mRemoved = null;
+  Boolean mDocOnly = null;
+  Boolean mDeprecated = null;
   String mText;
   ContainerInfo mBase;
   SourcePositionInfo mPosition;
diff --git a/src/com/google/doclava/Converter.java b/src/com/google/doclava/Converter.java
index bdf6af5..e620bf3 100644
--- a/src/com/google/doclava/Converter.java
+++ b/src/com/google/doclava/Converter.java
@@ -41,6 +41,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 
 public class Converter {
   private static RootDoc root;
@@ -131,6 +132,18 @@
 
     cl.setHiddenMethods(
             new ArrayList<MethodInfo>(Arrays.asList(Converter.getHiddenMethods(c.methods(false)))));
+    cl.setRemovedMethods(
+            new ArrayList<MethodInfo>(Arrays.asList(Converter.getRemovedMethods(c.methods(false)))));
+
+    cl.setRemovedSelfMethods(
+        new ArrayList<MethodInfo>(Converter.convertAllMethods(c.methods(false))));
+    cl.setRemovedConstructors(
+        new ArrayList<MethodInfo>(Converter.convertAllMethods(c.constructors(false))));
+    cl.setRemovedSelfFields(
+        new ArrayList<FieldInfo>(Converter.convertAllFields(c.fields(false))));
+    cl.setRemovedEnumConstants(
+        new ArrayList<FieldInfo>(Converter.convertAllFields(c.enumConstants())));
+
     cl.setNonWrittenConstructors(
             new ArrayList<MethodInfo>(Arrays.asList(Converter.convertNonWrittenConstructors(
                     c.constructors(false)))));
@@ -288,81 +301,91 @@
 
   private static MethodInfo[] getHiddenMethods(MethodDoc[] methods) {
     if (methods == null) return null;
-    ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
-    int N = methods.length;
-    for (int i = 0; i < N; i++) {
-      MethodInfo m = Converter.obtainMethod(methods[i]);
-      // System.out.println(m.toString() + ": ");
-      // for (TypeInfo ti : m.getTypeParameters()){
-      // if (ti.asClassInfo() != null){
-      // System.out.println(" " +ti.asClassInfo().toString());
-      // } else {
-      // System.out.println(" null");
-      // }
-      // }
-      if (m.isHidden()) {
-        out.add(m);
+    ArrayList<MethodInfo> hiddenMethods = new ArrayList<MethodInfo>();
+    for (MethodDoc method : methods) {
+      MethodInfo methodInfo = Converter.obtainMethod(method);
+      if (methodInfo.isHidden()) {
+        hiddenMethods.add(methodInfo);
       }
     }
-    return out.toArray(new MethodInfo[out.size()]);
+
+    return hiddenMethods.toArray(new MethodInfo[hiddenMethods.size()]);
+  }
+
+  // Gets the removed methods regardless of access levels
+  private static MethodInfo[] getRemovedMethods(MethodDoc[] methods) {
+    if (methods == null) return null;
+    ArrayList<MethodInfo> removedMethods = new ArrayList<MethodInfo>();
+    for (MethodDoc method : methods) {
+      MethodInfo methodInfo = Converter.obtainMethod(method);
+      if (methodInfo.isRemoved()) {
+        removedMethods.add(methodInfo);
+      }
+    }
+
+    return removedMethods.toArray(new MethodInfo[removedMethods.size()]);
   }
 
   /**
-   * Convert MethodDoc[] into MethodInfo[]. Also filters according to the -private, -public option,
-   * because the filtering doesn't seem to be working in the ClassDoc.constructors(boolean) call.
+   * Converts FieldDoc[] into List<FieldInfo>. No filtering is done.
    */
-  private static MethodInfo[] convertMethods(MethodDoc[] methods) {
-    if (methods == null) return null;
-    ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
-    int N = methods.length;
-    for (int i = 0; i < N; i++) {
-      MethodInfo m = Converter.obtainMethod(methods[i]);
-      // System.out.println(m.toString() + ": ");
-      // for (TypeInfo ti : m.getTypeParameters()){
-      // if (ti.asClassInfo() != null){
-      // System.out.println(" " +ti.asClassInfo().toString());
-      // } else {
-      // System.out.println(" null");
-      // }
-      // }
-      if (m.checkLevel()) {
-        out.add(m);
-      }
+  private static List<FieldInfo> convertAllFields(FieldDoc[] fields) {
+    if (fields == null) return null;
+    List<FieldInfo> allFields = new ArrayList<FieldInfo>();
+
+    for (FieldDoc field : fields) {
+      FieldInfo fieldInfo = Converter.obtainField(field);
+      allFields.add(fieldInfo);
     }
-    return out.toArray(new MethodInfo[out.size()]);
+
+    return allFields;
   }
 
-  private static MethodInfo[] convertMethods(ConstructorDoc[] methods) {
+  /**
+   * Converts ExecutableMemberDoc[] into List<MethodInfo>. No filtering is done.
+   */
+  private static List<MethodInfo> convertAllMethods(ExecutableMemberDoc[] methods) {
     if (methods == null) return null;
-    ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
-    int N = methods.length;
-    for (int i = 0; i < N; i++) {
-      MethodInfo m = Converter.obtainMethod(methods[i]);
-      if (m.checkLevel()) {
-        out.add(m);
+    List<MethodInfo> allMethods = new ArrayList<MethodInfo>();
+    for (ExecutableMemberDoc method : methods) {
+      MethodInfo methodInfo = Converter.obtainMethod(method);
+      allMethods.add(methodInfo);
+    }
+    return allMethods;
+  }
+
+  /**
+   * Convert MethodDoc[] or ConstructorDoc[] into MethodInfo[].
+   * Also filters according to the -private, -public option,
+   * because the filtering doesn't seem to be working in the ClassDoc.constructors(boolean) call.
+   */
+  private static MethodInfo[] convertMethods(ExecutableMemberDoc[] methods) {
+    if (methods == null) return null;
+    List<MethodInfo> filteredMethods = new ArrayList<MethodInfo>();
+    for (ExecutableMemberDoc method : methods) {
+      MethodInfo methodInfo = Converter.obtainMethod(method);
+      if (methodInfo.checkLevel()) {
+        filteredMethods.add(methodInfo);
       }
     }
-    return out.toArray(new MethodInfo[out.size()]);
+
+    return filteredMethods.toArray(new MethodInfo[filteredMethods.size()]);
   }
 
   private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods) {
     if (methods == null) return null;
-    ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
-    int N = methods.length;
-    for (int i = 0; i < N; i++) {
-      MethodInfo m = Converter.obtainMethod(methods[i]);
-      if (!m.checkLevel()) {
-        out.add(m);
+    ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>();
+    for (ConstructorDoc method : methods) {
+      MethodInfo methodInfo = Converter.obtainMethod(method);
+      if (!methodInfo.checkLevel()) {
+        ctors.add(methodInfo);
       }
     }
-    return out.toArray(new MethodInfo[out.size()]);
+
+    return ctors.toArray(new MethodInfo[ctors.size()]);
   }
 
-  private static MethodInfo obtainMethod(MethodDoc o) {
-    return (MethodInfo) mMethods.obtain(o);
-  }
-
-  private static MethodInfo obtainMethod(ConstructorDoc o) {
+  private static <E extends ExecutableMemberDoc> MethodInfo obtainMethod(E o) {
     return (MethodInfo) mMethods.obtain(o);
   }
 
@@ -559,11 +582,11 @@
       return keyString;
     }
   };
-  
+
   public static TypeInfo obtainTypeFromString(String type) {
     return (TypeInfo) mTypesFromString.obtain(type);
   }
-  
+
   private static final Cache mTypesFromString = new Cache() {
     @Override
     protected Object make(Object o) {
@@ -573,7 +596,7 @@
 
     @Override
     protected void made(Object o, Object r) {
-      
+
     }
 
     @Override
diff --git a/src/com/google/doclava/DocInfo.java b/src/com/google/doclava/DocInfo.java
index 714beb8..d8a1961 100644
--- a/src/com/google/doclava/DocInfo.java
+++ b/src/com/google/doclava/DocInfo.java
@@ -32,11 +32,30 @@
    * The relative path to a web page representing this item.
    */
   public abstract String htmlPage();
-  
+
+  /**
+   * @return true if the element has never been a part of public API
+   */
   public boolean isHidden() {
     return comment().isHidden();
   }
 
+  /**
+   * @return true if the element was once a part of public API, now removed.
+   */
+  public boolean isRemoved() {
+    return comment().isRemoved();
+  }
+
+  /**
+   * Hidden and removed elements should not be appear in api.txt files, nor
+   * should they appear in the java doc.
+   * @return true if the element is either hidden or removed.
+   */
+  public boolean isHiddenOrRemoved() {
+   return isHidden() || isRemoved();
+  }
+
   public boolean isDocOnly() {
     return comment().isDocOnly();
   }
@@ -84,7 +103,7 @@
   public String getSince() {
     return mSince;
   }
-  
+
   public void setDeprecatedSince(String since) {
     mDeprecatedSince = since;
   }
@@ -100,11 +119,11 @@
   public final void addFederatedReference(FederatedSite source) {
     mFederatedReferences.add(source);
   }
-  
+
   public final Set<FederatedSite> getFederatedReferences() {
     return mFederatedReferences;
   }
-  
+
   public final void setFederatedReferences(Data data, String base) {
     int pos = 0;
     for (FederatedSite source : getFederatedReferences()) {
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index a96b91c..79295fd 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -116,7 +116,6 @@
 
   public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp, boolean priv,
       boolean hidden) {
-    int level = 0;
     if (hidden && !checkLevel(SHOW_HIDDEN)) {
       return false;
     }
@@ -150,6 +149,7 @@
     // Create the dependency graph for the stubs  directory
     boolean offlineMode = false;
     String apiFile = null;
+    String removedApiFile = null;
     String debugStubsFile = "";
     HashSet<String> stubPackages = null;
     ArrayList<String> knownTagsFiles = new ArrayList<String>();
@@ -236,7 +236,10 @@
         sdkValuePath = a[1];
       } else if (a[0].equals("-api")) {
         apiFile = a[1];
-      } else if (a[0].equals("-nodocs")) {
+      } else if (a[0].equals("-removedApi")) {
+        removedApiFile = a[1];
+      }
+      else if (a[0].equals("-nodocs")) {
         generateDocs = false;
       } else if (a[0].equals("-nodefaultassets")) {
         includeDefaultAssets = false;
@@ -340,7 +343,7 @@
       }
 
       writeAssets();
-      
+
       // Navigation tree
       String refPrefix = new String();
       if(gmsRef){
@@ -385,8 +388,8 @@
     }
 
     // Stubs
-    if (stubsDir != null || apiFile != null || proguardFile != null) {
-      Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, stubPackages);
+    if (stubsDir != null || apiFile != null || proguardFile != null || removedApiFile != null) {
+      Stubs.writeStubsAndApi(stubsDir, apiFile, proguardFile, removedApiFile, stubPackages);
     }
 
     Errors.printErrors();
@@ -613,6 +616,9 @@
     if (option.equals("-api")) {
       return 2;
     }
+    if (option.equals("-removedApi")) {
+      return 2;
+    }
     if (option.equals("-nodocs")) {
       return 1;
     }
@@ -700,13 +706,13 @@
     for (String s : sorted.keySet()) {
       PackageInfo pkg = sorted.get(s);
 
-      if (pkg.isHidden()) {
+      if (pkg.isHiddenOrRemoved()) {
         continue;
       }
-      Boolean allHidden = true;
+      boolean allHiddenOrRemoved = true;
       int pass = 0;
       ClassInfo[] classesToCheck = null;
-      while (pass < 5) {
+      while (pass < 6) {
         switch (pass) {
           case 0:
             classesToCheck = pkg.ordinaryClasses();
@@ -723,22 +729,25 @@
           case 4:
             classesToCheck = pkg.interfaces();
             break;
+          case 5:
+            classesToCheck = pkg.annotations();
+            break;
           default:
             System.err.println("Error reading package: " + pkg.name());
             break;
         }
         for (ClassInfo cl : classesToCheck) {
-          if (!cl.isHidden()) {
-            allHidden = false;
+          if (!cl.isHiddenOrRemoved()) {
+            allHiddenOrRemoved = false;
             break;
           }
         }
-        if (!allHidden) {
+        if (!allHiddenOrRemoved) {
           break;
         }
         pass++;
       }
-      if (allHidden) {
+      if (allHiddenOrRemoved) {
         continue;
       }
       if(gmsRef){
@@ -842,7 +851,7 @@
 
     SortedMap<String, Object> sorted = new TreeMap<String, Object>();
     for (ClassInfo cl : classes) {
-      if (cl.isHidden()) {
+      if (cl.isHiddenOrRemoved()) {
         continue;
       }
       sorted.put(cl.qualifiedName(), cl);
@@ -911,7 +920,8 @@
         // If it's a .jd file we want to process
         if (len > 3 && ".jd".equals(templ.substring(len - 3))) {
           // remove the directories below the site root
-          String webPath = filePath.substring(filePath.indexOf("docs/html/") + 10, filePath.length());
+          String webPath = filePath.substring(filePath.indexOf("docs/html/") + 10,
+              filePath.length());
           // replace .jd with .html
           webPath = webPath.substring(0, webPath.length() - 3) + htmlExtension;
           // Parse the .jd file for properties data at top of page
@@ -1024,7 +1034,7 @@
     // If a class is public and not hidden, then it and everything it derives
     // from cannot be stripped. Otherwise we can strip it.
     for (ClassInfo cl : all) {
-      if (cl.isPublic() && !cl.isHidden()) {
+      if (cl.isPublic() && !cl.isHiddenOrRemoved()) {
         cantStripThis(cl, notStrippable);
       }
     }
@@ -1068,13 +1078,14 @@
     for (String s : sorted.keySet()) {
       PackageInfo pkg = sorted.get(s);
 
-      if (pkg.isHidden()) {
+      if (pkg.isHiddenOrRemoved()) {
         continue;
       }
-      Boolean allHidden = true;
+
+      boolean allHiddenOrRemoved = true;
       int pass = 0;
       ClassInfo[] classesToCheck = null;
-      while (pass < 5) {
+      while (pass < 6) {
         switch (pass) {
           case 0:
             classesToCheck = pkg.ordinaryClasses();
@@ -1091,22 +1102,25 @@
           case 4:
             classesToCheck = pkg.interfaces();
             break;
+          case 5:
+            classesToCheck = pkg.annotations();
+            break;
           default:
             System.err.println("Error reading package: " + pkg.name());
             break;
         }
         for (ClassInfo cl : classesToCheck) {
-          if (!cl.isHidden()) {
-            allHidden = false;
+          if (!cl.isHiddenOrRemoved()) {
+            allHiddenOrRemoved = false;
             break;
           }
         }
-        if (!allHidden) {
+        if (!allHiddenOrRemoved) {
           break;
         }
         pass++;
       }
-      if (allHidden) {
+      if (allHiddenOrRemoved) {
         continue;
       }
 
@@ -1153,6 +1167,7 @@
     data.setValue("package.descr", "...description...");
     pkg.setFederatedReferences(data, "package");
 
+    makeClassListHDF(data, "package.annotations", ClassInfo.sortByName(pkg.annotations()));
     makeClassListHDF(data, "package.interfaces", ClassInfo.sortByName(pkg.interfaces()));
     makeClassListHDF(data, "package.classes", ClassInfo.sortByName(pkg.ordinaryClasses()));
     makeClassListHDF(data, "package.enums", ClassInfo.sortByName(pkg.enums()));
@@ -1172,7 +1187,8 @@
     int i;
     Data data = makePackageHDF();
 
-    ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes()));
+    ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved(
+        Converter.convertClasses(root.classes()));
     if (classes.length == 0) {
       return;
     }
@@ -1226,7 +1242,7 @@
    * public static void writeKeywords() { ArrayList<KeywordEntry> keywords = new
    * ArrayList<KeywordEntry>();
    *
-   * ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes()));
+   * ClassInfo[] classes = PackageInfo.filterHiddenAndRemoved(Converter.convertClasses(root.classes()));
    *
    * for (ClassInfo cl: classes) { cl.makeKeywordEntries(keywords); }
    *
@@ -1245,7 +1261,7 @@
     ClassInfo[] classes = Converter.rootClasses();
     ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
     for (ClassInfo cl : classes) {
-      if (!cl.isHidden()) {
+      if (!cl.isHiddenOrRemoved()) {
         info.add(cl);
       }
     }
@@ -1260,7 +1276,7 @@
 
     for (ClassInfo cl : classes) {
       Data data = makePackageHDF();
-      if (!cl.isHidden()) {
+      if (!cl.isHiddenOrRemoved()) {
         writeClass(cl, data);
       }
     }
@@ -1277,7 +1293,7 @@
   public static void makeClassListHDF(Data data, String base, ClassInfo[] classes) {
     for (int i = 0; i < classes.length; i++) {
       ClassInfo cl = classes[i];
-      if (!cl.isHidden()) {
+      if (!cl.isHiddenOrRemoved()) {
         cl.makeShortDescrHDF(data, base + "." + i);
       }
     }
@@ -1313,20 +1329,21 @@
   }
 
   /**
-   * Returns true if the given element has an @hide or @pending annotation.
+   * Returns true if the given element has an @hide, @removed or @pending annotation.
    */
-  private static boolean hasHideAnnotation(Doc doc) {
+  private static boolean hasHideOrRemovedAnnotation(Doc doc) {
     String comment = doc.getRawCommentText();
-    return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1;
+    return comment.indexOf("@hide") != -1 || comment.indexOf("@pending") != -1 ||
+        comment.indexOf("@removed") != -1;
   }
 
   /**
    * Returns true if the given element is hidden.
    */
-  private static boolean isHidden(Doc doc) {
+  private static boolean isHiddenOrRemoved(Doc doc) {
     // Methods, fields, constructors.
     if (doc instanceof MemberDoc) {
-      return hasHideAnnotation(doc);
+      return hasHideOrRemovedAnnotation(doc);
     }
 
     // Classes, interfaces, enums, annotation types.
@@ -1334,7 +1351,7 @@
       ClassDoc classDoc = (ClassDoc) doc;
 
       // Check the containing package.
-      if (hasHideAnnotation(classDoc.containingPackage())) {
+      if (hasHideOrRemovedAnnotation(classDoc.containingPackage())) {
         return true;
       }
 
@@ -1342,7 +1359,7 @@
       // nested class.
       ClassDoc current = classDoc;
       do {
-        if (hasHideAnnotation(current)) {
+        if (hasHideOrRemovedAnnotation(current)) {
           return true;
         }
 
@@ -1354,9 +1371,9 @@
   }
 
   /**
-   * Filters out hidden elements.
+   * Filters out hidden and removed elements.
    */
-  private static Object filterHidden(Object o, Class<?> expected) {
+  private static Object filterHiddenAndRemoved(Object o, Class<?> expected) {
     if (o == null) {
       return null;
     }
@@ -1371,10 +1388,10 @@
       Object[] array = (Object[]) o;
       List<Object> list = new ArrayList<Object>(array.length);
       for (Object entry : array) {
-        if ((entry instanceof Doc) && isHidden((Doc) entry)) {
+        if ((entry instanceof Doc) && isHiddenOrRemoved((Doc) entry)) {
           continue;
         }
-        list.add(filterHidden(entry, componentType));
+        list.add(filterHiddenAndRemoved(entry, componentType));
       }
       return list.toArray((Object[]) Array.newInstance(componentType, list.size()));
     } else {
@@ -1412,7 +1429,7 @@
       }
 
       try {
-        return filterHidden(method.invoke(target, args), method.getReturnType());
+        return filterHiddenAndRemoved(method.invoke(target, args), method.getReturnType());
       } catch (InvocationTargetException e) {
         throw e.getTargetException();
       }
@@ -1492,7 +1509,7 @@
 
       // Now check the class for @Widget or if its in the android.widget package
       // (unless the class is hidden or abstract, or non public)
-      if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) {
+      if (clazz.isHiddenOrRemoved() == false && clazz.isPublic() && clazz.isAbstract() == false) {
         boolean annotated = false;
         ArrayList<AnnotationInstanceInfo> annotations = clazz.annotations();
         if (!annotations.isEmpty()) {
@@ -1760,5 +1777,5 @@
       return false;
     }
   }
-  
-}
\ No newline at end of file
+
+}
diff --git a/src/com/google/doclava/Hierarchy.java b/src/com/google/doclava/Hierarchy.java
index 0887b63..0fddf9a 100755
--- a/src/com/google/doclava/Hierarchy.java
+++ b/src/com/google/doclava/Hierarchy.java
@@ -88,7 +88,7 @@
   }
 
   private static boolean exists(ClassInfo cl) {
-    return cl != null && !cl.isHidden() && cl.isIncluded();
+    return cl != null && !cl.isHiddenOrRemoved() && cl.isIncluded();
   }
 
   private static void recurse(HashMap<String, TreeSet<String>> nodes, String name, Data hdf,
diff --git a/src/com/google/doclava/MemberInfo.java b/src/com/google/doclava/MemberInfo.java
index e5cc7a2..800edba 100644
--- a/src/com/google/doclava/MemberInfo.java
+++ b/src/com/google/doclava/MemberInfo.java
@@ -54,6 +54,23 @@
     return super.isHidden();
   }
 
+  @Override
+  public boolean isRemoved() {
+    if (mAnnotations != null) {
+      for (AnnotationInstanceInfo info : mAnnotations) {
+        if (Doclava.showAnnotations.contains(info.type().qualifiedName())) {
+          return false;
+        }
+      }
+    }
+    return super.isRemoved();
+  }
+
+  @Override
+  public boolean isHiddenOrRemoved() {
+    return isHidden() || isRemoved();
+  }
+
   public String anchor() {
     if (mSignature != null) {
       return mName + mSignature;
@@ -101,7 +118,7 @@
   public boolean isPrivate() {
     return mIsPrivate;
   }
-  
+
   public String scope() {
     if (isPublic()) {
       return "public";
@@ -134,7 +151,8 @@
   }
 
   public boolean checkLevel() {
-    return Doclava.checkLevel(mIsPublic, mIsProtected, mIsPackagePrivate, mIsPrivate, isHidden());
+    return Doclava.checkLevel(mIsPublic, mIsProtected, mIsPackagePrivate, mIsPrivate,
+        isHiddenOrRemoved());
   }
 
   public String kind() {
diff --git a/src/com/google/doclava/NavTree.java b/src/com/google/doclava/NavTree.java
index aa02d7c..cc4f43f 100644
--- a/src/com/google/doclava/NavTree.java
+++ b/src/com/google/doclava/NavTree.java
@@ -64,7 +64,7 @@
 
     SortedMap<String, Object> sorted = new TreeMap<String, Object>();
     for (ClassInfo cl : classes) {
-      if (cl.isHidden()) {
+      if (cl.isHiddenOrRemoved()) {
         continue;
       }
       sorted.put(cl.qualifiedName(), cl);
@@ -133,6 +133,7 @@
   private static Node makePackageNode(PackageInfo pkg) {
     List<Node> children = new ArrayList<Node>();
 
+    addClassNodes(children, "Annotations", pkg.annotations());
     addClassNodes(children, "Interfaces", pkg.interfaces());
     addClassNodes(children, "Classes", pkg.ordinaryClasses());
     addClassNodes(children, "Enums", pkg.enums());
diff --git a/src/com/google/doclava/PackageInfo.java b/src/com/google/doclava/PackageInfo.java
index 65a9639..e997b27 100644
--- a/src/com/google/doclava/PackageInfo.java
+++ b/src/com/google/doclava/PackageInfo.java
@@ -61,6 +61,7 @@
   }
 
   private void initializeMaps() {
+      mAnnotationsMap = new HashMap<String, ClassInfo>();
       mInterfacesMap = new HashMap<String, ClassInfo>();
       mOrdinaryClassesMap = new HashMap<String, ClassInfo>();
       mEnumsMap = new HashMap<String, ClassInfo>();
@@ -83,13 +84,82 @@
 
   @Override
   public boolean isHidden() {
-    return comment().isHidden();
+    if (mHidden == null) {
+      if (hasHideComment()) {
+        // We change the hidden value of the package if a class wants to be not hidden.
+        ClassInfo[][] types = new ClassInfo[][] { annotations(), interfaces(), ordinaryClasses(),
+            enums(), exceptions() };
+        for (ClassInfo[] type : types) {
+          if (type != null) {
+            for (ClassInfo c : type) {
+              if (c.hasShowAnnotation()) {
+                mHidden = false;
+                return false;
+              }
+            }
+          }
+        }
+        mHidden = true;
+      } else {
+        mHidden = false;
+      }
+    }
+    return mHidden;
+  }
+
+  @Override
+  public boolean isRemoved() {
+    if (mRemoved == null) {
+      if (hasRemovedComment()) {
+        // We change the removed value of the package if a class wants to be not hidden.
+        ClassInfo[][] types = new ClassInfo[][] { annotations(), interfaces(), ordinaryClasses(),
+            enums(), exceptions() };
+        for (ClassInfo[] type : types) {
+          if (type != null) {
+            for (ClassInfo c : type) {
+              if (c.hasShowAnnotation()) {
+                mRemoved = false;
+                return false;
+              }
+            }
+          }
+        }
+        mRemoved = true;
+      } else {
+        mRemoved = false;
+      }
+    }
+
+    return mRemoved;
+  }
+
+  @Override
+  public boolean isHiddenOrRemoved() {
+    return isHidden() || isRemoved();
+  }
+
+  /**
+   * Used by ClassInfo to determine packages default visability before annoations.
+   */
+  public boolean hasHideComment() {
+    if (mHiddenByComment == null) {
+      mHiddenByComment = comment().isHidden();
+    }
+    return mHiddenByComment;
+  }
+
+  public boolean hasRemovedComment() {
+    if (mRemovedByComment == null) {
+      mRemovedByComment = comment().isRemoved();
+    }
+
+    return mRemovedByComment;
   }
 
   public boolean checkLevel() {
     // TODO should return false if all classes are hidden but the package isn't.
     // We don't have this so I'm not doing it now.
-    return !isHidden();
+    return !isHiddenOrRemoved();
   }
 
   public String name() {
@@ -108,11 +178,15 @@
     return comment().briefTags();
   }
 
-  public static ClassInfo[] filterHidden(ClassInfo[] classes) {
+  /**
+   * @param classes the Array of ClassInfo to be filtered
+   * @return an Array of ClassInfo without any hidden or removed classes
+   */
+  public static ClassInfo[] filterHiddenAndRemoved(ClassInfo[] classes) {
     ArrayList<ClassInfo> out = new ArrayList<ClassInfo>();
 
     for (ClassInfo cl : classes) {
-      if (!cl.isHidden()) {
+      if (!cl.isHiddenOrRemoved()) {
         out.add(cl);
       }
     }
@@ -130,6 +204,7 @@
 
   public void makeClassLinkListHDF(Data data, String base) {
     makeLink(data, base);
+    ClassInfo.makeLinkListHDF(data, base + ".annotations", annotations());
     ClassInfo.makeLinkListHDF(data, base + ".interfaces", interfaces());
     ClassInfo.makeLinkListHDF(data, base + ".classes", ordinaryClasses());
     ClassInfo.makeLinkListHDF(data, base + ".enums", enums());
@@ -138,10 +213,20 @@
     data.setValue(base + ".since", getSince());
   }
 
+  public ClassInfo[] annotations() {
+    if (mAnnotations == null) {
+      mAnnotations =
+          ClassInfo.sortByName(filterHiddenAndRemoved(
+              Converter.convertClasses(mPackage.annotationTypes())));
+    }
+    return mAnnotations;
+  }
+
   public ClassInfo[] interfaces() {
     if (mInterfaces == null) {
       mInterfaces =
-          ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.interfaces())));
+          ClassInfo.sortByName(filterHiddenAndRemoved(
+              Converter.convertClasses(mPackage.interfaces())));
     }
     return mInterfaces;
   }
@@ -149,14 +234,16 @@
   public ClassInfo[] ordinaryClasses() {
     if (mOrdinaryClasses == null) {
       mOrdinaryClasses =
-          ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.ordinaryClasses())));
+          ClassInfo.sortByName(filterHiddenAndRemoved(
+              Converter.convertClasses(mPackage.ordinaryClasses())));
     }
     return mOrdinaryClasses;
   }
 
   public ClassInfo[] enums() {
     if (mEnums == null) {
-      mEnums = ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.enums())));
+      mEnums = ClassInfo.sortByName(filterHiddenAndRemoved(
+          Converter.convertClasses(mPackage.enums())));
     }
     return mEnums;
   }
@@ -164,14 +251,16 @@
   public ClassInfo[] exceptions() {
     if (mExceptions == null) {
       mExceptions =
-          ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.exceptions())));
+          ClassInfo.sortByName(filterHiddenAndRemoved(
+              Converter.convertClasses(mPackage.exceptions())));
     }
     return mExceptions;
   }
 
   public ClassInfo[] errors() {
     if (mErrors == null) {
-      mErrors = ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.errors())));
+      mErrors = ClassInfo.sortByName(filterHiddenAndRemoved(
+          Converter.convertClasses(mPackage.errors())));
     }
     return mErrors;
   }
@@ -190,15 +279,21 @@
     return mName.hashCode();
   }
 
+  private Boolean mHidden = null;
+  private Boolean mHiddenByComment = null;
+  private Boolean mRemoved = null;
+  private Boolean mRemovedByComment = null;
   private String mName;
   private PackageDoc mPackage;
   private ApiInfo mContainingApi;
+  private ClassInfo[] mAnnotations;
   private ClassInfo[] mInterfaces;
   private ClassInfo[] mOrdinaryClasses;
   private ClassInfo[] mEnums;
   private ClassInfo[] mExceptions;
   private ClassInfo[] mErrors;
 
+  private HashMap<String, ClassInfo> mAnnotationsMap;
   private HashMap<String, ClassInfo> mInterfacesMap;
   private HashMap<String, ClassInfo> mOrdinaryClassesMap;
   private HashMap<String, ClassInfo> mEnumsMap;
@@ -230,10 +325,24 @@
       if (cls != null) {
           return cls;
       }
+      cls = mAnnotationsMap.get(className);
+
+      if (cls != null) {
+          return cls;
+      }
 
       return mErrorsMap.get(className);
   }
 
+  public void addAnnotation(ClassInfo cls) {
+      cls.setPackage(this);
+      mAnnotationsMap.put(cls.name(), cls);
+  }
+
+  public ClassInfo getAnnotation(String annotationName) {
+      return mAnnotationsMap.get(annotationName);
+  }
+
   public void addInterface(ClassInfo cls) {
       cls.setPackage(this);
       mInterfacesMap.put(cls.name(), cls);
diff --git a/src/com/google/doclava/Scoped.java b/src/com/google/doclava/Scoped.java
index 03e42f9..931f299 100644
--- a/src/com/google/doclava/Scoped.java
+++ b/src/com/google/doclava/Scoped.java
@@ -24,6 +24,4 @@
   boolean isPackagePrivate();
 
   boolean isPrivate();
-
-  boolean isHidden();
 }
diff --git a/src/com/google/doclava/SinceTagger.java b/src/com/google/doclava/SinceTagger.java
index 858d98f..b8ad418 100644
--- a/src/com/google/doclava/SinceTagger.java
+++ b/src/com/google/doclava/SinceTagger.java
@@ -31,7 +31,7 @@
 
 /**
  * Applies version information to the Doclava class model from apicheck XML files. Sample usage:
- * 
+ *
  * <pre>
  *   ClassInfo[] classInfos = ...
  *
@@ -59,7 +59,7 @@
     for (Map.Entry<String, String> versionSpec : xmlToName.entrySet()) {
       String xmlFile = versionSpec.getKey();
       String versionName = versionSpec.getValue();
-      
+
       ApiInfo specApi;
       try {
         specApi = new ApiCheck().parseApi(xmlFile);
@@ -96,7 +96,7 @@
 
   /**
    * Applies the version information to {@code classDocs} where not already present.
-   * 
+   *
    * @param versionName the version name
    * @param specApi the spec for this version. If a symbol is in this spec, it was present in the
    *        named version
@@ -266,7 +266,8 @@
     List<T> result = Collections.emptyList();
     for (T t : all) {
       // if this member has version info or isn't documented, skip it
-      if (t.getSince() != null || t.isHidden() || !checkLevelRecursive(t.realContainingClass())) {
+      if (t.getSince() != null || t.isHiddenOrRemoved() ||
+          !checkLevelRecursive(t.realContainingClass())) {
         continue;
       }
 
diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java
index 9b9fc6e..560acef 100644
--- a/src/com/google/doclava/Stubs.java
+++ b/src/com/google/doclava/Stubs.java
@@ -32,12 +32,14 @@
 
 public class Stubs {
   public static void writeStubsAndApi(String stubsDir, String apiFile, String keepListFile,
-      HashSet<String> stubPackages) {
+      String removedApiFile, 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;
+
     if (apiFile != null) {
       try {
         File xml = new File(apiFile);
@@ -58,6 +60,17 @@
             "Cannot open file for write.");
       }
     }
+    if (removedApiFile != null) {
+      try {
+        File removedApi = new File(removedApiFile);
+        removedApi.getParentFile().mkdirs();
+        removedApiWriter = new PrintStream(
+            new BufferedOutputStream(new FileOutputStream(removedApi)));
+      } catch (FileNotFoundException e) {
+        Errors.error(Errors.IO_ERROR, new SourcePositionInfo(removedApiFile, 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) {
@@ -69,10 +82,10 @@
     // complain about anything that looks includeable but is not supposed to
     // be written, e.g. hidden things
     for (ClassInfo cl : notStrippable) {
-      if (!cl.isHidden()) {
+      if (!cl.isHiddenOrRemoved()) {
         for (MethodInfo m : cl.selfMethods()) {
-          if (m.isHidden()) {
-            Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to hidden method "
+          if (m.isHiddenOrRemoved()) {
+            Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable method "
                 + m.name());
           } else if (m.isDeprecated()) {
             // don't bother reporting deprecated methods
@@ -82,7 +95,7 @@
           }
 
           ClassInfo returnClass = m.returnType().asClassInfo();
-          if (returnClass != null && returnClass.isHidden()) {
+          if (returnClass != null && returnClass.isHiddenOrRemoved()) {
             Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Method " + cl.qualifiedName()
                 + "." + m.name() + " returns unavailable type " + returnClass.name());
           }
@@ -90,8 +103,8 @@
           for (ParameterInfo p :  m.parameters()) {
             TypeInfo t = p.type();
             if (!t.isPrimitive()) {
-              if (t.asClassInfo().isHidden()) {
-                Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Parameter of hidden type "
+              if (t.asClassInfo().isHiddenOrRemoved()) {
+                Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Parameter of unavailable type "
                     + t.fullName() + " in " + cl.qualifiedName() + "." + m.name() + "()");
               }
             }
@@ -100,13 +113,13 @@
 
         // annotations are handled like methods
         for (MethodInfo m : cl.annotationElements()) {
-          if (m.isHidden()) {
-            Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to hidden annotation "
+          if (m.isHiddenOrRemoved()) {
+            Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable annotation "
                 + m.name());
           }
 
           ClassInfo returnClass = m.returnType().asClassInfo();
-          if (returnClass != null && returnClass.isHidden()) {
+          if (returnClass != null && returnClass.isHiddenOrRemoved()) {
             Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Annotation '" + m.name()
                 + "' returns unavailable type " + returnClass.name());
           }
@@ -114,7 +127,7 @@
           for (ParameterInfo p :  m.parameters()) {
             TypeInfo t = p.type();
             if (!t.isPrimitive()) {
-              if (t.asClassInfo().isHidden()) {
+              if (t.asClassInfo().isHiddenOrRemoved()) {
                 Errors.error(Errors.UNAVAILABLE_SYMBOL, p.position(),
                     "Reference to unavailable annotation class " + t.fullName());
               }
@@ -128,6 +141,7 @@
       }
     }
 
+    // packages contains all the notStrippable classes mapped by their containing packages
     HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>();
     for (ClassInfo cl : notStrippable) {
       if (!cl.isDocOnly()) {
@@ -149,7 +163,6 @@
         }
       }
     }
-
     // write out the Api
     if (apiWriter != null) {
       writeApi(apiWriter, packages, notStrippable);
@@ -161,6 +174,23 @@
       writeKeepList(keepListWriter, packages, notStrippable);
       keepListWriter.close();
     }
+
+    HashMap<PackageInfo, List<ClassInfo>> allPackageClassMap =
+        new HashMap<PackageInfo, List<ClassInfo>>();
+    for (ClassInfo cl : Converter.allClasses()) {
+      if (allPackageClassMap.containsKey(cl.containingPackage())) {
+        allPackageClassMap.get(cl.containingPackage()).add(cl);
+      } else {
+        ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>();
+        classes.add(cl);
+        allPackageClassMap.put(cl.containingPackage(), classes);
+      }
+    }
+    // write out the removed Api
+    if (removedApiWriter != null) {
+      writeRemovedApi(removedApiWriter, allPackageClassMap, notStrippable);
+      removedApiWriter.close();
+    }
   }
 
   public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why) {
@@ -179,8 +209,8 @@
      * }
      */
     // cant strip any public fields or their generics
-    if (cl.allSelfFields() != null) {
-      for (FieldInfo fInfo : cl.allSelfFields()) {
+    if (cl.selfFields() != null) {
+      for (FieldInfo fInfo : cl.selfFields()) {
         if (fInfo.type() != null) {
           if (fInfo.type().asClassInfo() != null) {
             cantStripThis(fInfo.type().asClassInfo(), notStrippable, "2:" + cl.qualifiedName());
@@ -217,7 +247,7 @@
     // blow open super class and interfaces
     ClassInfo supr = cl.realSuperclass();
     if (supr != null) {
-      if (supr.isHidden()) {
+      if (supr.isHiddenOrRemoved()) {
         // cl is a public class declared as extending a hidden superclass.
         // this is not a desired practice but it's happened, so we deal
         // with it by finding the first super class which passes checklevel for purposes of
@@ -256,7 +286,7 @@
                 for (TypeInfo tInfoType : pInfo.type().typeArguments()) {
                   if (tInfoType.asClassInfo() != null) {
                     ClassInfo tcl = tInfoType.asClassInfo();
-                    if (tcl.isHidden()) {
+                    if (tcl.isHiddenOrRemoved()) {
                       Errors
                           .error(Errors.UNAVAILABLE_SYMBOL, mInfo.position(),
                               "Parameter of hidden type " + tInfoType.fullName() + " in "
@@ -427,7 +457,7 @@
 
     boolean fieldNeedsInitialization = false;
     boolean staticFieldNeedsInitialization = false;
-    for (FieldInfo field : cl.allSelfFields()) {
+    for (FieldInfo field : cl.selfFields()) {
       if (!field.isDocOnly()) {
         if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
           fieldNeedsInitialization = true;
@@ -445,8 +475,8 @@
     // and the super class doesn't have a default constructor, write in a private constructor
     // that works. TODO -- we generate this as protected, but we really should generate
     // it as private unless it also exists in the real code.
-    if ((cl.constructors().isEmpty() && (!cl.getNonWrittenConstructors().isEmpty() || fieldNeedsInitialization))
-        && !cl.isAnnotation() && !cl.isInterface() && !cl.isEnum()) {
+    if ((cl.constructors().isEmpty() && (!cl.getNonWrittenConstructors().isEmpty() ||
+        fieldNeedsInitialization)) && !cl.isAnnotation() && !cl.isInterface() && !cl.isEnum()) {
       // Errors.error(Errors.HIDDEN_CONSTRUCTOR,
       // cl.position(), "No constructors " +
       // "found and superclass has no parameterless constructor.  A constructor " +
@@ -458,8 +488,9 @@
 
     for (MethodInfo method : cl.allSelfMethods()) {
       if (cl.isEnum()) {
-        if (("values".equals(method.name()) && "()".equals(method.signature()))
-            || ("valueOf".equals(method.name()) && "(java.lang.String)".equals(method.signature()))) {
+        if (("values".equals(method.name()) && "()".equals(method.signature())) ||
+            ("valueOf".equals(method.name()) &&
+            "(java.lang.String)".equals(method.signature()))) {
           // skip these two methods on enums, because they're synthetic,
           // although for some reason javadoc doesn't mark them as synthetic,
           // maybe because they still want them documented
@@ -470,15 +501,18 @@
         writeMethod(stream, method, false);
       }
     }
-    // Write all methods that are hidden, but override abstract methods or interface methods.
+    // Write all methods that are hidden or removed, but override abstract methods or interface methods.
     // These can't be hidden.
-    for (MethodInfo method : cl.getHiddenMethods()) {
+    List<MethodInfo> hiddenAndRemovedMethods = cl.getHiddenMethods();
+    hiddenAndRemovedMethods.addAll(cl.getRemovedMethods());
+    for (MethodInfo method : hiddenAndRemovedMethods) {
       MethodInfo overriddenMethod =
           method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable);
       ClassInfo classContainingMethod =
           method.findRealOverriddenClass(method.name(), method.signature());
-      if (overriddenMethod != null && !overriddenMethod.isHidden() && !overriddenMethod.isDocOnly()
-          && (overriddenMethod.isAbstract() || overriddenMethod.containingClass().isInterface())) {
+      if (overriddenMethod != null && !overriddenMethod.isHiddenOrRemoved() &&
+          !overriddenMethod.isDocOnly() &&
+          (overriddenMethod.isAbstract() || overriddenMethod.containingClass().isInterface())) {
         method.setReason("1:" + classContainingMethod.qualifiedName());
         cl.addMethod(method);
         writeMethod(stream, method, false);
@@ -491,7 +525,7 @@
       }
     }
 
-    for (FieldInfo field : cl.allSelfFields()) {
+    for (FieldInfo field : cl.selfFields()) {
       if (!field.isDocOnly()) {
         writeField(stream, field);
       }
@@ -499,7 +533,7 @@
 
     if (staticFieldNeedsInitialization) {
       stream.print("static { ");
-      for (FieldInfo field : cl.allSelfFields()) {
+      for (FieldInfo field : cl.selfFields()) {
         if (!field.isDocOnly() && field.isStatic() && field.isFinal() && !fieldIsInitialized(field)
             && field.constantValue() == null) {
           stream.print(field.name() + " = " + field.type().defaultValue() + "; ");
@@ -628,9 +662,9 @@
         // Look only for overrides of an ancestor class implementation,
         // not of e.g. an abstract or interface method declaration
         if (!om.isAbstract()) {
-          // If the parent is hidden, we can't rely on it to provide
+          // If the parent is hidden or removed, we can't rely on it to provide
           // the API
-          if (!om.isHidden()) {
+          if (!om.isHiddenOrRemoved()) {
             // If the only "override" turns out to be in our own class
             // (which sometimes happens in concrete subclasses of
             // abstract base classes), it's not really an override
@@ -751,7 +785,7 @@
       if (ann.type() != null && ann.type().qualifiedName().equals("java.lang.Override")) {
         continue;
       }
-      if (!ann.type().isHidden()) {
+      if (!ann.type().isHiddenOrRemoved()) {
         stream.println(ann.toString());
         if (isDeprecated && ann.type() != null
             && ann.type().qualifiedName().equals("java.lang.Deprecated")) {
@@ -872,7 +906,7 @@
       }
     }
 
-    ArrayList<FieldInfo> fields = cl.allSelfFields();
+    ArrayList<FieldInfo> fields = cl.selfFields();
     Collections.sort(fields, FieldInfo.comparator);
     for (FieldInfo fi : fields) {
       writeFieldXML(xmlWriter, fi);
@@ -993,6 +1027,105 @@
     return returnString;
   }
 
+  static void writeRemovedApi(PrintStream apiWriter, HashMap<PackageInfo,
+      List<ClassInfo>> allPackageClassMap, Set<ClassInfo> notStrippable) {
+    final PackageInfo[] packages = allPackageClassMap.keySet().toArray(new PackageInfo[0]);
+    Arrays.sort(packages, PackageInfo.comparator);
+    for (PackageInfo pkg : packages) {
+      // beware that pkg.allClasses() has no class in it at the moment
+      final List<ClassInfo> classes = allPackageClassMap.get(pkg);
+      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);
+        }
+      }
+
+      // the package contains some classes with some removed members
+      if (hasWrittenPackageHead) {
+        apiWriter.print("}\n\n");
+      }
+    }
+  }
+
+  /**
+   * Write the removed members of the class to removed.txt
+   */
+  private static void writeClassRemovedSelfMembers(PrintStream apiWriter, ClassInfo cl,
+      Set<ClassInfo> notStrippable) {
+    apiWriter.print("  ");
+    apiWriter.print(cl.scope());
+    if (cl.isStatic()) {
+      apiWriter.print(" static");
+    }
+    if (cl.isFinal()) {
+      apiWriter.print(" final");
+    }
+    if (cl.isAbstract()) {
+      apiWriter.print(" abstract");
+    }
+    if (cl.isDeprecated()) {
+      apiWriter.print(" deprecated");
+    }
+    apiWriter.print(" ");
+    apiWriter.print(cl.isInterface() ? "interface" : "class");
+    apiWriter.print(" ");
+    apiWriter.print(cl.name());
+
+    if (!cl.isInterface()
+        && !"java.lang.Object".equals(cl.qualifiedName())
+        && cl.realSuperclass() != null
+        && !"java.lang.Object".equals(cl.realSuperclass().qualifiedName())) {
+      apiWriter.print(" extends ");
+      apiWriter.print(cl.realSuperclass().qualifiedName());
+    }
+
+    ArrayList<ClassInfo> interfaces = cl.realInterfaces();
+    Collections.sort(interfaces, ClassInfo.comparator);
+    boolean first = true;
+    for (ClassInfo iface : interfaces) {
+      if (notStrippable.contains(iface)) {
+        if (first) {
+          apiWriter.print(" implements");
+          first = false;
+        }
+        apiWriter.print(" ");
+        apiWriter.print(iface.qualifiedName());
+      }
+    }
+
+    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");
+  }
+
   public static void writeApi(PrintStream apiWriter, Collection<PackageInfo> pkgs) {
     final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]);
     Arrays.sort(packages, PackageInfo.comparator);
@@ -1107,7 +1240,7 @@
       writeFieldApi(apiWriter, fi, "enum_constant");
     }
 
-    ArrayList<FieldInfo> fields = cl.allSelfFields();
+    ArrayList<FieldInfo> fields = cl.selfFields();
     Collections.sort(fields, FieldInfo.comparator);
     for (FieldInfo fi : fields) {
       writeFieldApi(apiWriter, fi, "field");
@@ -1163,7 +1296,8 @@
     apiWriter.print(";\n");
   }
 
-  static void writeParametersApi(PrintStream apiWriter, MethodInfo method, ArrayList<ParameterInfo> params) {
+  static void writeParametersApi(PrintStream apiWriter, MethodInfo method,
+      ArrayList<ParameterInfo> params) {
     apiWriter.print("(");
 
     for (ParameterInfo pi : params) {
@@ -1297,9 +1431,8 @@
     ArrayList<MethodInfo> methods = cl.allSelfMethods();
     Collections.sort(methods, MethodInfo.comparator);
     for (MethodInfo mi : methods) {
-      if (!methodIsOverride(notStrippable, mi)) {
-        writeMethodKeepList(keepListWriter, mi);
-      }
+      // allSelfMethods is the non-hidden and visible methods. See Doclava.checkLevel.
+      writeMethodKeepList(keepListWriter, mi);
     }
 
     keepListWriter.print("\n");
@@ -1312,7 +1445,7 @@
 
     keepListWriter.print("\n");
 
-    ArrayList<FieldInfo> fields = cl.allSelfFields();
+    ArrayList<FieldInfo> fields = cl.selfFields();
     Collections.sort(fields, FieldInfo.comparator);
     for (FieldInfo fi : fields) {
       writeFieldKeepList(keepListWriter, fi);
diff --git a/src/com/google/doclava/TodoFile.java b/src/com/google/doclava/TodoFile.java
index 5cf4f1e..36df2c7 100644
--- a/src/com/google/doclava/TodoFile.java
+++ b/src/com/google/doclava/TodoFile.java
@@ -74,7 +74,7 @@
     int classIndex = 0;
 
     for (ClassInfo cl : classes) {
-      if (cl.isHidden()) {
+      if (cl.isHiddenOrRemoved()){
         continue;
       }
 
diff --git a/src/com/google/doclava/TypeInfo.java b/src/com/google/doclava/TypeInfo.java
index 048d0ea..36d9634 100644
--- a/src/com/google/doclava/TypeInfo.java
+++ b/src/com/google/doclava/TypeInfo.java
@@ -150,38 +150,61 @@
     return mFullName;
   }
 
-  public String fullNameNoDimension(HashSet<String> typeVars) {
-    String fullName = null;
+  public String fullNameNoBounds(HashSet<String> typeVars) {
+    return fullNameNoDimensionNoBounds(typeVars) + mDimension;
+  }
+
+  // don't recurse forever with the parameters. This handles
+  // Enum<K extends Enum<K>>
+  private boolean checkRecurringTypeVar(HashSet<String> typeVars) {
     if (mIsTypeVariable) {
       if (typeVars.contains(mQualifiedTypeName)) {
-        // don't recurse forever with the parameters. This handles
-        // Enum<K extends Enum<K>>
-        return mQualifiedTypeName;
+        return true;
       }
       typeVars.add(mQualifiedTypeName);
     }
+    return false;
+  }
+
+  private String fullNameNoDimensionNoBounds(HashSet<String> typeVars) {
+    String fullName = null;
+    if (checkRecurringTypeVar(typeVars)) {
+      return mQualifiedTypeName;
+    }
     /*
      * if (fullName != null) { return fullName; }
      */
     fullName = mQualifiedTypeName;
     if (mTypeArguments != null && !mTypeArguments.isEmpty()) {
       fullName += typeArgumentsName(mTypeArguments, typeVars);
-    } else if (mSuperBounds != null && !mSuperBounds.isEmpty()) {
+    }
+    return fullName;
+  }
+
+  public String fullNameNoDimension(HashSet<String> typeVars) {
+    String fullName = null;
+    if (checkRecurringTypeVar(typeVars)) {
+      return mQualifiedTypeName;
+    }
+    fullName = fullNameNoDimensionNoBounds(typeVars);
+    if (mTypeArguments == null || mTypeArguments.isEmpty()) {
+       if (mSuperBounds != null && !mSuperBounds.isEmpty()) {
         for (TypeInfo superBound : mSuperBounds) {
             if (superBound == mSuperBounds.get(0)) {
-                fullName += " super " + superBound.fullName(typeVars);
+                fullName += " super " + superBound.fullNameNoBounds(typeVars);
             } else {
-                fullName += " & " + superBound.fullName(typeVars);
+                fullName += " & " + superBound.fullNameNoBounds(typeVars);
             }
         }
-    } else if (mExtendsBounds != null && !mExtendsBounds.isEmpty()) {
+      } else if (mExtendsBounds != null && !mExtendsBounds.isEmpty()) {
         for (TypeInfo extendsBound : mExtendsBounds) {
             if (extendsBound == mExtendsBounds.get(0)) {
-                fullName += " extends " + extendsBound.fullName(typeVars);
+                fullName += " extends " + extendsBound.fullNameNoBounds(typeVars);
             } else {
-                fullName += " & " + extendsBound.fullName(typeVars);
+                fullName += " & " + extendsBound.fullNameNoBounds(typeVars);
             }
         }
+      }
     }
     return fullName;
   }
diff --git a/src/com/google/doclava/apicheck/ApiCheck.java b/src/com/google/doclava/apicheck/ApiCheck.java
index fb5b011..28d7ce0 100644
--- a/src/com/google/doclava/apicheck/ApiCheck.java
+++ b/src/com/google/doclava/apicheck/ApiCheck.java
@@ -103,10 +103,16 @@
 
     ApiInfo oldApi;
     ApiInfo newApi;
-    
+    ApiInfo oldRemovedApi;
+    ApiInfo newRemovedApi;
+
+    // commandline options look like:
+    // [other optoins] old_api.txt new_api.txt old_removed_api.txt new_removed_api.txt
     try {
       oldApi = parseApi(args.get(0));
       newApi = parseApi(args.get(1));
+      oldRemovedApi = parseApi(args.get(2));
+      newRemovedApi = parseApi(args.get(3));
     } catch (ApiParseException e) {
       e.printStackTrace();
       System.err.println("Error parsing API");
@@ -118,6 +124,10 @@
       oldApi.isConsistent(newApi);
     }
 
+    if (!Errors.hadError) {
+      oldRemovedApi.isConsistent(newRemovedApi);
+    }
+
     return new Report(Errors.hadError ? 1 : 0, Errors.getErrors());
   }