| /******************************************************************************* |
| * Copyright (c) 2007, 2016 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.pde.api.tools.internal.provisional.comparator; |
| |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers; |
| import org.eclipse.pde.api.tools.internal.util.Util; |
| |
| /** |
| * Class used to process the delta to find out if they are compatible or not. |
| * |
| * @since 1.0.0 |
| */ |
| public class DeltaProcessor { |
| |
| /** |
| * Return true is the given delta is compatible, false otherwise. |
| * |
| * @param delta the given delta |
| * @return true is the given delta is compatible, false otherwise. |
| */ |
| public static boolean isCompatible(IDelta delta) { |
| class CompatibleVisitor extends DeltaVisitor { |
| boolean isCompatible = true; |
| |
| @Override |
| public boolean visit(IDelta delta) { |
| if (!this.isCompatible) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public void endVisit(IDelta delta) { |
| if (this.isCompatible) { |
| this.isCompatible = isCompatible0(delta); |
| } |
| } |
| } |
| if (delta.getChildren().length != 0) { |
| CompatibleVisitor visitor = new CompatibleVisitor(); |
| delta.accept(visitor); |
| return visitor.isCompatible; |
| } else { |
| return isCompatible0(delta); |
| } |
| } |
| |
| /** |
| * Returns if the delta is compatible or not |
| * |
| * @param delta |
| * @return true if the delta represents a compatible change or not |
| */ |
| static boolean isCompatible0(IDelta delta) { |
| switch (delta.getElementType()) { |
| case IDelta.API_BASELINE_ELEMENT_TYPE: { |
| return isApiProfileCompatible(delta); |
| } |
| case IDelta.API_COMPONENT_ELEMENT_TYPE: { |
| return isApiComponentCompatible(delta); |
| } |
| case IDelta.INTERFACE_ELEMENT_TYPE: { |
| return isInterfaceCompatible(delta); |
| } |
| case IDelta.ANNOTATION_ELEMENT_TYPE: { |
| return isAnnotationCompatible(delta); |
| } |
| case IDelta.METHOD_ELEMENT_TYPE: { |
| return isMethodCompatible(delta); |
| } |
| case IDelta.CONSTRUCTOR_ELEMENT_TYPE: { |
| return isConstructorCompatible(delta); |
| } |
| case IDelta.FIELD_ELEMENT_TYPE: { |
| return isFieldCompatible(delta); |
| } |
| case IDelta.CLASS_ELEMENT_TYPE: { |
| return isClassCompatible(delta); |
| } |
| case IDelta.ENUM_ELEMENT_TYPE: { |
| return isEnumCompatible(delta); |
| } |
| case IDelta.TYPE_PARAMETER_ELEMENT_TYPE: { |
| return isTypeParameterCompatible(delta); |
| } |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if the API baseline is compatible |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isApiProfileCompatible(IDelta delta) { |
| switch (delta.getKind()) { |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.API_COMPONENT: |
| return false; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if the API component is compatible |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isApiComponentCompatible(IDelta delta) { |
| switch (delta.getKind()) { |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.TYPE: |
| case IDelta.API_TYPE: |
| case IDelta.REEXPORTED_API_TYPE: |
| case IDelta.REEXPORTED_TYPE: |
| return false; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if the annotation is compatible or not |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isAnnotationCompatible(IDelta delta) { |
| switch (delta.getKind()) { |
| case IDelta.ADDED: |
| switch (delta.getFlags()) { |
| case IDelta.FIELD: |
| case IDelta.TYPE_PARAMETER: |
| case IDelta.METHOD_WITHOUT_DEFAULT_VALUE: |
| return false; |
| default: |
| break; |
| } |
| break; |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.FIELD: |
| case IDelta.METHOD_WITHOUT_DEFAULT_VALUE: |
| case IDelta.METHOD_WITH_DEFAULT_VALUE: |
| case IDelta.API_FIELD: |
| case IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE: |
| case IDelta.API_METHOD_WITH_DEFAULT_VALUE: |
| case IDelta.TYPE_MEMBER: |
| case IDelta.TYPE_PARAMETER: |
| return false; |
| default: |
| break; |
| } |
| break; |
| case IDelta.CHANGED: |
| switch (delta.getFlags()) { |
| case IDelta.CONTRACTED_SUPERINTERFACES_SET: |
| case IDelta.TYPE_CONVERSION: |
| return false; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if the method is compatible or not |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isMethodCompatible(IDelta delta) { |
| int restrictions = delta.getCurrentRestrictions(); |
| if (RestrictionModifiers.isReferenceRestriction(restrictions)) { |
| return true; |
| } |
| switch (delta.getKind()) { |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.ANNOTATION_DEFAULT_VALUE: |
| case IDelta.TYPE_PARAMETER: |
| return !Util.isVisible(delta.getOldModifiers()); |
| default: |
| break; |
| } |
| break; |
| case IDelta.ADDED: |
| switch (delta.getFlags()) { |
| case IDelta.TYPE_PARAMETER: |
| case IDelta.RESTRICTIONS: |
| if (Util.isVisible(delta.getNewModifiers())) { |
| return RestrictionModifiers.isExtendRestriction(delta.getPreviousRestrictions()); |
| } |
| return true; |
| default: |
| break; |
| } |
| break; |
| case IDelta.CHANGED: |
| switch (delta.getFlags()) { |
| case IDelta.VARARGS_TO_ARRAY: |
| case IDelta.NON_ABSTRACT_TO_ABSTRACT: |
| case IDelta.NON_STATIC_TO_STATIC: |
| case IDelta.STATIC_TO_NON_STATIC: |
| return !Util.isVisible(delta.getNewModifiers()); |
| case IDelta.DECREASE_ACCESS: |
| return !Util.isVisible(delta.getOldModifiers()) || ( Flags.isProtected(delta.getOldModifiers()) && RestrictionModifiers.isExtendRestriction(restrictions)); |
| case IDelta.NON_FINAL_TO_FINAL: |
| return !Util.isVisible(delta.getOldModifiers()) || !Util.isVisible(delta.getNewModifiers()) || RestrictionModifiers.isExtendRestriction(restrictions) || RestrictionModifiers.isOverrideRestriction(restrictions); |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if the field is compatible or not |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isFieldCompatible(IDelta delta) { |
| int restrictions = delta.getCurrentRestrictions(); |
| if (RestrictionModifiers.isReferenceRestriction(restrictions)) { |
| return true; |
| } |
| int newModifiers = delta.getNewModifiers(); |
| int oldModifiers = delta.getOldModifiers(); |
| switch (delta.getKind()) { |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.VALUE: |
| if (Flags.isProtected(oldModifiers)) { |
| return RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions()); |
| } |
| if (Flags.isPublic(oldModifiers)) { |
| return false; |
| } |
| // not visible |
| return true; |
| case IDelta.TYPE_ARGUMENTS: |
| case IDelta.TYPE_ARGUMENT: |
| return !Util.isVisible(oldModifiers); |
| default: |
| break; |
| } |
| break; |
| case IDelta.CHANGED: |
| if (!Util.isVisible(oldModifiers)) { |
| return true; |
| } |
| switch (delta.getFlags()) { |
| case IDelta.TYPE: |
| if (Flags.isProtected(newModifiers)) { |
| return RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions()); |
| } |
| return !Util.isVisible(newModifiers); |
| case IDelta.TYPE_ARGUMENT: |
| case IDelta.NON_FINAL_TO_FINAL: |
| case IDelta.STATIC_TO_NON_STATIC: |
| case IDelta.NON_STATIC_TO_STATIC: |
| return !Util.isVisible(newModifiers); |
| case IDelta.VALUE: |
| case IDelta.FINAL_TO_NON_FINAL_STATIC_CONSTANT: |
| if (Flags.isProtected(newModifiers)) { |
| return RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions()); |
| } |
| if (Flags.isPublic(newModifiers)) { |
| return false; |
| } |
| // not visible |
| return true; |
| case IDelta.DECREASE_ACCESS: |
| // if the initial flag was protected, decrease access is |
| // compatible if class is extend restricted. |
| return Flags.isProtected(delta.getOldModifiers()) && RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions()); |
| default: |
| break; |
| } |
| break; |
| case IDelta.ADDED: |
| switch (delta.getFlags()) { |
| case IDelta.TYPE_ARGUMENT: |
| return !Util.isVisible(newModifiers); |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if the constructor is compatible or not |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isConstructorCompatible(IDelta delta) { |
| int restrictions = delta.getCurrentRestrictions(); |
| if (RestrictionModifiers.isReferenceRestriction(restrictions)) { |
| return true; |
| } |
| switch (delta.getKind()) { |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.TYPE_PARAMETER: |
| return !Util.isVisible(delta.getOldModifiers()); |
| default: |
| break; |
| } |
| break; |
| case IDelta.ADDED: |
| switch (delta.getFlags()) { |
| case IDelta.TYPE_PARAMETER: |
| return !Util.isVisible(delta.getNewModifiers()); |
| default: |
| break; |
| } |
| break; |
| case IDelta.CHANGED: |
| switch (delta.getFlags()) { |
| case IDelta.VARARGS_TO_ARRAY: |
| case IDelta.NON_ABSTRACT_TO_ABSTRACT: |
| case IDelta.NON_STATIC_TO_STATIC: |
| case IDelta.STATIC_TO_NON_STATIC: |
| return !Util.isVisible(delta.getNewModifiers()); |
| case IDelta.DECREASE_ACCESS: |
| return Flags.isProtected(delta.getOldModifiers()) && RestrictionModifiers.isExtendRestriction(restrictions); |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if the enum is compatible or not |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isEnumCompatible(IDelta delta) { |
| switch (delta.getKind()) { |
| case IDelta.ADDED: |
| switch (delta.getFlags()) { |
| case IDelta.FIELD: |
| case IDelta.METHOD: |
| return !Util.isVisible(delta.getNewModifiers()); |
| default: |
| break; |
| } |
| break; |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.FIELD: |
| case IDelta.ENUM_CONSTANT: |
| case IDelta.METHOD: |
| case IDelta.CONSTRUCTOR: |
| case IDelta.TYPE_MEMBER: |
| return !Util.isVisible(delta.getOldModifiers()); |
| case IDelta.API_FIELD: |
| case IDelta.API_ENUM_CONSTANT: |
| case IDelta.API_METHOD: |
| case IDelta.API_CONSTRUCTOR: |
| return false; |
| default: |
| break; |
| } |
| break; |
| case IDelta.CHANGED: |
| if (!Util.isVisible(delta.getNewModifiers())) { |
| return true; |
| } |
| switch (delta.getFlags()) { |
| case IDelta.CONTRACTED_SUPERINTERFACES_SET: |
| case IDelta.NON_ABSTRACT_TO_ABSTRACT: |
| case IDelta.TYPE_CONVERSION: |
| return false; |
| case IDelta.DECREASE_ACCESS: |
| return !Util.isVisible(delta.getOldModifiers()); |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if a class file is compatible |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isClassCompatible(IDelta delta) { |
| switch (delta.getKind()) { |
| case IDelta.ADDED: |
| int newModifiers = delta.getNewModifiers(); |
| switch (delta.getFlags()) { |
| case IDelta.FIELD: |
| return true; |
| case IDelta.METHOD: |
| if (Util.isVisible(newModifiers)) { |
| if (Flags.isAbstract(newModifiers)) { |
| // case where the implementation is provided and |
| // the class cannot be instantiated by the |
| // client |
| return RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions()); |
| } |
| } |
| return true; |
| case IDelta.TYPE_PARAMETER: |
| case IDelta.RESTRICTIONS: |
| return !Util.isVisible(newModifiers); |
| default: |
| break; |
| } |
| break; |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.FIELD: |
| case IDelta.API_FIELD: |
| case IDelta.API_METHOD: |
| case IDelta.METHOD: |
| case IDelta.TYPE_MEMBER: |
| if (Flags.isPublic(delta.getOldModifiers())) { |
| return false; |
| } |
| if (Flags.isProtected(delta.getOldModifiers())) { |
| return RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions()); |
| } |
| return true; |
| case IDelta.CONSTRUCTOR: |
| case IDelta.API_CONSTRUCTOR: |
| if (Util.isVisible(delta.getOldModifiers())) { |
| return RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions()) && (Flags.isProtected(delta.getOldModifiers()) || RestrictionModifiers.isInstantiateRestriction(delta.getCurrentRestrictions())); |
| } |
| return true; |
| case IDelta.TYPE_PARAMETER: |
| case IDelta.SUPERCLASS: |
| return !Util.isVisible(delta.getOldModifiers()); |
| default: |
| break; |
| } |
| break; |
| case IDelta.CHANGED: |
| switch (delta.getFlags()) { |
| case IDelta.NON_ABSTRACT_TO_ABSTRACT: |
| if (Util.isVisible(delta.getNewModifiers())) { |
| return RestrictionModifiers.isInstantiateRestriction(delta.getCurrentRestrictions()); |
| } |
| return true; |
| case IDelta.TYPE_CONVERSION: |
| case IDelta.CONTRACTED_SUPERINTERFACES_SET: |
| case IDelta.STATIC_TO_NON_STATIC: |
| case IDelta.NON_STATIC_TO_STATIC: |
| return !Util.isVisible(delta.getNewModifiers()); |
| case IDelta.NON_FINAL_TO_FINAL: |
| if (Util.isVisible(delta.getNewModifiers())) { |
| return RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions()); |
| } |
| return true; |
| case IDelta.DECREASE_ACCESS: |
| return (Flags.isProtected(delta.getOldModifiers()) && RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions())); |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if the interface element is compatible |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isTypeParameterCompatible(IDelta delta) { |
| switch (delta.getKind()) { |
| case IDelta.ADDED: |
| switch (delta.getFlags()) { |
| case IDelta.CLASS_BOUND: |
| case IDelta.INTERFACE_BOUND: |
| return false; |
| default: |
| break; |
| } |
| break; |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.CLASS_BOUND: |
| case IDelta.INTERFACE_BOUND: |
| return false; |
| default: |
| break; |
| } |
| break; |
| case IDelta.CHANGED: |
| switch (delta.getFlags()) { |
| case IDelta.CLASS_BOUND: |
| case IDelta.INTERFACE_BOUND: |
| return false; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns if the interface element is compatible |
| * |
| * @param delta |
| * @return true if compatible, false otherwise |
| */ |
| private static boolean isInterfaceCompatible(IDelta delta) { |
| switch (delta.getKind()) { |
| case IDelta.ADDED: |
| switch (delta.getFlags()) { |
| case IDelta.FIELD: |
| case IDelta.METHOD: |
| case IDelta.DEFAULT_METHOD: |
| case IDelta.SUPER_INTERFACE_WITH_METHODS: |
| return RestrictionModifiers.isImplementRestriction(delta.getPreviousRestrictions()) || RestrictionModifiers.isImplementRestriction(delta.getCurrentRestrictions()); |
| case IDelta.TYPE_PARAMETER: |
| return false; |
| case IDelta.RESTRICTIONS: |
| return false; |
| default: |
| break; |
| } |
| break; |
| case IDelta.REMOVED: |
| switch (delta.getFlags()) { |
| case IDelta.FIELD: |
| case IDelta.METHOD: |
| case IDelta.API_FIELD: |
| case IDelta.API_METHOD: |
| case IDelta.TYPE_MEMBER: |
| case IDelta.TYPE_PARAMETER: |
| return false; |
| default: |
| break; |
| } |
| break; |
| case IDelta.CHANGED: |
| switch (delta.getFlags()) { |
| case IDelta.CONTRACTED_SUPERINTERFACES_SET: |
| case IDelta.TYPE_CONVERSION: |
| return false; |
| case IDelta.DECREASE_ACCESS: |
| return RestrictionModifiers.isExtendRestriction(delta.getCurrentRestrictions()); |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| } |