private void areMethodsBinaryCompatible()

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