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()); }