public MethodInfo findRealOverriddenMethod(String name, String signature, HashSet notStrippable) {
    if (mReturnType == null) {
      // ctor
      return null;
    }
    if (mOverriddenMethod != null) {
      return mOverriddenMethod;
    }

    ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
    if (containingClass().realSuperclass() != null
        && containingClass().realSuperclass().isAbstract()) {
      queue.add(containingClass());
    }
    addInterfaces(containingClass().realInterfaces(), queue);
    for (ClassInfo iface : queue) {
      for (MethodInfo me : iface.methods()) {
        if (me.name().equals(name)
            && me.signature().equals(signature)
            && me.inlineTags().tags() != null
            && me.inlineTags().tags().length > 0
            && notStrippable.contains(me.containingClass())) {
          return me;
        }
      }
    }
    return null;
  }
  public MethodInfo findSuperclassImplementation(HashSet notStrippable) {
    if (mReturnType == null) {
      // ctor
      return null;
    }
    if (mOverriddenMethod != null) {
      // Even if we're told outright that this was the overridden method, we want to
      // be conservative and ignore mismatches of parameter types -- they arise from
      // extending generic specializations, and we want to consider the derived-class
      // method to be a non-override.
      if (this.signature().equals(mOverriddenMethod.signature())) {
        return mOverriddenMethod;
      }
    }

    ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
    if (containingClass().realSuperclass() != null
        && containingClass().realSuperclass().isAbstract()) {
      queue.add(containingClass());
    }
    addInterfaces(containingClass().realInterfaces(), queue);
    for (ClassInfo iface : queue) {
      for (MethodInfo me : iface.methods()) {
        if (me.name().equals(this.name())
            && me.signature().equals(this.signature())
            && notStrippable.contains(me.containingClass())) {
          return me;
        }
      }
    }
    return null;
  }
  public boolean isConsistent(MethodInfo mInfo) {
    boolean consistent = true;
    if (this.mReturnType != mInfo.mReturnType && !this.mReturnType.equals(mInfo.mReturnType)) {
      if (!mReturnType.isPrimitive() && !mInfo.mReturnType.isPrimitive()) {
        // Check to see if our class extends the old class.
        ApiInfo infoApi = mInfo.containingClass().containingPackage().containingApi();
        ClassInfo infoReturnClass = infoApi.findClass(mInfo.mReturnType.qualifiedTypeName());
        // Find the classes.
        consistent =
            infoReturnClass != null
                && infoReturnClass.isAssignableTo(mReturnType.qualifiedTypeName());
      } else {
        consistent = false;
      }

      if (!consistent) {
        Errors.error(
            Errors.CHANGED_TYPE,
            mInfo.position(),
            "Method "
                + mInfo.qualifiedName()
                + " has changed return type from "
                + mReturnType
                + " to "
                + mInfo.mReturnType);
      }
    }

    if (mIsAbstract != mInfo.mIsAbstract) {
      consistent = false;
      Errors.error(
          Errors.CHANGED_ABSTRACT,
          mInfo.position(),
          "Method " + mInfo.qualifiedName() + " has changed 'abstract' qualifier");
    }

    if (mIsNative != mInfo.mIsNative) {
      consistent = false;
      Errors.error(
          Errors.CHANGED_NATIVE,
          mInfo.position(),
          "Method " + mInfo.qualifiedName() + " has changed 'native' qualifier");
    }

    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 is not already inferred to be 'final' by virtue of its class.
      if (!isEffectivelyFinal() && mInfo.isEffectivelyFinal()) {
        consistent = false;
        Errors.error(
            Errors.ADDED_FINAL,
            mInfo.position(),
            "Method " + mInfo.qualifiedName() + " has added 'final' qualifier");
      } else if (isEffectivelyFinal() && !mInfo.isEffectivelyFinal()) {
        consistent = false;
        Errors.error(
            Errors.REMOVED_FINAL,
            mInfo.position(),
            "Method " + mInfo.qualifiedName() + " has removed 'final' qualifier");
      }
    }

    if (mIsStatic != mInfo.mIsStatic) {
      consistent = false;
      Errors.error(
          Errors.CHANGED_STATIC,
          mInfo.position(),
          "Method " + mInfo.qualifiedName() + " has changed 'static' qualifier");
    }

    if (!scope().equals(mInfo.scope())) {
      consistent = false;
      Errors.error(
          Errors.CHANGED_SCOPE,
          mInfo.position(),
          "Method "
              + mInfo.qualifiedName()
              + " changed scope from "
              + scope()
              + " to "
              + mInfo.scope());
    }

    if (!isDeprecated() == mInfo.isDeprecated()) {
      Errors.error(
          Errors.CHANGED_DEPRECATED,
          mInfo.position(),
          "Method "
              + mInfo.qualifiedName()
              + " has changed deprecation state "
              + isDeprecated()
              + " --> "
              + mInfo.isDeprecated());
      consistent = false;
    }

    // see JLS 3 13.4.20 "Adding or deleting a synchronized modifier of a method does not break "
    // "compatibility with existing binaries."
    /*
    if (mIsSynchronized != mInfo.mIsSynchronized) {
      Errors.error(Errors.CHANGED_SYNCHRONIZED, mInfo.position(), "Method " + mInfo.qualifiedName()
          + " has changed 'synchronized' qualifier from " + mIsSynchronized + " to "
          + mInfo.mIsSynchronized);
      consistent = false;
    }
    */

    for (ClassInfo exception : thrownExceptions()) {
      if (!mInfo.throwsException(exception)) {
        // exclude 'throws' changes to finalize() overrides with no arguments
        if (!name().equals("finalize") || (!mParameters.isEmpty())) {
          Errors.error(
              Errors.CHANGED_THROWS,
              mInfo.position(),
              "Method "
                  + mInfo.qualifiedName()
                  + " no longer throws exception "
                  + exception.qualifiedName());
          consistent = false;
        }
      }
    }

    for (ClassInfo exec : mInfo.thrownExceptions()) {
      // exclude 'throws' changes to finalize() overrides with no arguments
      if (!throwsException(exec)) {
        if (!name().equals("finalize") || (!mParameters.isEmpty())) {
          Errors.error(
              Errors.CHANGED_THROWS,
              mInfo.position(),
              "Method "
                  + mInfo.qualifiedName()
                  + " added thrown exception "
                  + exec.qualifiedName());
          consistent = false;
        }
      }
    }

    return consistent;
  }