apicheck: Allow safe addition and removal of final qualifier.

Split the CHANGED_FINAL error into ADDED_FINAL,
ADDED_FINAL_UNINSTANTIABLE and REMOVED_FINAL so that the
API check scripts can more precisely target policy around
changes to the final qualifier.

ADDED_FINAL_UNINSTANTIABLE covers the case of a class
that has become final but that was previously not
instantiable.  This change is safe since applications could
not have declared subclasses of their own.

Change-Id: I36a143bdbcffd04379788a55ff996b47398ba4fe
diff --git a/src/com/google/doclava/MethodInfo.java b/src/com/google/doclava/MethodInfo.java
index db5e0cf..b6a5cc3 100644
--- a/src/com/google/doclava/MethodInfo.java
+++ b/src/com/google/doclava/MethodInfo.java
@@ -624,6 +624,10 @@
     return mIsVarargs;
   }
 
+  public boolean isFinalOrBelongsToFinalClass() {
+      return mIsFinal || (containingClass() != null && containingClass().isFinal());
+  }
+
   @Override
   public String toString() {
     return this.name();
@@ -729,17 +733,19 @@
           + " has changed 'native' qualifier");
     }
 
-    if (mIsFinal != mInfo.mIsFinal) {
-      // Compiler-generated methods vary in their 'final' qual between versions of
+    if (!mIsStatic) {
+      // Compiler-generated methods vary in their 'final' qualifier between versions of
       // the compiler, so this check needs to be quite narrow. A change in 'final'
       // status of a method is only relevant if (a) the method is not declared 'static'
-      // and (b) the method's class is not itself 'final'.
-      if (!mIsStatic) {
-        if ((containingClass() == null) || (!containingClass().isFinal())) {
-          consistent = false;
-          Errors.error(Errors.CHANGED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
-              + " has changed 'final' qualifier");
-        }
+      // and (b) the method is not already inferred to be 'final' by virtue of its class.
+      if (!isFinalOrBelongsToFinalClass() && mInfo.isFinalOrBelongsToFinalClass()) {
+        consistent = false;
+        Errors.error(Errors.ADDED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
+            + " has added 'final' qualifier");
+      } else if (isFinalOrBelongsToFinalClass() && !mInfo.isFinalOrBelongsToFinalClass()) {
+        consistent = false;
+        Errors.error(Errors.REMOVED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
+            + " has removed 'final' qualifier");
       }
     }