| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package signature.compare; |
| |
| import signature.compare.model.IAnnotationDelta; |
| import signature.compare.model.IAnnotationElementDelta; |
| import signature.compare.model.IAnnotationFieldDelta; |
| import signature.compare.model.IApiDelta; |
| import signature.compare.model.IClassDefinitionDelta; |
| import signature.compare.model.IConstructorDelta; |
| import signature.compare.model.IDelta; |
| import signature.compare.model.IEnumConstantDelta; |
| import signature.compare.model.IFieldDelta; |
| import signature.compare.model.IGenericDeclarationDelta; |
| import signature.compare.model.IMethodDelta; |
| import signature.compare.model.IModifierDelta; |
| import signature.compare.model.IPackageDelta; |
| import signature.compare.model.IParameterDelta; |
| import signature.compare.model.IParameterizedTypeDelta; |
| import signature.compare.model.IPrimitiveTypeDelta; |
| import signature.compare.model.ITypeReferenceDelta; |
| import signature.compare.model.ITypeVariableDefinitionDelta; |
| import signature.compare.model.IUpperBoundsDelta; |
| import signature.compare.model.IValueDelta; |
| import signature.compare.model.IWildcardTypeDelta; |
| import signature.compare.model.impl.SigAnnotationDelta; |
| import signature.compare.model.impl.SigAnnotationElementDelta; |
| import signature.compare.model.impl.SigAnnotationFieldDelta; |
| import signature.compare.model.impl.SigApiDelta; |
| import signature.compare.model.impl.SigArrayTypeDelta; |
| import signature.compare.model.impl.SigClassDefinitionDelta; |
| import signature.compare.model.impl.SigClassReferenceDelta; |
| import signature.compare.model.impl.SigConstructorDelta; |
| import signature.compare.model.impl.SigEnumConstantDelta; |
| import signature.compare.model.impl.SigFieldDelta; |
| import signature.compare.model.impl.SigGenericDeclarationDelta; |
| import signature.compare.model.impl.SigMethodDelta; |
| import signature.compare.model.impl.SigModifierDelta; |
| import signature.compare.model.impl.SigPackageDelta; |
| import signature.compare.model.impl.SigParameterDelta; |
| import signature.compare.model.impl.SigParameterizedTypeDelta; |
| import signature.compare.model.impl.SigPrimitiveTypeDelta; |
| import signature.compare.model.impl.SigTypeDelta; |
| import signature.compare.model.impl.SigTypeVariableDefinitionDelta; |
| import signature.compare.model.impl.SigTypeVariableReferenceDelta; |
| import signature.compare.model.impl.SigUpperBoundsDelta; |
| import signature.compare.model.impl.SigValueDelta; |
| import signature.compare.model.impl.SigWildcardTypeDelta; |
| import signature.compare.model.subst.ClassProjection; |
| import signature.compare.model.subst.ViewpointAdapter; |
| import signature.model.IAnnotation; |
| import signature.model.IAnnotationElement; |
| import signature.model.IAnnotationField; |
| import signature.model.IApi; |
| import signature.model.IArrayType; |
| import signature.model.IClassDefinition; |
| import signature.model.IClassReference; |
| import signature.model.IConstructor; |
| import signature.model.IEnumConstant; |
| import signature.model.IExecutableMember; |
| import signature.model.IField; |
| import signature.model.IGenericDeclaration; |
| import signature.model.IMethod; |
| import signature.model.IPackage; |
| import signature.model.IParameter; |
| import signature.model.IParameterizedType; |
| import signature.model.IPrimitiveType; |
| import signature.model.ITypeReference; |
| import signature.model.ITypeVariableDefinition; |
| import signature.model.ITypeVariableReference; |
| import signature.model.IWildcardType; |
| import signature.model.Kind; |
| import signature.model.Modifier; |
| import signature.model.impl.SigAnnotationElement; |
| import signature.model.impl.SigArrayType; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * {@code ApiComparator} takes two signature models as input and creates a delta |
| * model describing the differences between those. |
| */ |
| public class ApiComparator implements IApiComparator { |
| |
| public IApiDelta compare(IApi from, IApi to) { |
| assert from.getVisibility() == to.getVisibility(); |
| |
| Set<IPackage> fromPackages = from.getPackages(); |
| Set<IPackage> toPackages = to.getPackages(); |
| |
| Set<IPackageDelta> packageDeltas = compareSets(fromPackages, |
| toPackages, new SigComparator<IPackage, IPackageDelta>() { |
| public IPackageDelta createChangedDelta(IPackage from, |
| IPackage to) { |
| return comparePackage(from, to); |
| } |
| |
| public IPackageDelta createAddRemoveDelta(IPackage from, |
| IPackage to) { |
| return new SigPackageDelta(from, to); |
| } |
| |
| public boolean considerEqualElement(IPackage from, |
| IPackage to) { |
| return from.getName().equals(to.getName()); |
| } |
| }); |
| |
| SigApiDelta delta = null; |
| if (packageDeltas != null) { |
| delta = new SigApiDelta(from, to); |
| delta.setPackageDeltas(packageDeltas); |
| } |
| return delta; |
| } |
| |
| private IPackageDelta comparePackage(IPackage from, IPackage to) { |
| assert from.getName().equals(to.getName()); |
| |
| Set<IClassDefinition> fromClasses = from.getClasses(); |
| Set<IClassDefinition> toClasses = to.getClasses(); |
| |
| Set<IClassDefinitionDelta> classDeltas = compareSets(fromClasses, |
| toClasses, |
| new SigComparator<IClassDefinition, IClassDefinitionDelta>() { |
| public boolean considerEqualElement(IClassDefinition from, |
| IClassDefinition to) { |
| return sameClassDefinition(from, to); |
| } |
| |
| public IClassDefinitionDelta createChangedDelta( |
| IClassDefinition from, IClassDefinition to) { |
| return compareClass(from, to); |
| } |
| |
| public IClassDefinitionDelta createAddRemoveDelta( |
| IClassDefinition from, IClassDefinition to) { |
| return new SigClassDefinitionDelta(from, to); |
| } |
| }); |
| |
| SigPackageDelta delta = null; |
| if (classDeltas != null) { |
| delta = new SigPackageDelta(from, to); |
| delta.setClassDeltas(classDeltas); |
| } |
| |
| // Annotations |
| Set<IAnnotationDelta> annotationDeltas = compareAnnotations(from |
| .getAnnotations(), to.getAnnotations()); |
| if (annotationDeltas != null) { |
| if (delta != null) { |
| delta = new SigPackageDelta(from, to); |
| } |
| delta.setAnnotationDeltas(annotationDeltas); |
| } |
| return delta; |
| } |
| |
| private IClassDefinitionDelta compareClass(IClassDefinition from, |
| IClassDefinition to) { |
| assert from.getKind() == to.getKind(); |
| assert from.getName().equals(to.getName()); |
| assert from.getPackageName().equals(to.getPackageName()); |
| |
| SigClassDefinitionDelta classDelta = null; |
| |
| // modifiers |
| Set<IModifierDelta> modifierDeltas = compareModifiers(from |
| .getModifiers(), to.getModifiers()); |
| if (modifierDeltas != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setModifierDeltas(modifierDeltas); |
| } |
| |
| // super class |
| ITypeReferenceDelta<?> superTypeDelta = compareType(from |
| .getSuperClass(), to.getSuperClass(), false); |
| if (superTypeDelta != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setSuperClassDelta(superTypeDelta); |
| } |
| |
| // interfaces |
| Set<ITypeReferenceDelta<?>> interfaceDeltas = compareInterfaces(from, |
| to); |
| if (interfaceDeltas != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setInterfaceDeltas(interfaceDeltas); |
| } |
| |
| // type variables |
| Set<ITypeVariableDefinitionDelta> typeVariableDeltas = |
| compareTypeVariableSequence(from.getTypeParameters(), |
| to.getTypeParameters()); |
| if (typeVariableDeltas != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setTypeVariableDeltas(typeVariableDeltas); |
| } |
| |
| // constructors |
| Set<IConstructorDelta> constructorDeltas = compareConstructors(from |
| .getConstructors(), to.getConstructors()); |
| if (constructorDeltas != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setConstructorDeltas(constructorDeltas); |
| } |
| |
| // methods |
| Set<IMethodDelta> methodDeltas = compareMethods(from, to); |
| if (methodDeltas != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setMethodDeltas(methodDeltas); |
| } |
| |
| // fields |
| Set<IFieldDelta> fieldDeltas = compareFields(from.getFields(), to |
| .getFields()); |
| if (fieldDeltas != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setFieldDeltas(fieldDeltas); |
| } |
| |
| // enum constants |
| if (from.getKind() == Kind.ENUM) { |
| Set<IEnumConstantDelta> enumDeltas = compareEnumConstants(from |
| .getEnumConstants(), to.getEnumConstants()); |
| if (enumDeltas != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setEnumConstantDeltas(enumDeltas); |
| } |
| } else if (from.getKind() == Kind.ANNOTATION) { |
| Set<IAnnotationFieldDelta> annotationFieldDeltas = |
| compareAnnotationFields(from.getAnnotationFields(), |
| to.getAnnotationFields()); |
| if (annotationFieldDeltas != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setAnnotationFieldDeltas(annotationFieldDeltas); |
| } |
| } |
| |
| Set<IAnnotationDelta> annotationDeltas = compareAnnotations(from |
| .getAnnotations(), to.getAnnotations()); |
| if (annotationDeltas != null) { |
| if (classDelta == null) { |
| classDelta = new SigClassDefinitionDelta(from, to); |
| } |
| classDelta.setAnnotationDeltas(annotationDeltas); |
| } |
| return classDelta; |
| } |
| |
| private Set<ITypeReferenceDelta<?>> compareInterfaces( |
| IClassDefinition from, IClassDefinition to) { |
| Set<ITypeReference> fromClosure = getInterfaceClosure(from); |
| Set<ITypeReference> toClosure = getInterfaceClosure(to); |
| |
| Set<ITypeReference> fromInterfaces = from.getInterfaces(); |
| Set<ITypeReference> toInterfaces = to.getInterfaces(); |
| |
| Set<ITypeReferenceDelta<?>> deltas = |
| new HashSet<ITypeReferenceDelta<?>>(); |
| |
| // check whether all from interfaces are directly or indirectly |
| // implemented by the to method |
| for (ITypeReference type : fromInterfaces) { |
| if (!containsType(type, toInterfaces)) { |
| if (!(containsType(type, toClosure) /* |
| * && !containsType(type, |
| * toInterfaces) |
| */)) { |
| deltas.add(new SigTypeDelta<ITypeReference>(type, null)); |
| } |
| } |
| } |
| |
| // check whether all interfaces to are directly or indirectly |
| // implemented by the from method |
| for (ITypeReference type : toInterfaces) { |
| if (!containsType(type, fromInterfaces)) { |
| if (!(containsType(type, fromClosure) /* |
| * && !containsType(type, |
| * fromInterfaces) |
| */)) { |
| deltas.add(new SigTypeDelta<ITypeReference>(null, type)); |
| } |
| } |
| } |
| return deltas.isEmpty() ? null : deltas; |
| } |
| |
| |
| private boolean containsType(ITypeReference type, |
| Set<ITypeReference> setOfTypes) { |
| for (ITypeReference other : setOfTypes) { |
| if (compareType(type, other, false) == null) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private Set<ITypeReference> getInterfaceClosure(IClassDefinition clazz) { |
| Set<ITypeReference> closure = new HashSet<ITypeReference>(); |
| collectInterfaceClosure(ViewpointAdapter.getReferenceTo(clazz), |
| closure); |
| return closure; |
| } |
| |
| private void collectInterfaceClosure(ITypeReference clazz, |
| Set<ITypeReference> closure) { |
| |
| IClassDefinition classDefinition = getClassDefinition(clazz); |
| Set<ITypeReference> interfaces = classDefinition.getInterfaces(); |
| if (interfaces == null) { |
| return; |
| } |
| for (ITypeReference interfaze : interfaces) { |
| closure.add(interfaze); |
| } |
| |
| ITypeReference superclass = classDefinition.getSuperClass(); |
| if (superclass != null) { |
| if (superclass instanceof IParameterizedType) { |
| collectInterfaceClosure(((IParameterizedType) superclass) |
| .getRawType(), closure); |
| } else { |
| collectInterfaceClosure(superclass, closure); |
| } |
| } |
| for (ITypeReference interfaze : interfaces) { |
| if (interfaze instanceof IParameterizedType) { |
| collectInterfaceClosure(((IParameterizedType) interfaze) |
| .getRawType(), closure); |
| } else { |
| collectInterfaceClosure(interfaze, closure); |
| } |
| } |
| } |
| |
| private Set<IAnnotationDelta> compareAnnotations(Set<IAnnotation> from, |
| Set<IAnnotation> to) { |
| return compareSets(from, to, |
| new SigComparator<IAnnotation, IAnnotationDelta>() { |
| public IAnnotationDelta createAddRemoveDelta( |
| IAnnotation from, IAnnotation to) { |
| return new SigAnnotationDelta(from, to); |
| } |
| |
| public boolean considerEqualElement(IAnnotation from, |
| IAnnotation to) { |
| return sameClassDefinition(from.getType() |
| .getClassDefinition(), to.getType() |
| .getClassDefinition()); |
| } |
| |
| public IAnnotationDelta createChangedDelta( |
| IAnnotation from, IAnnotation to) { |
| return compareAnnotation(from, to); |
| } |
| }); |
| } |
| |
| private Set<IAnnotationFieldDelta> compareAnnotationFields( |
| Set<IAnnotationField> from, Set<IAnnotationField> to) { |
| return compareSets(from, to, |
| new SigComparator<IAnnotationField, IAnnotationFieldDelta>() { |
| public boolean considerEqualElement(IAnnotationField from, |
| IAnnotationField to) { |
| return from.getName().equals(to.getName()); |
| } |
| |
| public IAnnotationFieldDelta createAddRemoveDelta( |
| IAnnotationField from, IAnnotationField to) { |
| return new SigAnnotationFieldDelta(from, to); |
| } |
| |
| public IAnnotationFieldDelta createChangedDelta( |
| IAnnotationField from, IAnnotationField to) { |
| return compareAnnotationField(from, to); |
| } |
| }); |
| } |
| |
| private Set<IEnumConstantDelta> compareEnumConstants( |
| Set<IEnumConstant> from, Set<IEnumConstant> to) { |
| return compareSets(from, to, |
| new SigComparator<IEnumConstant, IEnumConstantDelta>() { |
| public boolean considerEqualElement(IEnumConstant from, |
| IEnumConstant to) { |
| return from.getName().equals(to.getName()); |
| } |
| |
| public IEnumConstantDelta createAddRemoveDelta( |
| IEnumConstant from, IEnumConstant to) { |
| return new SigEnumConstantDelta(from, to); |
| } |
| |
| public IEnumConstantDelta createChangedDelta( |
| IEnumConstant from, IEnumConstant to) { |
| return compareEnumConstant(from, to); |
| } |
| }); |
| } |
| |
| private Set<IFieldDelta> compareFields(Set<IField> from, Set<IField> to) { |
| return compareSets(from, to, new SigComparator<IField, IFieldDelta>() { |
| public boolean considerEqualElement(IField from, IField to) { |
| return from.getName().equals(to.getName()); |
| } |
| |
| public IFieldDelta createAddRemoveDelta(IField from, IField to) { |
| return new SigFieldDelta(from, to); |
| } |
| |
| public IFieldDelta createChangedDelta(IField from, IField to) { |
| return compareField(from, to); |
| } |
| }); |
| } |
| |
| private Set<IMethodDelta> compareMethods(IClassDefinition from, |
| IClassDefinition to) { |
| assert from != null; |
| assert to != null; |
| |
| Set<IMethod> toMethods = new HashSet<IMethod>(to.getMethods()); |
| Set<IMethod> toClosure = getMethodClosure(to); |
| Set<IMethod> fromMethods = new HashSet<IMethod>(from.getMethods()); |
| Set<IMethod> fromClosure = getMethodClosure(from); |
| |
| Set<IMethodDelta> deltas = new HashSet<IMethodDelta>(); |
| |
| for (IMethod method : fromMethods) { |
| IMethod compatibleMethod = findCompatibleMethod(method, toMethods); |
| if (compatibleMethod == null) { |
| compatibleMethod = findCompatibleMethod(method, toClosure); |
| if (compatibleMethod == null) { |
| deltas.add(new SigMethodDelta(method, null)); |
| } |
| } |
| |
| if (compatibleMethod != null) { |
| IMethodDelta delta = compareMethod(method, compatibleMethod); |
| if (delta != null) { |
| deltas.add(delta); |
| } |
| } |
| } |
| |
| for (IMethod method : toMethods) { |
| IMethod compatibleMethod = findCompatibleMethod(method, fromMethods); |
| if (compatibleMethod == null) { |
| compatibleMethod = findCompatibleMethod(method, fromClosure); |
| if (compatibleMethod == null) { |
| deltas.add(new SigMethodDelta(null, method)); |
| } |
| } |
| } |
| return deltas.isEmpty() ? null : deltas; |
| } |
| |
| private IMethod findCompatibleMethod(IMethod method, Set<IMethod> set) { |
| for (IMethod methodFromSet : set) { |
| if (equalsSignature(method, methodFromSet)) { |
| return methodFromSet; |
| } |
| } |
| return null; |
| } |
| |
| |
| private Set<IMethod> getMethodClosure(IClassDefinition clazz) { |
| Set<IMethod> closure = new HashSet<IMethod>(); |
| collectMethods(new ClassProjection(clazz, |
| new HashMap<ITypeVariableDefinition, ITypeReference>()), |
| closure); |
| return closure; |
| } |
| |
| private void collectMethods(IClassDefinition clazz, Set<IMethod> closure) { |
| if (clazz == null) { |
| return; |
| } |
| if (clazz.getMethods() != null) { |
| closure.addAll(clazz.getMethods()); |
| } |
| if (clazz.getSuperClass() != null) { |
| collectMethods(getClassDefinition(clazz.getSuperClass()), closure); |
| } |
| if (clazz.getInterfaces() != null) { |
| for (ITypeReference interfaze : clazz.getInterfaces()) { |
| collectMethods(getClassDefinition(interfaze), closure); |
| } |
| } |
| } |
| |
| private Set<IConstructorDelta> compareConstructors(Set<IConstructor> from, |
| Set<IConstructor> to) { |
| return compareSets(from, to, |
| new SigComparator<IConstructor, IConstructorDelta>() { |
| public boolean considerEqualElement(IConstructor from, |
| IConstructor to) { |
| return equalsSignature(from, to); |
| } |
| |
| public IConstructorDelta createAddRemoveDelta( |
| IConstructor from, IConstructor to) { |
| return new SigConstructorDelta(from, to); |
| } |
| |
| public IConstructorDelta createChangedDelta( |
| IConstructor from, IConstructor to) { |
| return compareConstructor(from, to); |
| } |
| }); |
| } |
| |
| // compares names and parameter types |
| private boolean equalsSignature(IExecutableMember from, |
| IExecutableMember to) { |
| if (from.getName().equals(to.getName())) { |
| return compareTypeSequence(getParameterList(from.getParameters()), |
| getParameterList(to.getParameters()), true) == null; |
| } |
| return false; |
| } |
| |
| private List<ITypeReference> getParameterList(List<IParameter> parameters) { |
| List<ITypeReference> parameterTypes = new LinkedList<ITypeReference>(); |
| for (IParameter parameter : parameters) { |
| parameterTypes.add(parameter.getType()); |
| } |
| return parameterTypes; |
| } |
| |
| private IAnnotationDelta compareAnnotation(IAnnotation from, |
| IAnnotation to) { |
| assert sameClassDefinition(from.getType().getClassDefinition(), to |
| .getType().getClassDefinition()); |
| |
| Set<IAnnotationElement> fromAnnotationElement = |
| getNormalizedAnnotationElements(from); |
| Set<IAnnotationElement> toAnnotationElement = |
| getNormalizedAnnotationElements(to); |
| |
| Set<IAnnotationElementDelta> annotationElementDeltas = |
| compareAnnotationElements( |
| fromAnnotationElement, toAnnotationElement); |
| SigAnnotationDelta delta = null; |
| |
| if (annotationElementDeltas != null) { |
| delta = new SigAnnotationDelta(from, to); |
| delta.setAnnotationElementDeltas(annotationElementDeltas); |
| } |
| return delta; |
| } |
| |
| /** |
| * Returns the annotation elements for the given annotation. The returned |
| * set contains all declared elements plus all elements with default values. |
| * |
| * @param annotation |
| * the annotation to return the elements for |
| * @return the default enriched annotation elements |
| */ |
| private Set<IAnnotationElement> getNormalizedAnnotationElements( |
| IAnnotation annotation) { |
| Set<IAnnotationElement> elements = new HashSet<IAnnotationElement>( |
| annotation.getElements()); |
| |
| Set<String> names = new HashSet<String>(); |
| for (IAnnotationElement annotationElement : elements) { |
| names.add(annotationElement.getDeclaringField().getName()); |
| } |
| |
| for (IAnnotationField field : annotation.getType().getClassDefinition() |
| .getAnnotationFields()) { |
| if (!names.contains(field.getName())) { |
| SigAnnotationElement sigAnnotationElement = |
| new SigAnnotationElement(); |
| sigAnnotationElement.setDeclaringField(field); |
| sigAnnotationElement.setValue(field.getDefaultValue()); |
| elements.add(sigAnnotationElement); |
| } |
| } |
| return elements; |
| } |
| |
| private Set<IAnnotationElementDelta> compareAnnotationElements( |
| Set<IAnnotationElement> from, Set<IAnnotationElement> to) { |
| return compareSets(from, to, |
| new SigComparator<IAnnotationElement, IAnnotationElementDelta>() { |
| public boolean considerEqualElement( |
| IAnnotationElement from, IAnnotationElement to) { |
| return from.getDeclaringField().getName().equals( |
| to.getDeclaringField().getName()); |
| } |
| |
| public IAnnotationElementDelta createAddRemoveDelta( |
| IAnnotationElement from, IAnnotationElement to) { |
| return new SigAnnotationElementDelta(from, to); |
| } |
| |
| public IAnnotationElementDelta createChangedDelta( |
| IAnnotationElement from, IAnnotationElement to) { |
| return compareAnnotationElement(from, to); |
| } |
| }); |
| } |
| |
| private IAnnotationElementDelta compareAnnotationElement( |
| IAnnotationElement from, IAnnotationElement to) { |
| SigAnnotationElementDelta delta = null; |
| SigValueDelta valueDelta = compareValue(from.getValue(), to.getValue()); |
| |
| if (valueDelta != null) { |
| delta = new SigAnnotationElementDelta(from, to); |
| delta.setValueDelta(valueDelta); |
| } |
| return delta; |
| } |
| |
| /** |
| * Removes the {@link Modifier#ABSTRACT} modifier. |
| */ |
| private Set<Modifier> prepareMethodModifiers(IMethod method) { |
| Set<Modifier> modifierCopy = new HashSet<Modifier>(method |
| .getModifiers()); |
| modifierCopy.remove(Modifier.ABSTRACT); |
| return modifierCopy; |
| } |
| |
| private IMethodDelta compareMethod(IMethod from, IMethod to) { |
| assert from != null && to != null; |
| |
| SigMethodDelta methodDelta = null; |
| Set<IModifierDelta> modiferDeltas = compareModifiers( |
| prepareMethodModifiers(from), prepareMethodModifiers(to)); |
| if (modiferDeltas != null) { |
| methodDelta = new SigMethodDelta(from, to); |
| methodDelta.setModifierDeltas(modiferDeltas); |
| } |
| |
| Set<IParameterDelta> parameterDeltas = compareParameterSequence(from |
| .getParameters(), to.getParameters()); |
| if (parameterDeltas != null) { |
| if (methodDelta == null) { |
| methodDelta = new SigMethodDelta(from, to); |
| } |
| methodDelta.setParameterDeltas(parameterDeltas); |
| } |
| |
| Set<IAnnotationDelta> annotationDeltas = compareAnnotations(from |
| .getAnnotations(), to.getAnnotations()); |
| if (annotationDeltas != null) { |
| if (methodDelta == null) { |
| methodDelta = new SigMethodDelta(from, to); |
| } |
| methodDelta.setAnnotationDeltas(annotationDeltas); |
| } |
| |
| Set<ITypeVariableDefinitionDelta> typeParameterDeltas = |
| compareTypeVariableSequence(from.getTypeParameters(), |
| to.getTypeParameters()); |
| if (typeParameterDeltas != null) { |
| if (methodDelta == null) { |
| methodDelta = new SigMethodDelta(from, to); |
| } |
| methodDelta.setTypeVariableDeltas(typeParameterDeltas); |
| } |
| |
| Set<ITypeReferenceDelta<?>> exceptionDeltas = compareTypes( |
| normalizeExceptions(from.getExceptions()), |
| normalizeExceptions(to.getExceptions())); |
| if (exceptionDeltas != null) { |
| if (methodDelta == null) { |
| methodDelta = new SigMethodDelta(from, to); |
| } |
| methodDelta.setExceptionDeltas(exceptionDeltas); |
| } |
| |
| ITypeReferenceDelta<?> returnTypeDelta = compareType(from |
| .getReturnType(), to.getReturnType(), false); |
| if (returnTypeDelta != null) { |
| if (methodDelta == null) { |
| methodDelta = new SigMethodDelta(from, to); |
| } |
| methodDelta.setReturnTypeDelta(returnTypeDelta); |
| } |
| |
| return methodDelta; |
| } |
| |
| // remove runtime exceptions, |
| // remove sub types of containing exception |
| private Set<ITypeReference> normalizeExceptions( |
| Set<ITypeReference> exceptions) { |
| Set<ITypeReference> exceptionCopy = new HashSet<ITypeReference>( |
| exceptions); |
| |
| Iterator<ITypeReference> iterator = exceptionCopy.iterator(); |
| while (iterator.hasNext()) { |
| ITypeReference exception = iterator.next(); |
| if (isRuntimeExceptionOrErrorSubtype(exception)) { |
| iterator.remove(); |
| } |
| } |
| exceptionCopy = removeSpecializations(exceptionCopy); |
| return exceptionCopy; |
| } |
| |
| private Set<ITypeReference> removeSpecializations( |
| Set<ITypeReference> exceptions) { |
| Set<ITypeReference> exceptionCopy = new HashSet<ITypeReference>( |
| exceptions); |
| for (ITypeReference type : exceptions) { |
| Iterator<ITypeReference> it = exceptionCopy.iterator(); |
| while (it.hasNext()) { |
| ITypeReference subType = it.next(); |
| if (isSuperClass(getClassDefinition(type), |
| getClassDefinition(subType))) { |
| it.remove(); |
| } |
| } |
| } |
| return exceptionCopy; |
| } |
| |
| /** |
| * Returns true if superC is a super class of subC. |
| */ |
| private boolean isSuperClass(IClassDefinition superC, |
| IClassDefinition subC) { |
| if (superC == null || subC == null) { |
| return false; |
| } |
| |
| if (subC.getSuperClass() == null) { |
| return false; |
| } else { |
| if (getClassDefinition(subC.getSuperClass()).equals(superC)) { |
| return true; |
| } else { |
| return isSuperClass(superC, getClassDefinition(subC |
| .getSuperClass())); |
| } |
| } |
| } |
| |
| private boolean isSuperInterface(IClassDefinition superClass, |
| IClassDefinition subClass) { |
| if (superClass == null || subClass == null) { |
| return false; |
| } |
| |
| if (subClass.getInterfaces() == null) { |
| return false; |
| } else { |
| if (getClassDefinitions(subClass.getInterfaces()).contains( |
| superClass)) { |
| return true; |
| } else { |
| for (ITypeReference subType : subClass.getInterfaces()) { |
| if (isSuperInterface(superClass, |
| getClassDefinition(subType))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| } |
| |
| private Set<IClassDefinition> getClassDefinitions( |
| Set<ITypeReference> references) { |
| Set<IClassDefinition> definitions = new HashSet<IClassDefinition>(); |
| for (ITypeReference ref : references) { |
| definitions.add(getClassDefinition(ref)); |
| } |
| return definitions; |
| } |
| |
| /** |
| * Returns null if type is not one of: |
| * <ul> |
| * <li>IClassReference</li> |
| * <li>IParameterizedType</li> |
| * </ul> |
| */ |
| private IClassDefinition getClassDefinition(ITypeReference type) { |
| assert type != null; |
| |
| IClassDefinition returnValue = null; |
| if (type instanceof IClassReference) { |
| returnValue = ((IClassReference) type).getClassDefinition(); |
| } else if (type instanceof IParameterizedType) { |
| returnValue = ((IParameterizedType) type).getRawType() |
| .getClassDefinition(); |
| } |
| return returnValue; |
| } |
| |
| private boolean isRuntimeExceptionOrErrorSubtype(ITypeReference exception) { |
| |
| IClassDefinition clazz = getClassDefinition(exception); |
| if (clazz != null) { |
| if (isRuntimeExceptionOrError(clazz)) { |
| return true; |
| } else if (clazz.getSuperClass() != null) { |
| return isRuntimeExceptionOrErrorSubtype(clazz.getSuperClass()); |
| } else { |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| private boolean isRuntimeExceptionOrError(IClassDefinition exception) { |
| if (exception == null) { |
| return false; |
| } |
| String packageName = exception.getPackageName(); |
| String className = exception.getName(); |
| |
| if (packageName != null && className != null |
| && "java.lang".equals(packageName)) { |
| return "RuntimeException".equals(className) |
| || "Error".equals(className); |
| } |
| return false; |
| } |
| |
| private IConstructorDelta compareConstructor(IConstructor from, |
| IConstructor to) { |
| SigConstructorDelta constructorDelta = null; |
| Set<IModifierDelta> modiferDeltas = compareModifiers(from |
| .getModifiers(), to.getModifiers()); |
| if (modiferDeltas != null) { |
| constructorDelta = new SigConstructorDelta(from, to); |
| constructorDelta.setModifierDeltas(modiferDeltas); |
| } |
| |
| Set<IParameterDelta> parameterDeltas = compareParameterSequence(from |
| .getParameters(), to.getParameters()); |
| if (parameterDeltas != null) { |
| if (constructorDelta == null) { |
| constructorDelta = new SigConstructorDelta(from, to); |
| } |
| constructorDelta.setParameterDeltas(parameterDeltas); |
| } |
| |
| Set<IAnnotationDelta> annotationDeltas = compareAnnotations(from |
| .getAnnotations(), to.getAnnotations()); |
| if (annotationDeltas != null) { |
| if (constructorDelta == null) { |
| constructorDelta = new SigConstructorDelta(from, to); |
| } |
| constructorDelta.setAnnotationDeltas(annotationDeltas); |
| } |
| |
| Set<ITypeVariableDefinitionDelta> typeParameterDeltas = |
| compareTypeVariableSequence(from.getTypeParameters(), |
| to.getTypeParameters()); |
| if (typeParameterDeltas != null) { |
| if (constructorDelta == null) { |
| constructorDelta = new SigConstructorDelta(from, to); |
| } |
| constructorDelta.setTypeVariableDeltas(typeParameterDeltas); |
| } |
| |
| Set<ITypeReferenceDelta<?>> exceptionDeltas = compareTypes( |
| normalizeExceptions(from.getExceptions()), |
| normalizeExceptions(to.getExceptions())); |
| if (exceptionDeltas != null) { |
| if (constructorDelta == null) { |
| constructorDelta = new SigConstructorDelta(from, to); |
| } |
| constructorDelta.setExceptionDeltas(exceptionDeltas); |
| } |
| return constructorDelta; |
| } |
| |
| private Set<IParameterDelta> compareParameterSequence( |
| List<IParameter> from, List<IParameter> to) { |
| assert from.size() == to.size(); |
| Set<IParameterDelta> deltas = new HashSet<IParameterDelta>(); |
| Iterator<IParameter> fromIterator = from.iterator(); |
| Iterator<IParameter> toIterator = to.iterator(); |
| while (fromIterator.hasNext() && toIterator.hasNext()) { |
| IParameterDelta delta = compareParameter(fromIterator.next(), |
| toIterator.next()); |
| if (delta != null) { |
| deltas.add(delta); |
| } |
| } |
| return deltas.isEmpty() ? null : deltas; |
| } |
| |
| private IParameterDelta compareParameter(IParameter from, IParameter to) { |
| SigParameterDelta delta = null; |
| ITypeReferenceDelta<?> typeDelta = compareType(from.getType(), to |
| .getType(), false); |
| if (typeDelta != null) { |
| if (delta == null) { |
| delta = new SigParameterDelta(from, to); |
| } |
| delta.setTypeDelta(typeDelta); |
| } |
| |
| Set<IAnnotationDelta> annotationDeltas = compareAnnotations(from |
| .getAnnotations(), to.getAnnotations()); |
| if (annotationDeltas != null) { |
| if (delta == null) { |
| delta = new SigParameterDelta(from, to); |
| } |
| delta.setAnnotationDeltas(annotationDeltas); |
| } |
| return delta; |
| } |
| |
| private Set<ITypeVariableDefinitionDelta> compareTypeVariableSequence( |
| List<ITypeVariableDefinition> from, |
| List<ITypeVariableDefinition> to) { |
| Set<ITypeVariableDefinitionDelta> deltas = |
| new HashSet<ITypeVariableDefinitionDelta>(); |
| if (from.size() != to.size()) { |
| for (ITypeVariableDefinition fromVariable : from) { |
| deltas.add(new SigTypeVariableDefinitionDelta(fromVariable, |
| null)); |
| } |
| for (ITypeVariableDefinition toVariable : to) { |
| deltas |
| .add(new SigTypeVariableDefinitionDelta(null, |
| toVariable)); |
| } |
| } |
| |
| Iterator<ITypeVariableDefinition> fromIterator = from.iterator(); |
| Iterator<ITypeVariableDefinition> toIterator = to.iterator(); |
| while (fromIterator.hasNext() && toIterator.hasNext()) { |
| ITypeVariableDefinitionDelta delta = compareTypeVariableDefinition( |
| fromIterator.next(), toIterator.next()); |
| if (delta != null) { |
| deltas.add(delta); |
| } |
| } |
| return deltas.isEmpty() ? null : deltas; |
| } |
| |
| private ITypeVariableDefinitionDelta compareTypeVariableDefinition( |
| ITypeVariableDefinition from, ITypeVariableDefinition to) { |
| IGenericDeclarationDelta declarationDelta = compareGenericDeclaration( |
| from, to); |
| |
| if (declarationDelta != null) { |
| SigTypeVariableDefinitionDelta delta = |
| new SigTypeVariableDefinitionDelta(from, to); |
| delta.setGenericDeclarationDelta(declarationDelta); |
| return delta; |
| } |
| IUpperBoundsDelta upperBoundDelta = compareUpperBounds(from |
| .getUpperBounds(), to.getUpperBounds()); |
| |
| if (upperBoundDelta != null) { |
| SigTypeVariableDefinitionDelta delta = |
| new SigTypeVariableDefinitionDelta(from, to); |
| delta.setUpperBoundsDelta(upperBoundDelta); |
| return delta; |
| } |
| return null; |
| } |
| |
| private ITypeReferenceDelta<ITypeVariableReference> compareTypeVariableReference( |
| ITypeVariableReference from, ITypeVariableReference to) { |
| IGenericDeclarationDelta declarationDelta = compareGenericDeclaration( |
| from.getTypeVariableDefinition(), to |
| .getTypeVariableDefinition()); |
| if (declarationDelta != null) { |
| SigTypeVariableReferenceDelta delta = |
| new SigTypeVariableReferenceDelta(from, to); |
| delta.setGenericDeclarationDelta(declarationDelta); |
| return delta; |
| } |
| return null; |
| } |
| |
| private Set<IModifierDelta> compareModifiers(Set<Modifier> from, |
| Set<Modifier> to) { |
| return compareSets(from, to, |
| new SigComparator<Modifier, IModifierDelta>() { |
| public boolean considerEqualElement(Modifier from, |
| Modifier to) { |
| return from.equals(to); |
| } |
| |
| public IModifierDelta createAddRemoveDelta(Modifier from, |
| Modifier to) { |
| return new SigModifierDelta(from, to); |
| } |
| |
| public IModifierDelta createChangedDelta(Modifier from, |
| Modifier to) { |
| return null; |
| } |
| }); |
| } |
| |
| |
| private IFieldDelta compareField(IField from, IField to) { |
| SigFieldDelta fieldDelta = null; |
| |
| Set<IModifierDelta> modiferDeltas = compareModifiers(from |
| .getModifiers(), to.getModifiers()); |
| if (modiferDeltas != null) { |
| fieldDelta = new SigFieldDelta(from, to); |
| fieldDelta.setModifierDeltas(modiferDeltas); |
| } |
| |
| Set<IAnnotationDelta> annotationDeltas = compareAnnotations(from |
| .getAnnotations(), to.getAnnotations()); |
| if (annotationDeltas != null) { |
| if (fieldDelta == null) { |
| fieldDelta = new SigFieldDelta(from, to); |
| } |
| fieldDelta.setAnnotationDeltas(annotationDeltas); |
| } |
| |
| ITypeReferenceDelta<?> typeDelta = compareType(from.getType(), to |
| .getType(), false); |
| if (typeDelta != null) { |
| if (fieldDelta == null) { |
| fieldDelta = new SigFieldDelta(from, to); |
| } |
| fieldDelta.setTypeDelta(typeDelta); |
| } |
| return fieldDelta; |
| } |
| |
| private IEnumConstantDelta compareEnumConstant(IEnumConstant from, |
| IEnumConstant to) { |
| SigEnumConstantDelta enumConstantDelta = null; |
| |
| Set<IModifierDelta> modiferDeltas = compareModifiers(from |
| .getModifiers(), to.getModifiers()); |
| if (modiferDeltas != null) { |
| enumConstantDelta = new SigEnumConstantDelta(from, to); |
| enumConstantDelta.setModifierDeltas(modiferDeltas); |
| } |
| |
| Set<IAnnotationDelta> annotationDeltas = compareAnnotations(from |
| .getAnnotations(), to.getAnnotations()); |
| if (annotationDeltas != null) { |
| if (enumConstantDelta == null) { |
| enumConstantDelta = new SigEnumConstantDelta(from, to); |
| } |
| enumConstantDelta.setAnnotationDeltas(annotationDeltas); |
| } |
| |
| ITypeReferenceDelta<?> typeDelta = compareType(from.getType(), to |
| .getType(), false); |
| if (typeDelta != null) { |
| if (enumConstantDelta == null) { |
| enumConstantDelta = new SigEnumConstantDelta(from, to); |
| } |
| enumConstantDelta.setTypeDelta(typeDelta); |
| } |
| |
| // FIXME ordinal not supported in dex |
| // ValueDelta ordinalDelta = compareValue(from.getOrdinal(), |
| // to.getOrdinal()); |
| // if (ordinalDelta != null) { |
| // if (enumConstantDelta == null) { |
| // enumConstantDelta = new SigEnumConstantDelta(from, to); |
| // } |
| // enumConstantDelta.setOrdinalDelta(ordinalDelta); |
| // } |
| |
| return enumConstantDelta; |
| } |
| |
| private IAnnotationFieldDelta compareAnnotationField(IAnnotationField from, |
| IAnnotationField to) { |
| SigAnnotationFieldDelta annotationFieldDelta = null; |
| |
| Set<IModifierDelta> modiferDeltas = compareModifiers(from |
| .getModifiers(), to.getModifiers()); |
| if (modiferDeltas != null) { |
| annotationFieldDelta = new SigAnnotationFieldDelta(from, to); |
| annotationFieldDelta.setModifierDeltas(modiferDeltas); |
| } |
| |
| Set<IAnnotationDelta> annotationDeltas = compareAnnotations(from |
| .getAnnotations(), to.getAnnotations()); |
| if (annotationDeltas != null) { |
| if (annotationFieldDelta == null) { |
| annotationFieldDelta = new SigAnnotationFieldDelta(from, to); |
| } |
| annotationFieldDelta.setAnnotationDeltas(annotationDeltas); |
| } |
| |
| ITypeReferenceDelta<?> typeDelta = compareType(from.getType(), to |
| .getType(), false); |
| if (typeDelta != null) { |
| if (annotationFieldDelta == null) { |
| annotationFieldDelta = new SigAnnotationFieldDelta(from, to); |
| } |
| annotationFieldDelta.setTypeDelta(typeDelta); |
| } |
| |
| IValueDelta defaultValueDelta = compareValue(from.getDefaultValue(), to |
| .getDefaultValue()); |
| if (defaultValueDelta != null) { |
| if (annotationFieldDelta == null) { |
| annotationFieldDelta = new SigAnnotationFieldDelta(from, to); |
| } |
| annotationFieldDelta.setDefaultValueDelta(defaultValueDelta); |
| } |
| |
| return annotationFieldDelta; |
| } |
| |
| private SigValueDelta compareValue(Object from, Object to) { |
| // same value |
| if (from == null && to == null) { |
| return null; |
| } |
| |
| // one of both is null and other is not |
| if (from == null || to == null) { |
| return new SigValueDelta(from, to); |
| } |
| |
| SigValueDelta delta = null; |
| // different types |
| if (from.getClass() == to.getClass()) { |
| if (from.getClass().isArray()) { |
| Object[] fromArray = (Object[]) from; |
| Object[] toArray = (Object[]) from; |
| if (!Arrays.equals(fromArray, toArray)) { |
| delta = new SigValueDelta(from, to); |
| } |
| } else if (from instanceof IEnumConstant) { |
| IEnumConstantDelta enumConstantDelta = compareEnumConstant( |
| (IEnumConstant) from, (IEnumConstant) to); |
| if (enumConstantDelta != null) { |
| delta = new SigValueDelta(from, to); |
| } |
| } else if (from instanceof IAnnotation) { |
| IAnnotationDelta annotationDelta = compareAnnotation( |
| (IAnnotation) from, (IAnnotation) to); |
| if (annotationDelta != null) { |
| delta = new SigValueDelta(from, to); |
| } |
| } else if (from instanceof IField) { |
| IFieldDelta fieldDelta = compareField((IField) from, |
| (IField) to); |
| if (fieldDelta != null) { |
| delta = new SigValueDelta(from, to); |
| } |
| } else if (from instanceof ITypeReference) { |
| ITypeReferenceDelta<? extends ITypeReference> typeDelta = |
| compareType((ITypeReference) from, (ITypeReference) to, |
| false); |
| if (typeDelta != null) { |
| delta = new SigValueDelta(from, to); |
| } |
| } else if (!from.equals(to)) { |
| delta = new SigValueDelta(from, to); |
| } |
| |
| } else if (!(from == null && to == null)) { |
| delta = new SigValueDelta(from, to); |
| } |
| return delta; |
| } |
| |
| private boolean considerEqualTypes(ITypeReference from, ITypeReference to) { |
| assert from != null && to != null; |
| |
| if (implementInterface(from, to, IPrimitiveType.class)) { |
| return comparePrimitiveType((IPrimitiveType) from, |
| (IPrimitiveType) to) == null; |
| } |
| if (implementInterface(from, to, IClassReference.class)) { |
| return sameClassDefinition(((IClassReference) from) |
| .getClassDefinition(), ((IClassReference) to) |
| .getClassDefinition()); |
| } |
| if (implementInterface(from, to, IArrayType.class)) { |
| return considerEqualTypes(((IArrayType) from).getComponentType(), |
| ((IArrayType) to).getComponentType()); |
| } |
| if (implementInterface(from, to, IParameterizedType.class)) { |
| return compareClassReference(((IParameterizedType) from) |
| .getRawType(), ((IParameterizedType) to) |
| .getRawType()) == null; |
| } |
| if (implementInterface(from, to, ITypeVariableReference.class)) { |
| return compareTypeVariableReference((ITypeVariableReference) from, |
| (ITypeVariableReference) to) == null; |
| } |
| |
| return false; |
| } |
| |
| private Set<ITypeReference> fromComparison = new HashSet<ITypeReference>(); |
| private Set<ITypeReference> toComparison = new HashSet<ITypeReference>(); |
| |
| |
| private boolean areInComparison(ITypeReference from, ITypeReference to) { |
| return fromComparison.contains(from) && toComparison.contains(to); |
| } |
| |
| private void markInComparison(ITypeReference from, ITypeReference to) { |
| fromComparison.add(from); |
| toComparison.add(to); |
| } |
| |
| private void markFinishedComparison(ITypeReference from, |
| ITypeReference to) { |
| fromComparison.remove(from); |
| toComparison.remove(to); |
| } |
| |
| private ITypeReferenceDelta<? extends ITypeReference> compareType( |
| ITypeReference from, ITypeReference to, boolean acceptErasedTypes) { |
| |
| if (from == null && to == null) { |
| return null; |
| } |
| if ((from == null && to != null) || (from != null && to == null)) { |
| return new SigTypeDelta<ITypeReference>(from, to); |
| } |
| if (areInComparison(from, to)) { |
| return null; |
| } |
| try { |
| markInComparison(from, to); |
| |
| if (implementInterface(from, to, IPrimitiveType.class)) { |
| return comparePrimitiveType((IPrimitiveType) from, |
| (IPrimitiveType) to); |
| } |
| if (implementInterface(from, to, IClassReference.class)) { |
| return compareClassReference((IClassReference) from, |
| (IClassReference) to); |
| } |
| if (implementInterface(from, to, IArrayType.class)) { |
| return compareArrayType((IArrayType) from, (IArrayType) to); |
| } |
| if (implementInterface(from, to, IParameterizedType.class)) { |
| return compareParameterizedType((IParameterizedType) from, |
| (IParameterizedType) to, acceptErasedTypes); |
| } |
| if (implementInterface(from, to, ITypeVariableReference.class)) { |
| return compareTypeVariableReference( |
| (ITypeVariableReference) from, |
| (ITypeVariableReference) to); |
| } |
| if (implementInterface(from, to, IWildcardType.class)) { |
| return compareWildcardType((IWildcardType) from, |
| (IWildcardType) to); |
| } |
| |
| if (acceptErasedTypes) { |
| if (isGeneric(from) && !isGeneric(to)) { |
| return compareType(getErasedType(from), to, false); |
| } |
| |
| if (!isGeneric(from) && isGeneric(to)) { |
| return compareType(from, getErasedType(to), false); |
| } |
| } |
| return new SigTypeDelta<ITypeReference>(from, to); |
| } finally { |
| markFinishedComparison(from, to); |
| } |
| } |
| |
| private boolean isGeneric(ITypeReference reference) { |
| if (reference instanceof IParameterizedType |
| || reference instanceof ITypeVariableReference |
| || reference instanceof IWildcardType) { |
| return true; |
| } |
| if (reference instanceof IArrayType) { |
| return isGeneric(((IArrayType) reference).getComponentType()); |
| } |
| return false; |
| } |
| |
| private ITypeReference getErasedType(ITypeReference reference) { |
| |
| if (reference instanceof IParameterizedType) { |
| return ((IParameterizedType) reference).getRawType(); |
| } |
| if (reference instanceof ITypeVariableReference) { |
| ITypeVariableDefinition typeVariableDefinition = |
| ((ITypeVariableReference) reference) |
| .getTypeVariableDefinition(); |
| return getErasedType( |
| typeVariableDefinition.getUpperBounds().get(0)); |
| } |
| if (reference instanceof IWildcardType) { |
| return getErasedType(((IWildcardType) reference).getUpperBounds() |
| .get(0)); |
| } |
| if (reference instanceof IArrayType) { |
| // FIXME implement with erasure projection? |
| return new SigArrayType(getErasedType(((IArrayType) reference) |
| .getComponentType())); |
| } |
| if (reference instanceof IPrimitiveType) { |
| return reference; |
| } |
| if (reference instanceof IClassReference) { |
| return reference; |
| } |
| throw new IllegalArgumentException("Unexpected type: " + reference); |
| } |
| |
| private boolean implementInterface(ITypeReference from, ITypeReference to, |
| Class<?> check) { |
| return check.isAssignableFrom(from.getClass()) |
| && check.isAssignableFrom(to.getClass()); |
| } |
| |
| private IWildcardTypeDelta compareWildcardType(IWildcardType from, |
| IWildcardType to) { |
| SigWildcardTypeDelta delta = null; |
| |
| ITypeReference fromLowerBound = from.getLowerBound(); |
| ITypeReference toLowerBound = to.getLowerBound(); |
| |
| ITypeReferenceDelta<?> lowerBoundDelta = compareType(fromLowerBound, |
| toLowerBound, false); |
| if (lowerBoundDelta != null) { |
| delta = new SigWildcardTypeDelta(from, to); |
| delta.setLowerBoundDelta(lowerBoundDelta); |
| } |
| |
| IUpperBoundsDelta upperBoundsDelta = compareUpperBounds(from |
| .getUpperBounds(), to.getUpperBounds()); |
| if (upperBoundsDelta != null) { |
| if (delta == null) { |
| delta = new SigWildcardTypeDelta(from, to); |
| } |
| delta.setUpperBoundDelta(upperBoundsDelta); |
| } |
| return delta; |
| } |
| |
| private IGenericDeclarationDelta compareGenericDeclaration( |
| ITypeVariableDefinition fromVariable, |
| ITypeVariableDefinition toVariable) { |
| IGenericDeclarationDelta delta = null; |
| |
| IGenericDeclaration from = fromVariable.getGenericDeclaration(); |
| IGenericDeclaration to = toVariable.getGenericDeclaration(); |
| |
| if (from != null && to != null) { |
| |
| if (from.getClass() != to.getClass()) { |
| delta = new SigGenericDeclarationDelta(from, to); |
| } else if (from instanceof IClassDefinition) { |
| IClassDefinition fromDeclaringClass = (IClassDefinition) from; |
| IClassDefinition toDeclaringClass = (IClassDefinition) to; |
| |
| if (!sameClassDefinition(fromDeclaringClass, |
| toDeclaringClass)) { |
| delta = new SigGenericDeclarationDelta(from, to); |
| } |
| |
| } else if (from instanceof IConstructor) { |
| IConstructor fromConstructor = (IConstructor) from; |
| IConstructor toConstructor = (IConstructor) from; |
| |
| String fromConstructorName = fromConstructor.getName(); |
| String fromClassName = fromConstructor.getDeclaringClass() |
| .getQualifiedName(); |
| |
| String toConstructorName = toConstructor.getName(); |
| String toClassName = toConstructor.getDeclaringClass() |
| .getQualifiedName(); |
| |
| if ((!fromConstructorName.equals(toConstructorName)) |
| || (!fromClassName.equals(toClassName))) { |
| delta = new SigGenericDeclarationDelta(from, to); |
| } |
| |
| } else if (from instanceof IMethod) { |
| IMethod fromMethod = (IMethod) from; |
| IMethod toMethod = (IMethod) from; |
| |
| String fromConstructorName = fromMethod.getName(); |
| String fromClassName = fromMethod.getDeclaringClass() |
| .getQualifiedName(); |
| |
| String toConstructorName = toMethod.getName(); |
| String toClassName = toMethod.getDeclaringClass() |
| .getQualifiedName(); |
| |
| if ((!fromConstructorName.equals(toConstructorName)) |
| || (!fromClassName.equals(toClassName))) { |
| delta = new SigGenericDeclarationDelta(from, to); |
| } |
| } else { |
| throw new IllegalStateException("Invlaid eclaration site: " |
| + from); |
| } |
| |
| // check position |
| int fromPosition = getPositionOf(fromVariable, from); |
| int toPosition = getPositionOf(toVariable, to); |
| |
| if (fromPosition != toPosition) { |
| delta = new SigGenericDeclarationDelta(from, to); |
| } |
| |
| |
| } else { |
| // one of both is null |
| delta = new SigGenericDeclarationDelta(from, to); |
| } |
| return delta; |
| } |
| |
| private int getPositionOf(ITypeVariableDefinition variable, |
| IGenericDeclaration declaration) { |
| return declaration.getTypeParameters().indexOf(variable); |
| } |
| |
| private IUpperBoundsDelta compareUpperBounds(List<ITypeReference> from, |
| List<ITypeReference> to) { |
| if (from.isEmpty() && to.isEmpty()) { |
| return null; |
| } |
| SigUpperBoundsDelta delta = null; |
| |
| ITypeReference fromFirstUpperBound = from.get(0); |
| ITypeReference toFirstUpperBound = to.get(0); |
| |
| ITypeReferenceDelta<?> firstUpperBoundDelta = compareType( |
| fromFirstUpperBound, toFirstUpperBound, false); |
| if (firstUpperBoundDelta != null) { |
| delta = new SigUpperBoundsDelta(from, to); |
| delta.setFirstUpperBoundDelta(firstUpperBoundDelta); |
| } else { |
| // normalize |
| Set<ITypeReference> normalizedfrom = removeGeneralizations( |
| new HashSet<ITypeReference>(from)); |
| Set<ITypeReference> normalizedto = removeGeneralizations( |
| new HashSet<ITypeReference>(to)); |
| |
| Set<ITypeReferenceDelta<?>> remainingUpperBoundsDelta = |
| compareTypes(normalizedfrom, normalizedto); |
| if (remainingUpperBoundsDelta != null) { |
| delta = new SigUpperBoundsDelta(from, to); |
| delta.setRemainingUpperBoundDeltas(remainingUpperBoundsDelta); |
| } |
| } |
| return delta; |
| } |
| |
| private Set<ITypeReference> removeGeneralizations( |
| Set<ITypeReference> bounds) { |
| Set<ITypeReference> boundsCopy = new HashSet<ITypeReference>(bounds); |
| for (ITypeReference type : bounds) { |
| Iterator<ITypeReference> it = boundsCopy.iterator(); |
| while (it.hasNext()) { |
| ITypeReference superType = it.next(); |
| if (isSuperClass(getClassDefinition(superType), |
| getClassDefinition(type)) |
| || isSuperInterface(getClassDefinition(superType), |
| getClassDefinition(type))) { |
| it.remove(); |
| } |
| } |
| } |
| return boundsCopy; |
| } |
| |
| private IParameterizedTypeDelta compareParameterizedType( |
| IParameterizedType from, IParameterizedType to, |
| boolean ignoreTypeArguments) { |
| |
| SigParameterizedTypeDelta delta = null; |
| // check raw type |
| ITypeReferenceDelta<?> rawTypeDelta = compareType(from.getRawType(), to |
| .getRawType(), false); |
| if (rawTypeDelta != null) { |
| delta = new SigParameterizedTypeDelta(from, to); |
| delta.setRawTypeDelta(rawTypeDelta); |
| } else { |
| // check owner type |
| ITypeReferenceDelta<?> ownerTypeDelta = compareType(from |
| .getOwnerType(), to.getOwnerType(), false); |
| if (ownerTypeDelta != null) { |
| delta = new SigParameterizedTypeDelta(from, to); |
| delta.setOwnerTypeDelta(ownerTypeDelta); |
| } else { |
| // check argument type |
| if (!ignoreTypeArguments) { |
| Set<ITypeReferenceDelta<?>> argumentTypeDeltas = |
| compareTypeSequence(from.getTypeArguments(), |
| to.getTypeArguments(), false); |
| if (argumentTypeDeltas != null) { |
| delta = new SigParameterizedTypeDelta(from, to); |
| delta.setArgumentTypeDeltas(argumentTypeDeltas); |
| } |
| } |
| } |
| } |
| return delta; |
| } |
| |
| private Set<ITypeReferenceDelta<? extends ITypeReference>> compareTypeSequence( |
| List<ITypeReference> from, List<ITypeReference> to, |
| boolean ignoreTypeArguments) { |
| Set<ITypeReferenceDelta<?>> deltas = |
| new HashSet<ITypeReferenceDelta<?>>(); |
| if (from.size() != to.size()) { |
| |
| for (ITypeReference type : from) { |
| deltas.add(new SigTypeDelta<ITypeReference>(type, null)); |
| } |
| for (ITypeReference type : to) { |
| deltas.add(new SigTypeDelta<ITypeReference>(null, type)); |
| } |
| return deltas; |
| } |
| |
| Iterator<? extends ITypeReference> fromIterator = from.iterator(); |
| Iterator<? extends ITypeReference> toIterator = to.iterator(); |
| while (fromIterator.hasNext() && toIterator.hasNext()) { |
| ITypeReferenceDelta<?> delta = compareType(fromIterator.next(), |
| toIterator.next(), ignoreTypeArguments); |
| if (delta != null) { |
| deltas.add(delta); |
| } |
| } |
| return deltas.isEmpty() ? null : deltas; |
| } |
| |
| private Set<ITypeReferenceDelta<? extends ITypeReference>> compareTypes( |
| Set<ITypeReference> from, Set<ITypeReference> to) { |
| return compareSets(from, to, |
| new SigComparator<ITypeReference, ITypeReferenceDelta<? extends ITypeReference>>() { |
| public ITypeReferenceDelta<? extends ITypeReference> createAddRemoveDelta( |
| ITypeReference from, ITypeReference to) { |
| return new SigTypeDelta<ITypeReference>(from, to); |
| } |
| |
| public boolean considerEqualElement(ITypeReference from, |
| ITypeReference to) { |
| return considerEqualTypes(from, to); |
| } |
| |
| public ITypeReferenceDelta<? extends ITypeReference> createChangedDelta( |
| ITypeReference from, ITypeReference to) { |
| return compareType(from, to, false); |
| } |
| }); |
| } |
| |
| private static interface SigComparator<T, S extends IDelta<? extends T>> { |
| boolean considerEqualElement(T from, T to); |
| |
| S createChangedDelta(T from, T to); |
| |
| /** |
| * If null is returned, it will be ignored. |
| */ |
| S createAddRemoveDelta(T from, T to); |
| } |
| |
| |
| private <T, S extends IDelta<? extends T>> Set<S> compareSets(Set<T> from, |
| Set<T> to, SigComparator<T, S> comparator) { |
| |
| Set<T> toCopy = new HashSet<T>(to); |
| Set<S> deltas = new HashSet<S>(); |
| |
| for (T fromType : from) { |
| Iterator<T> toIterator = toCopy.iterator(); |
| boolean equals = false; |
| boolean hasNext = toIterator.hasNext(); |
| |
| while (hasNext && !equals) { |
| T toElement = toIterator.next(); |
| equals = comparator.considerEqualElement(fromType, toElement); |
| if (equals) { |
| S compare = comparator.createChangedDelta(fromType, |
| toElement); |
| if (compare != null) { |
| deltas.add(compare); |
| } |
| } |
| hasNext = toIterator.hasNext(); |
| } |
| |
| if (equals) { |
| toIterator.remove(); |
| } else { |
| S delta = comparator.createAddRemoveDelta(fromType, null); |
| if (delta != null) { |
| deltas.add(delta); |
| } |
| } |
| } |
| |
| for (T type : toCopy) { |
| S delta = comparator.createAddRemoveDelta(null, type); |
| if (delta != null) { |
| deltas.add(delta); |
| } |
| } |
| return deltas.isEmpty() ? null : deltas; |
| } |
| |
| |
| private ITypeReferenceDelta<?> compareArrayType(IArrayType from, |
| IArrayType to) { |
| ITypeReferenceDelta<?> componentTypeDelta = compareType(from |
| .getComponentType(), to.getComponentType(), false); |
| if (componentTypeDelta != null) { |
| SigArrayTypeDelta delta = new SigArrayTypeDelta(from, to); |
| delta.setComponentTypeDelta(componentTypeDelta); |
| return delta; |
| } |
| return null; |
| } |
| |
| private ITypeReferenceDelta<IClassReference> compareClassReference( |
| IClassReference fromRef, IClassReference toRef) { |
| IClassDefinition from = fromRef.getClassDefinition(); |
| IClassDefinition to = toRef.getClassDefinition(); |
| |
| if (!sameClassDefinition(from, to)) { |
| return new SigClassReferenceDelta(fromRef, toRef); |
| } |
| return null; |
| } |
| |
| |
| private boolean sameClassDefinition(IClassDefinition from, |
| IClassDefinition to) { |
| boolean sameName = from.getName().equals(to.getName()); |
| boolean samePackage = from.getPackageName().equals(to.getPackageName()); |
| |
| Kind fromKind = from.getKind(); |
| Kind toKind = to.getKind(); |
| boolean sameKind = (fromKind == null || toKind == null) |
| || fromKind.equals(toKind); |
| |
| return sameName && samePackage && sameKind; |
| } |
| |
| private IPrimitiveTypeDelta comparePrimitiveType(IPrimitiveType from, |
| IPrimitiveType to) { |
| if (!from.equals(to)) { |
| return new SigPrimitiveTypeDelta(from, to); |
| } |
| return null; |
| } |
| } |