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 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 InheritedTags inherited() {
   MethodInfo m = findOverriddenMethod(name(), signature());
   if (m != null) {
     return m.inlineTags();
   } else {
     return null;
   }
 }
  // first looks for a superclass, and then does a breadth first search to
  // find the least far away match
  public MethodInfo findOverriddenMethod(String name, String signature) {
    if (mReturnType == null) {
      // ctor
      return null;
    }
    if (mOverriddenMethod != null) {
      return mOverriddenMethod;
    }

    ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
    addInterfaces(containingClass().interfaces(), 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) {
          return me;
        }
      }
    }
    return null;
  }
 /**
  * Clone this MethodInfo as if it belonged to the specified ClassInfo and apply the
  * typeArgumentMapping to the parameters and return types.
  */
 public MethodInfo cloneForClass(
     ClassInfo newContainingClass, Map<String, TypeInfo> typeArgumentMapping) {
   TypeInfo returnType = mReturnType.getTypeWithArguments(typeArgumentMapping);
   ArrayList<ParameterInfo> parameters = new ArrayList<ParameterInfo>();
   for (ParameterInfo pi : mParameters) {
     parameters.add(pi.cloneWithTypeArguments(typeArgumentMapping));
   }
   MethodInfo result =
       new MethodInfo(
           getRawCommentText(),
           mTypeParameters,
           name(),
           signature(),
           newContainingClass,
           realContainingClass(),
           isPublic(),
           isProtected(),
           isPackagePrivate(),
           isPrivate(),
           isFinal(),
           isStatic(),
           isSynthetic(),
           mIsAbstract,
           mIsSynchronized,
           mIsNative,
           mIsAnnotationElement,
           kind(),
           mFlatSignature,
           mOverriddenMethod,
           returnType,
           mParameters,
           mThrownExceptions,
           position(),
           annotations());
   result.init(mDefaultAnnotationElementValue);
   return result;
 }
  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;
  }
 public boolean matches(MethodInfo other) {
   return prettySignature().equals(other.prettySignature());
 }
  public ParamTagInfo[] paramTags() {
    if (mParamTags == null) {
      final int N = mParameters.size();

      String[] names = new String[N];
      String[] comments = new String[N];
      SourcePositionInfo[] positions = new SourcePositionInfo[N];

      // get the right names so we can handle our names being different from
      // our parent's names.
      int i = 0;
      for (ParameterInfo param : mParameters) {
        names[i] = param.name();
        comments[i] = "";
        positions[i] = param.position();
        i++;
      }

      // gather our comments, and complain about misnamed @param tags
      for (ParamTagInfo tag : comment().paramTags()) {
        int index = indexOfParam(tag.parameterName(), names);
        if (index >= 0) {
          comments[index] = tag.parameterComment();
          positions[index] = tag.position();
        } else {
          Errors.error(
              Errors.UNKNOWN_PARAM_TAG_NAME,
              tag.position(),
              "@param tag with name that doesn't match the parameter list: '"
                  + tag.parameterName()
                  + "'");
        }
      }

      // get our parent's tags to fill in the blanks
      MethodInfo overridden = this.findOverriddenMethod(name(), signature());
      if (overridden != null) {
        ParamTagInfo[] maternal = overridden.paramTags();
        for (i = 0; i < N; i++) {
          if (comments[i].equals("")) {
            comments[i] = maternal[i].parameterComment();
            positions[i] = maternal[i].position();
          }
        }
      }

      // construct the results, and cache them for next time
      mParamTags = new ParamTagInfo[N];
      for (i = 0; i < N; i++) {
        mParamTags[i] =
            new ParamTagInfo(
                "@param", "@param", names[i] + " " + comments[i], parent(), positions[i]);

        // while we're here, if we find any parameters that are still undocumented at this
        // point, complain. (this warning is off by default, because it's really, really
        // common; but, it's good to be able to enforce it)
        if (comments[i].equals("")) {
          Errors.error(
              Errors.UNDOCUMENTED_PARAMETER,
              positions[i],
              "Undocumented parameter '" + names[i] + "' on method '" + name() + "'");
        }
      }
    }
    return mParamTags;
  }
 public int compare(MethodInfo a, MethodInfo b) {
   return a.name().compareTo(b.name());
 }