IncompatibleDifferenceStrategy.java

/*
 *  Copyright (c) 2001-2024, Jean Tessier
 *  All rights reserved.
 *  
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *  
 *      * Redistributions in binary form must reproduce the above copyright
 *        notice, this list of conditions and the following disclaimer in the
 *        documentation and/or other materials provided with the distribution.
 *  
 *      * Neither the name of Jean Tessier nor the names of his contributors
 *        may be used to endorse or promote products derived from this software
 *        without specific prior written permission.
 *  
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jeantessier.diff;

import java.util.*;

import com.jeantessier.classreader.*;

public class IncompatibleDifferenceStrategy extends APIDifferenceStrategy {
    public IncompatibleDifferenceStrategy(DifferenceStrategy strategy) {
        super(strategy);
    }

    public boolean isClassDifferent(Classfile oldClass, Classfile newClass) {
        return isRemoved(oldClass, newClass) ||
               (!isNew(oldClass, newClass) &&
                isClassModified(oldClass, newClass));
    }

    public boolean isFieldDifferent(Field_info oldField, Field_info newField) {
        return isRemoved(oldField, newField) ||
               isDeprecationModified(oldField, newField) ||
               isDeclarationModified(oldField, newField);
    }

    public boolean isMethodDifferent(Method_info oldMethod, Method_info newMethod) {
        return isRemoved(oldMethod, newMethod) ||
               isDeprecationModified(oldMethod, newMethod) ||
               isDeclarationModified(oldMethod, newMethod);
    }

    public boolean isPackageDifferent(Map<String, Classfile> oldPackage, Map<String, Classfile> newPackage) {
        return isPackageRemoved(oldPackage, newPackage) ||
               (!isPackageNew(oldPackage, newPackage) &&
                isPackageModified(oldPackage, newPackage));
    }

    public boolean isDeclarationModified(Classfile oldClass, Classfile newClass) {
        return oldClass != null && newClass != null &&
               ((oldClass.isPublic() && !newClass.isPublic()) ||
                (oldClass.isInterface() != newClass.isInterface()) ||
                (!oldClass.isAbstract() && newClass.isAbstract()) ||
                (!oldClass.isFinal() && newClass.isFinal()) ||
                isExtendsClauseModified(oldClass, newClass) ||
                isImplementsClauseModified(oldClass, newClass));
    }

    private boolean isExtendsClauseModified(Classfile oldClass, Classfile newClass) {
        return !oldClass.getSuperclassName().equals(newClass.getSuperclassName());
    }

    private boolean isImplementsClauseModified(Classfile oldClass, Classfile newClass) {
        return !oldClass.getAllInterfaces().containsAll(newClass.getAllInterfaces()) ||
               !newClass.getAllInterfaces().containsAll(oldClass.getAllInterfaces());
    }

    private boolean isDeclarationModified(Field_info oldField, Field_info newField) {
        return oldField != null && newField != null &&
               ((oldField.isPublic() && !newField.isPublic()) ||
                (oldField.isProtected() && (newField.isPackage() || newField.isPrivate())) ||
                (!oldField.isFinal() && newField.isFinal()) ||
                !oldField.getType().equals(newField.getType()));
    }

    private boolean isDeclarationModified(Method_info oldMethod, Method_info newMethod) {
        return oldMethod != null && newMethod != null &&
               ((oldMethod.isPublic() && !newMethod.isPublic()) ||
                (oldMethod.isProtected() && (newMethod.isPackage() || newMethod.isPrivate())) ||
                (!oldMethod.isAbstract() && newMethod.isAbstract()) ||
                (!oldMethod.isStatic() && newMethod.isStatic()) ||
                (!oldMethod.isFinal() && newMethod.isFinal()) ||
                !oldMethod.getReturnType().equals(newMethod.getReturnType()) ||
                isThrowsClauseModified(oldMethod, newMethod));
    }

    private boolean isThrowsClauseModified(Method_info oldMethod, Method_info newMethod) {
        return !oldMethod.getExceptions().containsAll(newMethod.getExceptions()) ||
               !newMethod.getExceptions().containsAll(oldMethod.getExceptions());
    }

    protected boolean isDeprecationModified(Deprecatable oldItem, Deprecatable newItem) {
        return oldItem != null && newItem != null && !oldItem.isDeprecated() && newItem.isDeprecated();
    }
}