in versioning/versioning-checker/src/main/java/org/apache/aries/versioning/utils/ClassDeclaration.java [443:538]
private void areMethodsBinaryCompatible(
Map<String, Set<MethodDeclaration>> oldMethods, Map<String, Set<MethodDeclaration>> newMethods, List<String> reasons) {
boolean compatible = true;
Map<String, Collection<MethodDeclaration>> extraMethods = new HashMap<String, Collection<MethodDeclaration>>();
for (Map.Entry<String, Set<MethodDeclaration>> me : newMethods.entrySet()) {
Collection<MethodDeclaration> mds = new ArrayList<MethodDeclaration>(me.getValue());
extraMethods.put(me.getKey(), mds);
}
for (Map.Entry<String, Set<MethodDeclaration>> methods : oldMethods.entrySet()) {
// all overloading methods, check against the current class
String methodName = methods.getKey();
Collection<MethodDeclaration> oldMDSigs = methods.getValue();
// If the method cannot be found in the current class, it means that it has been deleted.
Collection<MethodDeclaration> newMDSigs = newMethods.get(methodName);
// for each overloading methods
outer:
for (MethodDeclaration md : oldMDSigs) {
String mdName = md.getName();
String prefix = "The " + SemanticVersioningUtils.getReadableMethodSignature(mdName, md.getDesc());
if (md.isProtected() || md.isPublic()) {
boolean found = false;
if (newMDSigs != null) {
// try to find it in the current class
for (MethodDeclaration new_md : newMDSigs) {
// find the method with the same return type, parameter list
if ((md.equals(new_md))) {
found = true;
// If the old method is final but the new one is not or vice versa
// If the old method is static but the new one is non static
// If the old method is not abstract but the new is
if (!Modifier.isFinal(md.getAccess()) && !Modifier.isStatic(md.getAccess()) && Modifier.isFinal(new_md.getAccess())) {
compatible = false;
reasons.add(prefix + " was not final but has been changed to be final.");
}
if (Modifier.isStatic(md.getAccess()) != Modifier.isStatic(new_md.getAccess())) {
compatible = false;
reasons.add(prefix + " has changed from static to non-static or vice versa.");
}
if ((Modifier.isAbstract(new_md.getAccess())) && (!Modifier.isAbstract(md.getAccess()))) {
compatible = false;
reasons.add(prefix + " has changed from non abstract to abstract.");
}
if (SemanticVersioningUtils.isLessAccessible(md, new_md)) {
compatible = false;
reasons.add(prefix + " is less accessible.");
}
if (compatible) {
// remove from the extra map
Collection<MethodDeclaration> mds = extraMethods.get(methodName);
mds.remove(new_md);
extraMethods.put(methodName, mds);
continue outer;
}
}
}
}
//
// if we are here, it means that we have not found the method with the same description and signature
// which means that the method has been deleted. Let's make sure it is not moved to its upper chain.
if (!found) {
if (!isMethodInSuperClass(md)) {
compatible = false;
reasons.add(prefix + " has been deleted or its return type or parameter list has changed.");
} else {
if (newMDSigs != null) {
for (MethodDeclaration new_md : newMDSigs) {
// find the method with the same return type, parameter list
if ((md.equals(new_md))) {
Collection<MethodDeclaration> mds = extraMethods.get(methodName);
mds.remove(new_md);
extraMethods.put(methodName, mds);
}
}
}
}
}
}
}
}
// Check the newly added method has not caused binary incompatibility
for (Map.Entry<String, Collection<MethodDeclaration>> extraMethodSet : extraMethods.entrySet()) {
for (MethodDeclaration md : extraMethodSet.getValue()) {
String head = "The " + SemanticVersioningUtils.getReadableMethodSignature(md.getName(), md.getDesc());
isNewMethodSpecialCase(md, head, reasons);
}
}
}