blob: 2c508db4b15810e0a79a5a5cc2b09c9a2cbfa681 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 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.comparator;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.Signature;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations;
import org.eclipse.pde.api.tools.internal.provisional.IApiDescription;
import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers;
import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers;
import org.eclipse.pde.api.tools.internal.provisional.comparator.ApiComparator;
import org.eclipse.pde.api.tools.internal.provisional.comparator.IDelta;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMemberDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiField;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiMember;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot;
import org.eclipse.pde.api.tools.internal.util.Signatures;
import org.eclipse.pde.api.tools.internal.util.Util;
import org.objectweb.asm.signature.SignatureReader;
import com.ibm.icu.text.MessageFormat;
/**
* Compares class files from the workspace to those in the default
* {@link IApiBaseline}
*
* @since 1.0.0
*/
public class ClassFileComparator {
private boolean isCheckedException(IApiBaseline baseline, IApiComponent apiComponent, String exceptionName) {
if (baseline == null) {
return true;
}
try {
if (Util.isJavaLangRuntimeException(exceptionName)) {
return false;
}
String packageName = Signatures.getPackageName(exceptionName);
IApiTypeRoot result = Util.getClassFile(baseline.resolvePackage(apiComponent, packageName), exceptionName);
if (result != null) {
// TODO should this be reported as a checked exception
IApiType exception = result.getStructure();
if (exception == null) {
return false;
}
while (!Util.isJavaLangObject(exception.getName())) {
String superName = exception.getSuperclassName();
packageName = Signatures.getPackageName(superName);
result = Util.getClassFile(baseline.resolvePackage(apiComponent, packageName), superName);
if (result == null) {
// TODO should we report this failure ?
if (ApiPlugin.DEBUG_CLASSFILE_COMPARATOR) {
System.err.println("CHECKED EXCEPTION LOOKUP: Could not find " + superName + " in baseline " + baseline.getName() + " from component " + apiComponent.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
break;
}
exception = result.getStructure();
if (Util.isJavaLangRuntimeException(exception.getName())) {
return false;
}
}
}
} catch (CoreException e) {
// by default exception are considered as checked exception
reportStatus(e);
}
return true;
}
private IApiBaseline apiBaseline1 = null;
private IApiBaseline apiBaseline2 = null;
private IApiComponent component = null;
private IApiComponent component2 = null;
private Delta delta = null;
private IApiType type1 = null;
private IApiType type2 = null;
private int visibilityModifiers;
private int currentDescriptorRestrictions;
private int initialDescriptorRestrictions;
private MultiStatus status = null;
/**
* Constructor
*
* @param classFile the class file from the workspace to compare
* @param classFile2 the class file from the baseline to compare to
* @param component the API component from the workspace
* @param component2 the API component from the baseline
* @param apiState the workspace API baseline
* @param apiState2 the baseline API baseline
* @param visibilityModifiers any modifiers from the class file
* @throws CoreException if the contents of the specified class files cannot
* be acquired
*/
public ClassFileComparator(IApiTypeRoot classFile, IApiTypeRoot classFile2, IApiComponent component, IApiComponent component2, IApiBaseline apiState, IApiBaseline apiState2, int visibilityModifiers) throws CoreException {
this.component = component;
this.component2 = component2;
this.type1 = classFile.getStructure();
this.type2 = classFile2.getStructure();
this.apiBaseline1 = apiState;
this.apiBaseline2 = apiState2;
this.visibilityModifiers = visibilityModifiers;
}
/**
* Constructor
*
* @param type the {@link IApiType} from the workspace to compare
* @param classFile2 the class file from the baseline to compare to
* @param component the API component from the workspace
* @param component2 the API component from the baseline
* @param apiState the workspace API baseline
* @param apiState2 the baseline API baseline
* @param visibilityModifiers any modifiers from the class file
* @throws CoreException if the contents of the specified class file cannot
* be acquired
*/
public ClassFileComparator(IApiType type, IApiTypeRoot classFile2, IApiComponent component, IApiComponent component2, IApiBaseline apiState, IApiBaseline apiState2, int visibilityModifiers) throws CoreException {
this.component = component;
this.component2 = component2;
this.type1 = type;
this.type2 = classFile2.getStructure();
this.apiBaseline1 = apiState;
this.apiBaseline2 = apiState2;
this.visibilityModifiers = visibilityModifiers;
}
private void addDelta(IDelta delta) {
this.delta.add(delta);
}
private void addDelta(int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, IApiType type, String key, String data) {
this.addDelta(new Delta(Util.getDeltaComponentVersionsId(this.component2), elementType, kind, flags, restrictions, oldModifiers, newModifiers, type.getName(), key, data));
}
private void addDelta(int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, IApiType type, String key, String[] datas) {
this.addDelta(new Delta(Util.getDeltaComponentVersionsId(this.component2), elementType, kind, flags, restrictions, 0, oldModifiers, newModifiers, type.getName(), key, datas));
}
private void addDelta(int elementType, int kind, int flags, int currentRestrictions, int previousRestrictions, int oldModifiers, int newModifiers, IApiType type, String key, String[] datas) {
this.addDelta(new Delta(Util.getDeltaComponentVersionsId(this.component2), elementType, kind, flags, currentRestrictions, previousRestrictions, oldModifiers, newModifiers, type.getName(), key, datas));
}
/**
* Checks if the super-class set has been change in any way compared to the
* baseline (grown or reduced or types changed)
*/
private void checkSuperclass() {
// check superclass set
List<?> superclassList1 = getSuperclassList(this.type1);
if (!isStatusOk()) {
return;
}
Set<String> superclassNames2 = null;
List<IApiType> superclassList2 = getSuperclassList(this.type2);
if (!isStatusOk()) {
return;
}
if (superclassList2 != null) {
superclassNames2 = new HashSet<>();
Iterator<IApiType> iterator = superclassList2.iterator();
while (iterator.hasNext()) {
superclassNames2.add(iterator.next().getName());
}
}
if (superclassList1 == null) {
if (superclassList2 != null) {
// this means the direct super class of descriptor1 is
// java.lang.Object
this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.SUPERCLASS, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
} else if (superclassList2 == null) {
// this means the direct super class of descriptor2 is
// java.lang.Object
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.SUPERCLASS, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
// get superclass of descriptor2
if (superclassList1 != null && superclassList2 != null) {
IApiType superclassType2 = superclassList2.get(0);
IApiType superclassType = (IApiType) superclassList1.get(0);
if (!superclassType.getName().equals(superclassType2.getName())) {
if (!superclassNames2.contains(superclassType.getName())) {
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.SUPERCLASS, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
} else {
this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.SUPERCLASS, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
}
}
}
/**
* reports problem status to the comparators' complete status
*
* @param newstatus
*/
protected void reportStatus(IStatus newstatus) {
if (this.status == null) {
String msg = MessageFormat.format(ComparatorMessages.ClassFileComparator_0, this.type1.getName());
this.status = new MultiStatus(ApiPlugin.PLUGIN_ID, IStatus.ERROR, msg, null);
}
this.status.add(newstatus);
}
/**
* Report problem to the comparators' status
*
* @param e
*/
private void reportStatus(CoreException e) {
reportStatus(e.getStatus());
}
/**
* @return if the status of the compare is ok
*/
private boolean isStatusOk() {
return this.status == null;
}
/**
* @return the status of the compare and delta creation
*/
public IStatus getStatus() {
return this.status;
}
/**
* Checks if there are any changes to the super-interface set for the
* current type descriptor context. A change is one of:
* <ul>
* <li>An interface has been added to the current super-interface set
* compared to the current baseline</li>
* <li>An interface has been removed from the current super-interface set
* compared to the current baseline</li>
* <li>An interface has changed (same number of interfaces, but different
* types) compared to the current baseline</li>
* </ul>
*/
private void checkSuperInterfaces() {
Set<IApiType> superinterfacesSet1 = getInterfacesSet(this.type1);
if (!isStatusOk()) {
return;
}
Set<IApiType> superinterfacesSet2 = getInterfacesSet(this.type2);
if (!isStatusOk()) {
return;
}
if (superinterfacesSet1 == null) {
if (superinterfacesSet2 != null) {
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.EXPANDED_SUPERINTERFACES_SET, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
if (this.type1.isInterface()) {
for (Iterator<IApiType> iterator = superinterfacesSet2.iterator(); iterator.hasNext();) {
IApiType type = iterator.next();
IApiMethod[] methods = type.getMethods();
int length = methods.length;
if (length != 0) {
// we should check if every method defined in the
// new interface exists in the old hierarchy
// could be methods moved up in the hierarchy
methodLoop: for (int j = 0; j < length; j++) {
IApiMethod method = methods[j];
IApiMethod method3 = this.type1.getMethod(method.getName(), method.getSignature());
if (method3 == null) {
this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.SUPER_INTERFACE_WITH_METHODS, this.currentDescriptorRestrictions, this.initialDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
type.getName(),
getMethodDisplayName(method, type) });
break methodLoop;
}
}
}
}
}
}
} else if (superinterfacesSet2 == null) {
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.CONTRACTED_SUPERINTERFACES_SET, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
} else {
Set<String> names2 = new HashSet<>();
for (Iterator<IApiType> iterator = superinterfacesSet2.iterator(); iterator.hasNext();) {
names2.add(iterator.next().getName());
}
boolean contracted = false;
for (Iterator<IApiType> iterator = superinterfacesSet1.iterator(); iterator.hasNext();) {
IApiType superInterfaceType = iterator.next();
if (!names2.remove(superInterfaceType.getName())) {
contracted = true;
}
}
if (contracted) {
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.CONTRACTED_SUPERINTERFACES_SET, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
return;
}
if (names2.size() > 0) {
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.EXPANDED_SUPERINTERFACES_SET, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
if (this.type1.isInterface()) {
for (Iterator<String> iterator = names2.iterator(); iterator.hasNext();) {
String interfaceName = iterator.next();
try {
IApiTypeRoot interfaceClassFile = getType(interfaceName, this.component2, this.apiBaseline2);
if (interfaceClassFile == null) {
continue;
}
IApiType type = interfaceClassFile.getStructure();
if (type == null) {
continue;
}
IApiMethod[] methods = type.getMethods();
int length = methods.length;
if (length > 0) {
// we should check if every method defined in
// the new interface exists in the old hierarchy
// could be methods moved up in the hierarchy
methodLoop: for (int j = 0; j < length; j++) {
IApiMethod method = methods[j];
boolean found = false;
interfaceLoop: for (Iterator<IApiType> iterator2 = superinterfacesSet1.iterator(); iterator2.hasNext();) {
IApiType superTypeDescriptor = iterator2.next();
IApiMethod method3 = superTypeDescriptor.getMethod(method.getName(), method.getSignature());
if (method3 == null) {
continue interfaceLoop;
} else {
found = true;
break interfaceLoop;
}
}
if (!found) {
this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.SUPER_INTERFACE_WITH_METHODS, this.currentDescriptorRestrictions, this.initialDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
type.getName(),
getMethodDisplayName(method, type) });
break methodLoop;
}
}
}
} catch (CoreException e) {
ApiPlugin.log(e);
}
}
}
}
}
}
private void checkTypeMembers() throws CoreException {
IApiType[] typeMembers = this.type1.getMemberTypes();
IApiType[] typeMembers2 = this.type2.getMemberTypes();
List<String> added = new ArrayList<>(typeMembers2.length);
for (int i = 0; i < typeMembers2.length; i++) {
added.add(typeMembers2[i].getName());
}
if (typeMembers.length > 0) {
if (typeMembers2.length == 0) {
loop: for (int i = 0; i < typeMembers.length; i++) {
try {
IApiType typeMember = typeMembers[i];
// check visibility
IApiDescription apiDescription = this.component.getApiDescription();
IApiAnnotations memberTypeElementDescription = apiDescription.resolveAnnotations(typeMember.getHandle());
int memberTypeVisibility = 0;
if (memberTypeElementDescription != null) {
memberTypeVisibility = memberTypeElementDescription.getVisibility();
}
if ((memberTypeVisibility & visibilityModifiers) == 0) {
// we skip the class file according to their
// visibility
continue loop;
}
if (visibilityModifiers == VisibilityModifiers.API) {
// if the visibility is API, we only consider public
// and protected types
if (Util.isDefault(typeMember.getModifiers()) || Flags.isPrivate(typeMember.getModifiers())) {
continue loop;
}
}
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.TYPE_MEMBER, this.currentDescriptorRestrictions, typeMember.getModifiers(), 0, this.type1, typeMember.getName(), new String[] {
typeMember.getName().replace('$', '.'),
Util.getComponentVersionsId(component2) });
} catch (CoreException e) {
reportStatus(e);
}
}
return;
}
// check removed or added type members
List<IApiType> removedTypeMembers = new ArrayList<>();
loop: for (int i = 0; i < typeMembers.length; i++) {
IApiType typeMember = typeMembers[i];
IApiType typeMember2 = this.type2.getMemberType(typeMember.getSimpleName());
if (typeMember2 == null) {
removedTypeMembers.add(typeMember);
} else {
added.remove(typeMember2.getName());
// check deltas inside the type member
try {
// check visibility of member types
IApiDescription apiDescription = this.component.getApiDescription();
IApiAnnotations memberTypeElementDescription = apiDescription.resolveAnnotations(typeMember.getHandle());
int memberTypeVisibility = 0;
if (memberTypeElementDescription != null) {
memberTypeVisibility = memberTypeElementDescription.getVisibility();
}
if ((memberTypeVisibility & visibilityModifiers) == 0) {
// we skip the class file according to their
// visibility
continue loop;
}
IApiDescription apiDescription2 = this.component2.getApiDescription();
IApiAnnotations memberTypeElementDescription2 = apiDescription2.resolveAnnotations(typeMember2.getHandle());
int memberTypeVisibility2 = 0;
if (memberTypeElementDescription2 != null) {
memberTypeVisibility2 = memberTypeElementDescription2.getVisibility();
}
String deltaComponentID = Util.getDeltaComponentVersionsId(component2);
int restrictions = memberTypeElementDescription2 != null ? memberTypeElementDescription2.getRestrictions() : RestrictionModifiers.NO_RESTRICTIONS;
if (Flags.isFinal(this.type2.getModifiers())) {
restrictions |= RestrictionModifiers.NO_EXTEND;
}
if (isAPI(memberTypeVisibility, typeMember) && !isAPI(memberTypeVisibility2, typeMember2)) {
this.addDelta(new Delta(deltaComponentID, getElementType(typeMember), IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions | this.currentDescriptorRestrictions, 0, typeMember.getModifiers(), typeMember2.getModifiers(), typeMember.getName(), typeMember.getName(), new String[] { typeMember.getName().replace('$', '.') }));
continue;
}
if ((memberTypeVisibility2 & visibilityModifiers) == 0) {
// we simply report a changed visibility
this.addDelta(new Delta(deltaComponentID, getElementType(typeMember), IDelta.CHANGED, IDelta.TYPE_VISIBILITY, restrictions | this.currentDescriptorRestrictions, 0, typeMember.getModifiers(), typeMember2.getModifiers(), typeMember.getName(), typeMember.getName(), new String[] { typeMember.getName().replace('$', '.') }));
}
if (this.visibilityModifiers == VisibilityModifiers.API) {
// if the visibility is API, we only consider public
// and protected types
if (Util.isDefault(typeMember2.getModifiers()) || Flags.isPrivate(typeMember2.getModifiers())) {
continue loop;
}
}
IApiTypeRoot memberType2 = this.component2.findTypeRoot(typeMember.getName());
ClassFileComparator comparator = new ClassFileComparator(typeMember, memberType2, this.component, this.component2, this.apiBaseline1, this.apiBaseline2, this.visibilityModifiers);
IDelta delta2 = comparator.getDelta(null);
if (delta2 != null && delta2 != ApiComparator.NO_DELTA) {
this.addDelta(delta2);
}
} catch (CoreException e) {
reportStatus(e);
}
}
}
loop: for (Iterator<IApiType> iterator = removedTypeMembers.iterator(); iterator.hasNext();) {
try {
IApiType typeMember = iterator.next();
// check visibility
IApiDescription apiDescription = this.component.getApiDescription();
IApiAnnotations memberTypeElementDescription = apiDescription.resolveAnnotations(typeMember.getHandle());
int memberTypeVisibility = 0;
if (memberTypeElementDescription != null) {
memberTypeVisibility = memberTypeElementDescription.getVisibility();
}
if ((memberTypeVisibility & visibilityModifiers) == 0) {
// we skip the class file according to their visibility
continue loop;
}
if (this.visibilityModifiers == VisibilityModifiers.API) {
// if the visibility is API, we only consider public and
// protected types
if (Util.isDefault(typeMember.getModifiers()) || Flags.isPrivate(typeMember.getModifiers())) {
continue loop;
}
}
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.TYPE_MEMBER, memberTypeElementDescription != null ? memberTypeElementDescription.getRestrictions() : RestrictionModifiers.NO_RESTRICTIONS, typeMember.getModifiers(), 0, this.type1, typeMember.getName(), new String[] {
typeMember.getName().replace('$', '.'),
Util.getComponentVersionsId(component2) });
} catch (CoreException e) {
reportStatus(e);
}
}
}
// report remaining types as addition
// Report delta as a breakage
loop: for (Iterator<String> iterator = added.iterator(); iterator.hasNext();) {
try {
String name = iterator.next();
int index = name.lastIndexOf('$');
IApiType typeMember = this.type2.getMemberType(name.substring(index + 1));
// check visibility
IApiDescription apiDescription2 = this.component2.getApiDescription();
IApiAnnotations memberTypeElementDescription2 = apiDescription2.resolveAnnotations(typeMember.getHandle());
int memberTypeVisibility2 = 0;
if (memberTypeElementDescription2 != null) {
memberTypeVisibility2 = memberTypeElementDescription2.getVisibility();
}
if ((memberTypeVisibility2 & visibilityModifiers) == 0) {
// we skip the class file according to their visibility
continue loop;
}
if (this.visibilityModifiers == VisibilityModifiers.API) {
// if the visibility is API, we only consider public and
// protected types
if (Util.isDefault(typeMember.getModifiers()) || Flags.isPrivate(typeMember.getModifiers())) {
continue loop;
}
}
this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.TYPE_MEMBER, this.currentDescriptorRestrictions, 0, typeMember.getModifiers(), this.type1, typeMember.getSimpleName(), typeMember.getSimpleName());
} catch (CoreException e) {
reportStatus(e);
}
}
}
private void checkGenericSignature(String signature1, String signature2, IApiMember element1, IApiMember element2) {
if (signature1 == null) {
if (signature2 != null) {
// added type parameter from scratch (none before)
// report delta as compatible
SignatureDescriptor signatureDescriptor2 = getSignatureDescriptor(signature2);
TypeParameterDescriptor[] typeParameterDescriptors = signatureDescriptor2.getTypeParameterDescriptors();
if (typeParameterDescriptors.length != 0) {
this.addDelta(getElementType(element1), IDelta.ADDED, IDelta.TYPE_PARAMETERS, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
getDataFor(element1, this.type1) });
} else if (signatureDescriptor2.getTypeArguments().length != 0) {
this.addDelta(getElementType(element1), IDelta.ADDED, IDelta.TYPE_ARGUMENTS, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
getDataFor(element1, this.type1) });
}
}
} else if (signature2 == null) {
// removed type parameters
SignatureDescriptor signatureDescriptor = getSignatureDescriptor(signature1);
TypeParameterDescriptor[] typeParameterDescriptors = signatureDescriptor.getTypeParameterDescriptors();
int length = typeParameterDescriptors.length;
if (length != 0) {
for (int i = 0; i < length; i++) {
TypeParameterDescriptor typeParameterDescriptor = typeParameterDescriptors[i];
this.addDelta(getElementType(element1), IDelta.REMOVED, IDelta.TYPE_PARAMETER, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
getDataFor(element1, type1),
typeParameterDescriptor.name });
}
} else {
String[] typeArguments = signatureDescriptor.getTypeArguments();
length = typeArguments.length;
if (length != 0) {
for (int i = 0; i < length; i++) {
String typeArgument = typeArguments[i];
this.addDelta(getElementType(element1), IDelta.REMOVED, IDelta.TYPE_ARGUMENT, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
getDataFor(element1, type1), typeArgument });
}
}
}
} else {
// both types have generic signature
// need to check delta for type parameter one by one
SignatureDescriptor signatureDescriptor = getSignatureDescriptor(signature1);
SignatureDescriptor signatureDescriptor2 = getSignatureDescriptor(signature2);
TypeParameterDescriptor[] typeParameterDescriptors1 = signatureDescriptor.getTypeParameterDescriptors();
TypeParameterDescriptor[] typeParameterDescriptors2 = signatureDescriptor2.getTypeParameterDescriptors();
int length = typeParameterDescriptors1.length;
int length2 = typeParameterDescriptors2.length;
int min = length;
int max = length2;
if (length > length2) {
min = length2;
max = length;
}
int i = 0;
for (; i < min; i++) {
TypeParameterDescriptor parameterDescriptor1 = typeParameterDescriptors1[i];
TypeParameterDescriptor parameterDescriptor2 = typeParameterDescriptors2[i];
String name = parameterDescriptor1.name;
if (!name.equals(parameterDescriptor2.name)) {
this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_PARAMETER_NAME, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
getDataFor(element1, type1), name });
}
if (parameterDescriptor1.classBound == null) {
if (parameterDescriptor2.classBound != null) {
// report delta added class bound of a type parameter
this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.ADDED, IDelta.CLASS_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
getDataFor(element1, type1), name });
}
} else if (parameterDescriptor2.classBound == null) {
// report delta removed class bound of a type parameter
this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.REMOVED, IDelta.CLASS_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
getDataFor(element1, type1), name });
} else if (!parameterDescriptor1.classBound.equals(parameterDescriptor2.classBound)) {
// report delta changed class bound of a type parameter
this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.CHANGED, IDelta.CLASS_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
getDataFor(element1, type1), name });
}
List<String> interfaceBounds1 = parameterDescriptor1.interfaceBounds;
List<String> interfaceBounds2 = parameterDescriptor2.interfaceBounds;
if (interfaceBounds1 == null) {
if (interfaceBounds2 != null) {
for (Iterator<String> iterator = interfaceBounds2.iterator(); iterator.hasNext();) {
// report delta added interface bounds
this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.ADDED, IDelta.INTERFACE_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
getDataFor(element1, type1), name,
iterator.next() });
}
}
} else if (interfaceBounds2 == null) {
// report delta removed interface bounds
for (Iterator<String> iterator = interfaceBounds1.iterator(); iterator.hasNext();) {
// report delta added interface bounds
this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.REMOVED, IDelta.INTERFACE_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
getDataFor(element1, type1), name,
iterator.next() });
}
} else {
int size1 = interfaceBounds1.size();
int size2 = interfaceBounds2.size();
int boundsMin = size1;
int boundsMax = size2;
if (size1 > size2) {
boundsMin = size2;
boundsMax = size1;
}
int index = 0;
for (; index < boundsMin; index++) {
String currentInterfaceBound = interfaceBounds1.get(index);
if (!currentInterfaceBound.equals(interfaceBounds2.get(index))) {
// report delta: different interface bounds (or
// reordered interface bound)
this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.CHANGED, IDelta.INTERFACE_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
getDataFor(element1, type1), name,
currentInterfaceBound });
}
}
if (boundsMin != boundsMax) {
// if max = length2 => addition of type parameter
// descriptor
// if max = length => removal of type parameter
// descriptor
boolean added = boundsMax == size2;
for (; index < boundsMax; index++) {
String currentInterfaceBound = added ? (String) interfaceBounds2.get(index) : (String) interfaceBounds1.get(index);
this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, added ? IDelta.ADDED : IDelta.REMOVED, IDelta.INTERFACE_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
getDataFor(element1, type1), name,
currentInterfaceBound });
}
}
}
}
if (min != max) {
// if max = length2 => addition of type parameter descriptor
// if max = length => removal of type parameter descriptor
boolean added = max == length2;
for (; i < max; i++) {
TypeParameterDescriptor currentTypeParameter = added ? typeParameterDescriptors2[i] : typeParameterDescriptors1[i];
int kind = added ? IDelta.ADDED : IDelta.REMOVED;
int flags = added && length == 0 ? IDelta.TYPE_PARAMETERS : IDelta.TYPE_PARAMETER;
this.addDelta(getElementType(element1), kind, flags, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
getDataFor(element1, type1),
currentTypeParameter.name });
}
}
if (length2 > 0 || length > 0) {
return;
}
String[] typeArguments = signatureDescriptor.getTypeArguments();
String[] typeArguments2 = signatureDescriptor2.getTypeArguments();
length = typeArguments.length;
length2 = typeArguments2.length;
min = length;
max = length2;
if (length > length2) {
min = length2;
max = length;
}
i = 0;
for (; i < min; i++) {
String currentTypeArgument = typeArguments[i];
String newTypeArgument = typeArguments2[i];
if (!currentTypeArgument.equals(newTypeArgument)) {
this.addDelta(getElementType(element1), IDelta.CHANGED, IDelta.TYPE_ARGUMENT, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
getDataFor(element1, type1), currentTypeArgument,
newTypeArgument });
}
}
if (min != max) {
// if max = length2 => addition of type arguments
// if max = length => removal of type arguments
boolean added = max == length2;
for (; i < max; i++) {
String currentTypeArgument = added ? typeArguments2[i] : typeArguments[i];
this.addDelta(getElementType(element1), added ? IDelta.ADDED : IDelta.REMOVED, IDelta.TYPE_ARGUMENT, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
getDataFor(element1, type1), currentTypeArgument });
}
}
}
}
/**
* Recursively collects all of the super-interfaces of the given type
* descriptor within the scope of the given API component
*
* @param type
* @param set
*/
private void collectAllInterfaces(IApiType type, Set<IApiType> set) {
try {
IApiType[] interfaces = type.getSuperInterfaces();
if (interfaces != null) {
for (int i = 0; i < interfaces.length; i++) {
IApiType anInterface = interfaces[i];
int visibility = VisibilityModifiers.PRIVATE;
IApiComponent ifaceComponent = anInterface.getApiComponent();
IApiDescription apiDescription = ifaceComponent.getApiDescription();
IApiAnnotations elementDescription = apiDescription.resolveAnnotations(anInterface.getHandle());
if (elementDescription != null) {
visibility = elementDescription.getVisibility();
}
if ((visibility & visibilityModifiers) != 0) {
set.add(anInterface);
}
collectAllInterfaces(anInterface, set);
}
}
String superclassName = type.getSuperclassName();
if (superclassName != null && !Util.isJavaLangObject(superclassName)) {
IApiType superclass = type.getSuperclass();
collectAllInterfaces(superclass, set);
}
} catch (CoreException e) {
reportStatus(e);
}
}
private String getDataFor(IApiMember member, IApiType type) {
switch (member.getType()) {
case IApiElement.TYPE:
if (((IApiType) member).isMemberType()) {
return member.getName().replace('$', '.');
}
return member.getName();
case IApiElement.METHOD:
StringBuffer buffer = new StringBuffer();
buffer.append(type.getName()).append('.').append(getMethodDisplayName((IApiMethod) member, type));
return String.valueOf(buffer);
case IApiElement.FIELD:
buffer = new StringBuffer();
buffer.append(type.getName()).append('.').append(member.getName());
return String.valueOf(buffer);
default:
break;
}
return null;
}
private String getKeyFor(IApiMember member, IApiType type) {
switch (member.getType()) {
case IApiElement.TYPE:
return member.getName();
case IApiElement.METHOD:
return getKeyForMethod((IApiMethod) member, type);
case IApiElement.FIELD:
return member.getName();
default:
break;
}
return null;
}
/**
* Returns a new {@link Delta} to use, and resets the status of creating a
* delta
*
* @return
*/
private Delta createDelta() {
return new Delta();
}
/**
* Returns the change(s) between the type descriptor and its equivalent in
* the current baseline.
*
* @return the changes in the type descriptor or <code>null</code>
*/
public IDelta getDelta(IProgressMonitor monitor) {
SubMonitor localmonitor = SubMonitor.convert(monitor, 10);
try {
this.delta = createDelta();
// check visibility
int typeAccess = this.type1.getModifiers();
int typeAccess2 = this.type2.getModifiers();
final IApiDescription component2ApiDescription = component2.getApiDescription();
IApiAnnotations elementDescription2 = component2ApiDescription.resolveAnnotations(this.type2.getHandle());
this.initialDescriptorRestrictions = RestrictionModifiers.NO_RESTRICTIONS;
this.currentDescriptorRestrictions = RestrictionModifiers.NO_RESTRICTIONS;
if (elementDescription2 != null) {
int restrictions2 = elementDescription2.getRestrictions();
IApiDescription apiDescription = this.component.getApiDescription();
if (this.component.hasApiDescription()) {
int restrictions = RestrictionModifiers.NO_RESTRICTIONS;
IApiAnnotations componentApiDescription = apiDescription.resolveAnnotations(this.type1.getHandle());
if (componentApiDescription != null) {
restrictions = componentApiDescription.getRestrictions();
this.initialDescriptorRestrictions = restrictions;
}
if (restrictions2 != restrictions) {
// report different restrictions
// adding/removing no extend on a final class is ok
// adding/removing no instantiate on an abstract class
// is ok
if (this.type1.isInterface()) {
if ((RestrictionModifiers.isImplementRestriction(restrictions2) && !RestrictionModifiers.isImplementRestriction(restrictions)) || (RestrictionModifiers.isExtendRestriction(restrictions2) && !RestrictionModifiers.isExtendRestriction(restrictions))) {
this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.RESTRICTIONS, restrictions2, typeAccess, typeAccess2, this.type2, this.type2.getName(), Util.getDescriptorName(type1));
}
} else {
boolean reportChangedRestrictions = false;
if (!Flags.isFinal(typeAccess2) && !Flags.isFinal(typeAccess)) {
if (RestrictionModifiers.isExtendRestriction(restrictions2) && !RestrictionModifiers.isExtendRestriction(restrictions)) {
reportChangedRestrictions = true;
this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.RESTRICTIONS, restrictions2, typeAccess, typeAccess2, this.type2, this.type2.getName(), Util.getDescriptorName(type1));
}
}
if (!reportChangedRestrictions && !Flags.isAbstract(typeAccess2) && !Flags.isAbstract(typeAccess)) {
if (RestrictionModifiers.isInstantiateRestriction(restrictions2) && !RestrictionModifiers.isInstantiateRestriction(restrictions)) {
this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.RESTRICTIONS, restrictions2, typeAccess, typeAccess2, this.type2, this.type2.getName(), Util.getDescriptorName(type1));
}
}
}
}
}
this.currentDescriptorRestrictions = restrictions2;
}
// first make sure that we compare interface with interface, class
// with class,
// annotation with annotation and enum with enums
if (Flags.isFinal(typeAccess2)) {
this.currentDescriptorRestrictions |= RestrictionModifiers.NO_EXTEND;
}
if (Flags.isFinal(typeAccess)) {
this.initialDescriptorRestrictions |= RestrictionModifiers.NO_EXTEND;
}
if (Flags.isDeprecated(typeAccess)) {
if (!Flags.isDeprecated(typeAccess2)) {
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.DEPRECATION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
} else if (Flags.isDeprecated(typeAccess2)) {
this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.DEPRECATION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
if (Flags.isProtected(typeAccess)) {
if (Flags.isPrivate(typeAccess2) || Util.isDefault(typeAccess2)) {
// report delta - decrease access: protected to default or
// private
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.DECREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
return this.delta;
} else if (Flags.isPublic(typeAccess2)) {
// report delta - increase access: protected to public
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.INCREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
return this.delta;
}
} else if (Flags.isPublic(typeAccess) && (Flags.isProtected(typeAccess2) || Flags.isPrivate(typeAccess2) || Util.isDefault(typeAccess2))) {
// report delta - decrease access: public to protected, default
// or private
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.DECREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
return this.delta;
} else if (Util.isDefault(typeAccess) && (Flags.isPublic(typeAccess2) || Flags.isProtected(typeAccess2))) {
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.INCREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
return this.delta;
} else if (Flags.isPrivate(typeAccess) && (Util.isDefault(typeAccess2) || Flags.isPublic(typeAccess2) || Flags.isProtected(typeAccess2))) {
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.INCREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
return this.delta;
}
if (Flags.isAnnotation(typeAccess)) {
if (!Flags.isAnnotation(typeAccess2)) {
if (Flags.isInterface(typeAccess2)) {
// report conversion from annotation to interface
this.addDelta(IDelta.ANNOTATION_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE),
Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE) });
} else if (Flags.isEnum(typeAccess2)) {
// report conversion from annotation to enum
this.addDelta(IDelta.ANNOTATION_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE),
Integer.toString(IDelta.ENUM_ELEMENT_TYPE) });
} else {
// report conversion from annotation to class
this.addDelta(IDelta.ANNOTATION_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE),
Integer.toString(IDelta.CLASS_ELEMENT_TYPE) });
}
return this.delta;
}
} else if (Flags.isInterface(typeAccess)) {
if (Flags.isAnnotation(typeAccess2)) {
// conversion from interface to annotation
this.addDelta(IDelta.INTERFACE_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE),
Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE) });
return this.delta;
} else if (!Flags.isInterface(typeAccess2)) {
if (Flags.isEnum(typeAccess2)) {
// conversion from interface to enum
this.addDelta(IDelta.INTERFACE_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE),
Integer.toString(IDelta.ENUM_ELEMENT_TYPE) });
} else {
// conversion from interface to class
this.addDelta(IDelta.INTERFACE_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE),
Integer.toString(IDelta.CLASS_ELEMENT_TYPE) });
}
return this.delta;
}
} else if (Flags.isEnum(typeAccess)) {
if (!Flags.isEnum(typeAccess2)) {
if (Flags.isAnnotation(typeAccess2)) {
// report conversion from enum to annotation
this.addDelta(IDelta.ENUM_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.ENUM_ELEMENT_TYPE),
Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE) });
} else if (Flags.isInterface(typeAccess2)) {
// report conversion from enum to interface
this.addDelta(IDelta.ENUM_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.ENUM_ELEMENT_TYPE),
Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE) });
} else {
// report conversion from enum to class
this.addDelta(IDelta.ENUM_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.ENUM_ELEMENT_TYPE),
Integer.toString(IDelta.CLASS_ELEMENT_TYPE) });
}
return this.delta;
}
} else if (!Util.isClass(typeAccess2)) {
if (Flags.isAnnotation(typeAccess2)) {
// report conversion from class to annotation
this.addDelta(IDelta.CLASS_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.CLASS_ELEMENT_TYPE),
Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE) });
} else if (Flags.isInterface(typeAccess2)) {
// report conversion from class to interface
this.addDelta(IDelta.CLASS_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.CLASS_ELEMENT_TYPE),
Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE) });
} else {
// report conversion from class to enum
this.addDelta(IDelta.CLASS_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
Util.getDescriptorName(type1),
Integer.toString(IDelta.CLASS_ELEMENT_TYPE),
Integer.toString(IDelta.ENUM_ELEMENT_TYPE) });
}
return this.delta;
}
if (Flags.isStatic(typeAccess)) {
if (!Flags.isStatic(typeAccess2)) {
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.STATIC_TO_NON_STATIC, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
} else if (Flags.isStatic(typeAccess2)) {
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.NON_STATIC_TO_STATIC, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
// check super class set
checkSuperclass();
// check super interfaces set
checkSuperInterfaces();
// checks fields
IApiField[] fields1 = this.type1.getFields();
IApiField[] fields2 = this.type2.getFields();
Set<String> addedFields = new HashSet<>(fields2.length);
for (int i = 0; i < fields2.length; i++) {
addedFields.add(fields2[i].getName());
}
for (int i = 0; i < fields1.length; i++) {
addedFields.remove(fields1[i].getName());
getDeltaForField(fields1[i]);
}
// checks remaining fields (added fields)
for (Iterator<String> iterator = addedFields.iterator(); iterator.hasNext();) {
IApiField field = this.type2.getField(iterator.next());
reportFieldAddition(field, this.type2);
}
// checks methods
IApiMethod[] methods1 = this.type1.getMethods();
IApiMethod[] methods2 = this.type2.getMethods();
Set<IMemberDescriptor> addedMethods = new HashSet<>(methods2.length);
for (int i = 0; i < methods2.length; i++) {
if (!methods2[i].isSynthetic()) {
addedMethods.add(methods2[i].getHandle());
}
}
for (int i = 0; i < methods1.length; i++) {
addedMethods.remove(methods1[i].getHandle());
getDeltaForMethod(methods1[i]);
}
// checks remaining methods (added methods)
for (Iterator<IMemberDescriptor> iterator = addedMethods.iterator(); iterator.hasNext();) {
IMethodDescriptor md = (IMethodDescriptor) iterator.next();
IApiMethod method = this.type2.getMethod(md.getName(), md.getSignature());
reportMethodAddition(method, this.type2);
}
if (Flags.isAbstract(typeAccess)) {
if (!Flags.isAbstract(typeAccess2)) {
// report delta - changed from abstract to non-abstract
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.ABSTRACT_TO_NON_ABSTRACT, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
} else if (Flags.isAbstract(typeAccess2)) {
// report delta - changed from non-abstract to abstract
if (!RestrictionModifiers.isInstantiateRestriction(initialDescriptorRestrictions)) {
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.NON_ABSTRACT_TO_ABSTRACT, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
}
if (Flags.isFinal(typeAccess)) {
if (!Flags.isFinal(typeAccess2)) {
// report delta - changed from final to non-final
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
} else if (Flags.isFinal(typeAccess2)) {
// report delta - changed from non-final to final
this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.NON_FINAL_TO_FINAL, this.initialDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
// check type parameters
String signature1 = this.type1.getGenericSignature();
String signature2 = this.type2.getGenericSignature();
checkGenericSignature(signature1, signature2, this.type1, this.type2);
// check type members
checkTypeMembers();
return this.delta.isEmpty() ? ApiComparator.NO_DELTA : this.delta;
} catch (CoreException e) {
reportStatus(e);
return null;
} finally {
localmonitor.done();
}
}
private void getDeltaForField(IApiField field) {
int access = field.getModifiers();
if (Flags.isSynthetic(access)) {
// we ignore synthetic fields
return;
}
String name = field.getName();
IApiField field2 = this.type2.getField(name);
if (field2 == null) {
if (Flags.isPrivate(access) || Util.isDefault(access)) {
if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
// report non-API delta:
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.FIELD, this.currentDescriptorRestrictions, access, 0, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
} else {
boolean found = false;
if (this.component2 != null) {
if (this.type1.isInterface()) {
Set<?> interfacesSet = getInterfacesSet(this.type2);
if (interfacesSet != null) {
for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = (IApiType) iterator.next();
IApiField field3 = superTypeDescriptor.getField(name);
if (field3 == null) {
continue;
} else {
// interface method can only be public
// method has been move up in the hierarchy
// - report the delta and abort loop
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.FIELD_MOVED_UP, this.currentDescriptorRestrictions, access, field3.getModifiers(), this.type1, name, new String[] {
Util.getDescriptorName(this.type1),
name });
found = true;
break;
}
}
}
} else {
List<IApiType> superclassList = getSuperclassList(this.type2);
if (superclassList != null && isStatusOk()) {
loop: for (Iterator<IApiType> iterator = superclassList.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = iterator.next();
IApiField field3 = superTypeDescriptor.getField(name);
if (field3 == null) {
continue;
} else {
int access3 = field3.getModifiers();
if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
// method has been move up in the
// hierarchy - report the delta and
// abort loop
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.FIELD_MOVED_UP, this.currentDescriptorRestrictions, access, field3.getModifiers(), this.type1, name, new String[] {
Util.getDescriptorName(this.type1),
name });
found = true;
break loop;
}
}
}
}
}
}
if (!found) {
if ((this.visibilityModifiers == VisibilityModifiers.API) && component.hasApiDescription()) {
// check if this field should be removed because it is
// tagged as @noreference
IApiDescription apiDescription = null;
try {
apiDescription = this.component.getApiDescription();
} catch (CoreException e) {
reportStatus(e);
}
if (apiDescription != null) {
IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(field.getHandle());
if (apiAnnotations != null) {
int restrictions = apiAnnotations.getRestrictions();
if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
// if not found, but tagged as @noreference
// in reference we don't need to report
// a removed field
return;
}
}
}
}
if (field.isEnumConstant()) {
// report delta (removal of an enum constant - not
// compatible)
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.ENUM_CONSTANT, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
return;
}
// removing a public field is a breakage
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.FIELD, this.currentDescriptorRestrictions, access, 0, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
}
return;
}
int restrictions = RestrictionModifiers.NO_RESTRICTIONS;
int referenceRestrictions = RestrictionModifiers.NO_RESTRICTIONS;
int access2 = field2.getModifiers();
if (this.component2.hasApiDescription()) {
try {
IApiDescription apiDescription = this.component2.getApiDescription();
IApiAnnotations resolvedAPIDescription = apiDescription.resolveAnnotations(field2.getHandle());
if (resolvedAPIDescription != null) {
restrictions = resolvedAPIDescription.getRestrictions();
}
} catch (CoreException e) {
// ignore
}
}
if ((this.visibilityModifiers == VisibilityModifiers.API) && component.hasApiDescription()) {
// check if this field should be removed because it is tagged as
// @noreference
IApiDescription apiDescription = null;
try {
apiDescription = this.component.getApiDescription();
} catch (CoreException e) {
reportStatus(e);
}
if (apiDescription != null) {
IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(field.getHandle());
if (apiAnnotations != null) {
referenceRestrictions = apiAnnotations.getRestrictions();
}
}
if (RestrictionModifiers.isReferenceRestriction(referenceRestrictions)) {
// tagged as @noreference in the reference component
if (!RestrictionModifiers.isReferenceRestriction(restrictions)) {
// no longer tagged as @noreference
// report a field addition
if (field2.isEnumConstant()) {
// report delta (addition of an enum constant -
// compatible
this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.ENUM_CONSTANT, this.currentDescriptorRestrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type2), name });
} else {
this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.FIELD, this.currentDescriptorRestrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type2), name });
}
return;
}
} else if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
if (((Flags.isPublic(access2) || Flags.isProtected(access2)) && (Flags.isPublic(access) || Flags.isProtected(access))) && (this.visibilityModifiers == VisibilityModifiers.API)) {
// report that it is no longer an API field
this.addDelta(getElementType(this.type2), IDelta.REMOVED, field2.isEnumConstant() ? IDelta.API_ENUM_CONSTANT : IDelta.API_FIELD, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type2), name });
}
return;
}
if ((Flags.isPrivate(access) || Util.isDefault(access) || (Flags.isProtected(access) && RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions)))
&& (Flags.isPrivate(access2) || Util.isDefault(access2) || (Flags.isProtected(access2) && RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)))) {
// don't report non-API deltas
return;
}
}
restrictions |= this.currentDescriptorRestrictions;
if (!field.getSignature().equals(field2.getSignature())) {
// report delta
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
} else {
// check type parameters
String signature1 = field.getGenericSignature();
String signature2 = field2.getGenericSignature();
checkGenericSignature(signature1, signature2, field, field2);
}
boolean changeFinalToNonFinal = false;
if (Flags.isProtected(access)) {
if (Flags.isPrivate(access2) || Util.isDefault(access2)) {
// report delta - decrease access: protected to default or
// private
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
} else if (Flags.isPublic(access2)) {
// report delta - increase access: protected to public
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
} else if (Flags.isPublic(access) && (Flags.isProtected(access2) || Flags.isPrivate(access2) || Util.isDefault(access2))) {
// report delta - decrease access: public to protected, default or
// private
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
} else if (Flags.isPrivate(access) && (Flags.isProtected(access2) || Util.isDefault(access2) || Flags.isPublic(access2))) {
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
} else if (Util.isDefault(access) && (Flags.isProtected(access2) || Flags.isPublic(access2))) {
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
if (Flags.isFinal(access)) {
if (!Flags.isFinal(access2)) {
if (!Flags.isStatic(access2)) {
// report delta - final to non-final for a non static field
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL_NON_STATIC, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
} else if (field.getConstantValue() != null) {
// report delta - final to non-final for a static field with
// a compile time constant
changeFinalToNonFinal = true;
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL_STATIC_CONSTANT, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
} else {
// report delta - final to non-final for a static field with
// no compile time constant
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL_STATIC_NON_CONSTANT, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
}
} else if (Flags.isFinal(access2)) {
// report delta - non-final to final
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.NON_FINAL_TO_FINAL, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
if (Flags.isStatic(access)) {
if (!Flags.isStatic(access2)) {
// report delta - static to non-static
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.STATIC_TO_NON_STATIC, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
} else if (Flags.isStatic(access2)) {
// report delta - non-static to static
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.NON_STATIC_TO_STATIC, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
if (Flags.isTransient(access)) {
if (!Flags.isTransient(access2)) {
// report delta - transient to non-transient
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TRANSIENT_TO_NON_TRANSIENT, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
} else if (Flags.isTransient(access2)) {
// report delta - non-transient to transient
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.NON_TRANSIENT_TO_TRANSIENT, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
if (Flags.isVolatile(access)) {
if (!Flags.isVolatile(access2)) {
// report delta - volatile to non-volatile
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.VOLATILE_TO_NON_VOLATILE, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
} else if (Flags.isVolatile(access2)) {
// report delta - non-volatile to volatile
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.NON_VOLATILE_TO_VOLATILE, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
if (Flags.isDeprecated(access)) {
if (!Flags.isDeprecated(access2)) {
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.REMOVED, IDelta.DEPRECATION, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
} else if (Flags.isDeprecated(access2)) {
// report delta - non-volatile to volatile
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.ADDED, IDelta.DEPRECATION, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name });
}
if (field.getConstantValue() != null) {
if (field2.getConstantValue() == null) {
if (!changeFinalToNonFinal) {
// report delta - removal of constant value
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.REMOVED, IDelta.VALUE, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name,
String.valueOf(field.getConstantValue()) });
}
} else if (!field.getConstantValue().equals(field2.getConstantValue())) {
// report delta - modified constant value
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.VALUE, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name,
String.valueOf(field.getConstantValue()) });
}
} else if (field2.getConstantValue() != null) {
// report delta
this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.ADDED, IDelta.VALUE, restrictions, access, access2, this.type1, name, new String[] {
Util.getDescriptorName(this.type1), name,
String.valueOf(field2.getConstantValue()) });
}
}
private void getDeltaForMethod(IApiMethod method) {
int access = method.getModifiers();
if (Flags.isSynthetic(access)) {
// we ignore synthetic methods
return;
}
String name = method.getName();
String descriptor = method.getSignature();
String key = getKeyForMethod(method, this.type1);
IApiMethod method2 = this.type2.getMethod(name, descriptor);
String methodDisplayName = getMethodDisplayName(method, this.type1);
if (method2 == null) {
if (method.isClassInitializer()) {
if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
// report non-API delta: removal of a clinit method
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.CLINIT, this.currentDescriptorRestrictions, access, 0, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
}
return;
} else if (Flags.isPrivate(access) || Util.isDefault(access)) {
if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
// report non-API delta:
this.addDelta(getElementType(this.type1), IDelta.REMOVED, getTargetType(method), Flags.isAbstract(this.type2.getModifiers()) ? this.currentDescriptorRestrictions | RestrictionModifiers.NO_INSTANTIATE : this.currentDescriptorRestrictions, access, 0, this.type1, getKeyForMethod(method, this.type1), new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
return;
}
// if null we need to walk the hierarchy of descriptor2
boolean found = false;
if (this.component2 != null && !method.isConstructor()) {
if (this.type1.isInterface()) {
Set<?> interfacesSet = getInterfacesSet(this.type2);
if (interfacesSet != null && isStatusOk()) {
for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = (IApiType) iterator.next();
IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
if (method3 == null) {
continue;
} else {
// interface method can only be public
// method has been move up in the hierarchy -
// report the delta and abort loop
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.METHOD_MOVED_UP, this.currentDescriptorRestrictions, access, method3.getModifiers(), this.type1, getKeyForMethod(method3, this.type1), new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName });
found = true;
break;
}
}
}
} else {
List<?> superclassList = getSuperclassList(this.type2, true);
if (superclassList != null && isStatusOk()) {
loop: for (Iterator<?> iterator = superclassList.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = (IApiType) iterator.next();
IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
if (method3 == null) {
continue;
} else {
int access3 = method3.getModifiers();
if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
// method has been move up in the hierarchy
// - report the delta and abort loop
// TODO need to make the distinction between
// methods that need to be re-implemented
// and methods that don't
this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.METHOD_MOVED_UP, this.currentDescriptorRestrictions, access, access3, this.type1, getKeyForMethod(method3, this.type1), new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName });
found = true;
break loop;
}
}
}
}
}
}
if (!found) {
if ((this.visibilityModifiers == VisibilityModifiers.API) && component.hasApiDescription()) {
// check if this method should be removed because it is
// tagged as @noreference
IApiDescription apiDescription = null;
try {
apiDescription = this.component.getApiDescription();
} catch (CoreException e) {
reportStatus(e);
}
if (apiDescription != null) {
IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(method.getHandle());
if (apiAnnotations != null) {
int restrictions = apiAnnotations.getRestrictions();
if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
// if not found, but tagged as @noreference in
// reference we don't need to report
// a removed method
return;
}
}
}
}
if (this.type1.isAnnotation()) {
this.addDelta(getElementType(this.type1), IDelta.REMOVED, method.getDefaultValue() != null ? IDelta.METHOD_WITH_DEFAULT_VALUE : IDelta.METHOD_WITHOUT_DEFAULT_VALUE, this.currentDescriptorRestrictions, access, 0, this.type1, getKeyForMethod(method, this.type1), new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName });
} else {
int restrictions = this.currentDescriptorRestrictions;
if (RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions) && !RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)) {
restrictions = this.initialDescriptorRestrictions;
}
this.addDelta(getElementType(this.type1), IDelta.REMOVED, getTargetType(method), Flags.isAbstract(this.type2.getModifiers()) ? restrictions | RestrictionModifiers.NO_INSTANTIATE : restrictions, access, 0, this.type1, getKeyForMethod(method, this.type1), new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName });
}
}
return;
}
int restrictions = this.currentDescriptorRestrictions;
if (component2.hasApiDescription()) {
try {
IApiDescription apiDescription = this.component2.getApiDescription();
IApiAnnotations resolvedAPIDescription = apiDescription.resolveAnnotations(method2.getHandle());
if (resolvedAPIDescription != null) {
restrictions |= resolvedAPIDescription.getRestrictions();
}
} catch (CoreException e) {
// ignore
}
}
int referenceRestrictions = this.initialDescriptorRestrictions;
int access2 = method2.getModifiers();
if (this.component.hasApiDescription()) {
// check if this method should be removed because it is tagged as
// @noreference
IApiDescription apiDescription = null;
try {
apiDescription = this.component.getApiDescription();
} catch (CoreException e) {
reportStatus(e);
}
if (apiDescription != null) {
IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(method.getHandle());
if (apiAnnotations != null) {
referenceRestrictions |= apiAnnotations.getRestrictions();
}
}
}
if ((this.visibilityModifiers == VisibilityModifiers.API) && this.component.hasApiDescription()) {
if (RestrictionModifiers.isReferenceRestriction(referenceRestrictions)) {
// tagged as @noreference in the reference component
if (!RestrictionModifiers.isReferenceRestriction(restrictions)) {
// no longer tagged as @noreference
// report a method addition
if (method.isConstructor()) {
this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.CONSTRUCTOR, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method, this.type2), new String[] {
Util.getDescriptorName(this.type2),
methodDisplayName });
} else if (this.type2.isAnnotation()) {
if (method.getDefaultValue() != null) {
this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.METHOD_WITH_DEFAULT_VALUE, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method, this.type2), new String[] {
Util.getDescriptorName(this.type2),
methodDisplayName });
} else {
this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.METHOD_WITHOUT_DEFAULT_VALUE, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method, this.type2), new String[] {
Util.getDescriptorName(this.type2),
methodDisplayName });
}
} else {
// check superclass
// if null we need to walk the hierarchy of descriptor2
boolean found = false;
if (this.component2 != null) {
if (this.type1.isInterface()) {
Set<?> interfacesSet = getInterfacesSet(this.type2);
if (interfacesSet != null && isStatusOk()) {
for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = (IApiType) iterator.next();
IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
if (method3 == null) {
continue;
} else {
// interface method can only be
// public
// method has been move up in the
// hierarchy - report the delta and
// abort loop
found = true;
break;
}
}
}
} else {
List<IApiType> superclassList = getSuperclassList(this.type2, true);
if (superclassList != null) {
loop: for (Iterator<IApiType> iterator = superclassList.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = iterator.next();
IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
if (method3 == null) {
continue;
} else {
int access3 = method3.getModifiers();
if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
// method has been move up in
// the hierarchy - report the
// delta and abort loop
// TODO need to make the
// distinction between methods
// that need to be
// re-implemented and methods
// that don't
found = true;
break loop;
}
}
}
}
}
}
this.addDelta(getElementType(this.type2), IDelta.ADDED, found ? IDelta.OVERRIDEN_METHOD : IDelta.METHOD, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method, this.type2), new String[] {
Util.getDescriptorName(this.type2),
methodDisplayName });
}
return;
}
} else if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
if (Flags.isPublic(access2) || Flags.isProtected(access2)) {
// report that it is no longer an API method
if (this.type2.isAnnotation()) {
this.addDelta(getElementType(this.type2), IDelta.REMOVED, method.getDefaultValue() != null ? IDelta.API_METHOD_WITH_DEFAULT_VALUE : IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method2, this.type2), new String[] {
Util.getDescriptorName(this.type2),
methodDisplayName });
} else if (Flags.isPublic(access) || Flags.isProtected(access)) {
this.addDelta(getElementType(this.type2), IDelta.REMOVED, method.isConstructor() ? IDelta.API_CONSTRUCTOR : IDelta.API_METHOD, Flags.isAbstract(this.type2.getModifiers()) ? this.currentDescriptorRestrictions | RestrictionModifiers.NO_INSTANTIATE : this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method2, this.type2), new String[] {
Util.getDescriptorName(this.type2),
methodDisplayName });
}
return;
}
}
if ((Flags.isPrivate(access) || Util.isDefault(access) || (Flags.isProtected(access) && RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions)))
&& (Flags.isPrivate(access2) || Util.isDefault(access2) || (Flags.isProtected(access2) && RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)))) {
// don't report non-API deltas
return;
}
}
if (this.component.hasApiDescription() && !method.isConstructor() && !method.isClassInitializer() && !(type1.isInterface() || type1.isAnnotation())) {
if (restrictions != referenceRestrictions) {
if (!Flags.isFinal(access2)) {
if (RestrictionModifiers.isOverrideRestriction(restrictions) && !RestrictionModifiers.isOverrideRestriction(referenceRestrictions)) {
this.addDelta(getElementType(method), IDelta.ADDED, IDelta.RESTRICTIONS, restrictions, referenceRestrictions, access, access2, this.type1, getKeyForMethod(method2, this.type2), new String[] {
Util.getDescriptorName(this.type2),
methodDisplayName });
}
}
}
}
String[] names1 = method.getExceptionNames();
List<String> list1 = null;
if (names1 != null) {
list1 = new ArrayList<>(names1.length);
for (int i = 0; i < names1.length; i++) {
list1.add(names1[i]);
}
}
String[] names2 = method2.getExceptionNames();
List<String> list2 = null;
if (names2 != null) {
list2 = new ArrayList<>(names2.length);
for (int i = 0; i < names2.length; i++) {
list2.add(names2[i]);
}
}
if (names1 != null) {
if (names2 == null) {
// check all exception in method descriptor to see if they are
// checked or unchecked exceptions
loop: for (Iterator<String> iterator = list1.iterator(); iterator.hasNext();) {
String exceptionName = iterator.next().replace('/', '.');
if (isCheckedException(this.apiBaseline1, this.component, exceptionName)) {
// report delta - removal of checked exception
// TODO should we continue the loop for all remaining
// exceptions
this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.CHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName, exceptionName });
break loop;
} else {
// report delta - removal of unchecked exception
this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.UNCHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName, exceptionName });
}
}
} else {
// check if the exceptions are consistent for both descriptors
List<String> removedExceptions = new ArrayList<>();
for (Iterator<String> iterator = list1.iterator(); iterator.hasNext();) {
String exceptionName = iterator.next().replace('/', '.');
if (!list2.remove(exceptionName)) {
// this means that the exceptionName was not found
// inside the new set of exceptions
// so it has been removed
removedExceptions.add(exceptionName);
}
}
if (removedExceptions.size() != 0) {
loop: for (Iterator<String> iterator = removedExceptions.iterator(); iterator.hasNext();) {
String exceptionName = iterator.next().replace('/', '.');
if (isCheckedException(this.apiBaseline1, this.component, exceptionName)) {
// report delta - removal of checked exception
// TODO should we continue the loop for all
// remaining exceptions
this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.CHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName, exceptionName });
break loop;
} else {
// report delta - removal of unchecked exception
this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.UNCHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName, exceptionName });
}
}
}
loop: for (Iterator<String> iterator = list2.iterator(); iterator.hasNext();) {
String exceptionName = iterator.next().replace('/', '.');
if (isCheckedException(this.apiBaseline2, this.component2, exceptionName)) {
// report delta - addition of checked exception
// TODO should we continue the loop for all remaining
// exceptions
this.addDelta(getElementType(method), IDelta.ADDED, IDelta.CHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName, exceptionName });
break loop;
} else {
// report delta - addition of unchecked exception
this.addDelta(getElementType(method), IDelta.ADDED, IDelta.UNCHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName, exceptionName });
}
}
}
} else if (names2 != null) {
// check all exception in method descriptor to see if they are
// checked or unchecked exceptions
loop: for (Iterator<String> iterator = list2.iterator(); iterator.hasNext();) {
String exceptionName = iterator.next().replace('/', '.');
if (isCheckedException(this.apiBaseline2, this.component2, exceptionName)) {
// report delta - addition of checked exception
this.addDelta(getElementType(method), IDelta.ADDED, IDelta.CHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName, exceptionName });
// TODO should we continue the loop for all remaining
// exceptions
break loop;
} else {
// report delta - addition of unchecked exception
this.addDelta(getElementType(method), IDelta.ADDED, IDelta.UNCHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1),
methodDisplayName, exceptionName });
}
}
}
if (Flags.isVarargs(access)) {
if (!Flags.isVarargs(access2)) {
// report delta: conversion from T... to T[] - break
// compatibility
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.VARARGS_TO_ARRAY, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
} else if (Flags.isVarargs(access2)) {
// report delta: conversion from T[] to T... compatible
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.ARRAY_TO_VARARGS, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
if (Flags.isProtected(access)) {
if (Flags.isPrivate(access2) || Util.isDefault(access2)) {
// report delta - decrease access: protected to default or
// private
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
} else if (Flags.isPublic(access2)) {
// report delta - increase access: protected to public
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
} else if (Flags.isPublic(access) && (Flags.isProtected(access2) || Flags.isPrivate(access2) || Util.isDefault(access2))) {
// report delta - decrease access: public to protected, default or
// private
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
} else if (Util.isDefault(access) && (Flags.isPublic(access2) || Flags.isProtected(access2))) {
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
} else if (Flags.isPrivate(access) && (Util.isDefault(access2) || Flags.isPublic(access2) || Flags.isProtected(access2))) {
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
if (Flags.isAbstract(access)) {
if (!Flags.isAbstract(access2)) {
// report delta - changed from abstract to non-abstract
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.ABSTRACT_TO_NON_ABSTRACT, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
} else if (Flags.isAbstract(access2)) {
// report delta - changed from non-abstract to abstract
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NON_ABSTRACT_TO_ABSTRACT, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
if (Flags.isFinal(access)) {
if (!Flags.isFinal(access2)) {
// report delta - changed from final to non-final
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
} else if (Flags.isFinal(access2)) {
int res = restrictions;
if (!RestrictionModifiers.isOverrideRestriction(res)) {
if (RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)) {
res = this.currentDescriptorRestrictions;
} else if (RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions)) {
res = this.initialDescriptorRestrictions;
}
if (RestrictionModifiers.isOverrideRestriction(referenceRestrictions)) {
// it is ok to remove @nooverride and add final at the same
// time
res = referenceRestrictions;
}
}
// only report this delta is the method was visible
this.addDelta(getElementType(method2), IDelta.CHANGED, IDelta.NON_FINAL_TO_FINAL, res, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type2),
getMethodDisplayName(method2, this.type2) });
}
if (Flags.isStatic(access)) {
if (!Flags.isStatic(access2)) {
// report delta: change from static to non-static
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.STATIC_TO_NON_STATIC, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
} else if (Flags.isStatic(access2)) {
// report delta: change from non-static to static
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NON_STATIC_TO_STATIC, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
if (Flags.isNative(access)) {
if (!Flags.isNative(access2)) {
// report delta: change from native to non-native
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NATIVE_TO_NON_NATIVE, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
} else if (Flags.isNative(access2)) {
// report delta: change from non-native to native
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NON_NATIVE_TO_NATIVE, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
if (Flags.isSynchronized(access)) {
if (!Flags.isSynchronized(access2)) {
// report delta: change from synchronized to non-synchronized
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.SYNCHRONIZED_TO_NON_SYNCHRONIZED, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
} else if (Flags.isSynchronized(access2)) {
// report delta: change from non-synchronized to synchronized
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NON_SYNCHRONIZED_TO_SYNCHRONIZED, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
if (Flags.isDeprecated(access)) {
if (!Flags.isDeprecated(access2)) {
this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.DEPRECATION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
} else if (Flags.isDeprecated(access2)) {
// report delta - non-volatile to volatile
this.addDelta(getElementType(method), IDelta.ADDED, IDelta.DEPRECATION, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
// check type parameters
String signature1 = method.getGenericSignature();
String signature2 = method2.getGenericSignature();
checkGenericSignature(signature1, signature2, method, method2);
if (method.getDefaultValue() == null) {
if (method2.getDefaultValue() != null) {
// report delta : default value has been added - compatible
this.addDelta(getElementType(method), IDelta.ADDED, IDelta.ANNOTATION_DEFAULT_VALUE, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
} else if (method2.getDefaultValue() == null) {
// report delta : default value has been removed - incompatible
this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.ANNOTATION_DEFAULT_VALUE, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
} else if (!method.getDefaultValue().equals(method2.getDefaultValue())) {
// report delta: default value has changed
this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.ANNOTATION_DEFAULT_VALUE, restrictions, access, access2, this.type1, key, new String[] {
Util.getDescriptorName(this.type1), methodDisplayName });
}
}
/**
* Returns the complete super-interface set for the given type descriptor or
* null, if it could not be computed
*
* @param type
* @return the complete super-interface set for the given descriptor, or
* <code>null</code>
*/
private Set<IApiType> getInterfacesSet(IApiType type) {
HashSet<IApiType> set = new HashSet<>();
this.status = null;
collectAllInterfaces(type, set);
if (set.isEmpty()) {
return null;
}
return set;
}
private String getMethodDisplayName(IApiMethod method, IApiType type) {
String methodName = null;
if (method.isConstructor()) {
methodName = type.getSimpleName();
} else {
methodName = method.getName();
}
String signature = null;
String genericSignature = method.getGenericSignature();
if (genericSignature != null) {
signature = genericSignature;
} else {
signature = method.getSignature();
}
return Signature.toString(signature, methodName, null, false, false);
}
private SignatureDescriptor getSignatureDescriptor(String signature) {
SignatureDescriptor signatureDescriptor = new SignatureDescriptor();
SignatureReader signatureReader = new SignatureReader(signature);
signatureReader.accept(new SignatureDecoder(signatureDescriptor));
return signatureDescriptor;
}
private List<IApiType> getSuperclassList(IApiType type) {
return getSuperclassList(type, false);
}
private List<IApiType> getSuperclassList(IApiType type, boolean includeObject) {
return getSuperclassList(type, includeObject, false);
}
private List<IApiType> getSuperclassList(IApiType type, boolean includeObject, boolean includePrivate) {
IApiType superClass = type;
this.status = null;
String superName = superClass.getSuperclassName();
if (Util.isJavaLangObject(superName) && !includeObject) {
return null;
}
List<IApiType> list = new ArrayList<>();
try {
while (superName != null && (!Util.isJavaLangObject(superName) || includeObject)) {
superClass = superClass.getSuperclass();
int visibility = VisibilityModifiers.PRIVATE;
IApiComponent superComponent = superClass.getApiComponent();
IApiDescription apiDescription = superComponent.getApiDescription();
IApiAnnotations elementDescription = apiDescription.resolveAnnotations(superClass.getHandle());
if (elementDescription != null) {
visibility = elementDescription.getVisibility();
}
if (includePrivate || ((visibility & visibilityModifiers) != 0)) {
list.add(superClass);
}
superName = superClass.getSuperclassName();
}
} catch (CoreException e) {
reportStatus(e);
}
if (list.isEmpty()) {
return null;
}
return list;
}
private void reportFieldAddition(IApiField field, IApiType type) {
int access = field.getModifiers();
String name = field.getName();
if (Flags.isSynthetic(access)) {
// we ignore synthetic fields
return;
}
if ((this.visibilityModifiers == VisibilityModifiers.API) && component2.hasApiDescription()) {
// check if this method should be removed because it is tagged as
// @noreference
IApiDescription apiDescription = null;
try {
apiDescription = this.component2.getApiDescription();
} catch (CoreException e) {
reportStatus(e);
}
if (apiDescription != null) {
IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(field.getHandle());
if (apiAnnotations != null) {
int restrictions = apiAnnotations.getRestrictions();
if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
// such a method is not seen as an API method
return;
}
}
}
}
if (field.isEnumConstant()) {
// report delta (addition of an enum constant - compatible
this.addDelta(getElementType(type), IDelta.ADDED, IDelta.ENUM_CONSTANT, this.currentDescriptorRestrictions, this.initialDescriptorRestrictions, 0, access, this.type1, name, new String[] {
Util.getDescriptorName(type), name });
} else {
if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription()) || Flags.isPublic(access) || Flags.isProtected(access)) {
// report non-API delta:
this.addDelta(getElementType(type), IDelta.ADDED, IDelta.FIELD, this.currentDescriptorRestrictions, this.initialDescriptorRestrictions, 0, access, this.type1, name, new String[] {
Util.getDescriptorName(type), name });
}
}
}
private void reportMethodAddition(IApiMethod method, IApiType type) {
int access = method.getModifiers();
if (method.isClassInitializer()) {
if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
// report non-API delta: addition of clinit method
this.addDelta(getElementType(type), IDelta.ADDED, IDelta.CLINIT, this.currentDescriptorRestrictions, 0, access, this.type1, type.getName(), Util.getDescriptorName(type1));
}
return;
}
if (Flags.isSynthetic(access)) {
// we ignore synthetic method
return;
}
IApiDescription apiDescription = null;
if (((this.visibilityModifiers & VisibilityModifiers.API) != 0) && component2.hasApiDescription()) {
// check if this method should be removed because it is tagged as
// @noreference
int restrictions = RestrictionModifiers.NO_RESTRICTIONS;
try {
apiDescription = this.component2.getApiDescription();
} catch (CoreException e) {
reportStatus(e);
}
if (apiDescription != null) {
IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(method.getHandle());
if (apiAnnotations != null) {
restrictions = apiAnnotations.getRestrictions();
}
}
// check if this method should be removed because it is tagged as
// @noreference
if (this.visibilityModifiers == VisibilityModifiers.API && RestrictionModifiers.isReferenceRestriction(restrictions)) {
// such a method is not seen as an API method
return;
}
}
String methodDisplayName = getMethodDisplayName(method, type);
int restrictionsForMethodAddition = this.currentDescriptorRestrictions;
if (Flags.isFinal(this.type2.getModifiers())) {
restrictionsForMethodAddition |= RestrictionModifiers.NO_EXTEND;
}
if (apiDescription != null) {
if (this.type2.isMemberType() && Flags.isProtected(this.type2.getModifiers())) {
// protected member - check restriction on the enclosing type
IApiType enclosingType = this.type2;
try {
do {
if (enclosingType != null) {
final IApiAnnotations memberTypeAnnotations = apiDescription.resolveAnnotations(enclosingType.getHandle());
if (memberTypeAnnotations != null) {
int restrictions = memberTypeAnnotations.getRestrictions();
if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
restrictionsForMethodAddition |= RestrictionModifiers.NO_REFERENCE;
}
if (RestrictionModifiers.isExtendRestriction(restrictions)) {
// @noextend on a class that contains a
// protected member means that it cannot be
// referenced
restrictionsForMethodAddition |= RestrictionModifiers.NO_EXTEND;
if (this.visibilityModifiers == VisibilityModifiers.API) {
return;
}
}
if (RestrictionModifiers.isImplementRestriction(restrictions)) {
restrictionsForMethodAddition |= RestrictionModifiers.NO_IMPLEMENT;
}
}
}
enclosingType = enclosingType.getEnclosingType();
} while (enclosingType != null);
} catch (CoreException e) {
reportStatus(e);
}
}
}
if (Flags.isPublic(access) || Flags.isProtected(access)) {
if (method.isConstructor()) {
this.addDelta(getElementType(type), IDelta.ADDED, IDelta.CONSTRUCTOR, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, access, this.type1, getKeyForMethod(method, type), new String[] {
Util.getDescriptorName(type), methodDisplayName });
} else if (type.isAnnotation()) {
if (method.getDefaultValue() != null) {
this.addDelta(getElementType(type), IDelta.ADDED, IDelta.METHOD_WITH_DEFAULT_VALUE, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, access, this.type1, getKeyForMethod(method, type), new String[] {
Util.getDescriptorName(type), methodDisplayName });
} else {
this.addDelta(getElementType(type), IDelta.ADDED, IDelta.METHOD_WITHOUT_DEFAULT_VALUE, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, access, this.type1, getKeyForMethod(method, type), new String[] {
Util.getDescriptorName(type), methodDisplayName });
}
} else {
// check superclass
// if null we need to walk the hierarchy of descriptor2
boolean found = false;
if (this.component2 != null) {
String name = method.getName();
String descriptor = method.getSignature();
if (this.type1.isInterface()) {
Set<?> interfacesSet = getInterfacesSet(this.type2);
if (interfacesSet != null && isStatusOk()) {
for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = (IApiType) iterator.next();
IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
if (method3 == null) {
continue;
} else {
// interface method can only be public
// method has been move up in the hierarchy
// - report the delta and abort loop
found = true;
break;
}
}
}
} else {
List<?> superclassList = getSuperclassList(this.type2, true);
if (superclassList != null && isStatusOk()) {
loop: for (Iterator<?> iterator = superclassList.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = (IApiType) iterator.next();
IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
if (method3 == null) {
continue;
} else {
int access3 = method3.getModifiers();
if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
// method has been move up in the
// hierarchy - report the delta and
// abort loop
// TODO need to make the distinction
// between methods that need to be
// re-implemented and methods that don't
found = true;
break loop;
}
}
}
}
}
}
if (!found) {
// check if the method has been pushed down
// if null we need to walk the hierarchy of descriptor
if (this.component != null) {
String name = method.getName();
String descriptor = method.getSignature();
if (this.type1.isInterface()) {
Set<?> interfacesSet = getInterfacesSet(this.type1);
if (interfacesSet != null && isStatusOk()) {
for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = (IApiType) iterator.next();
IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
if (method3 == null) {
continue;
} else {
// interface method can only be public
// method has been move up in the
// hierarchy - report the delta and
// abort loop
found = true;
break;
}
}
}
} else {
List<?> superclassList = getSuperclassList(this.type1, true);
if (superclassList != null && isStatusOk()) {
loop: for (Iterator<?> iterator = superclassList.iterator(); iterator.hasNext();) {
IApiType superTypeDescriptor = (IApiType) iterator.next();
IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
if (method3 == null) {
continue;
} else {
int access3 = method3.getModifiers();
if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
// method has been pushed down in
// the hierarchy - report the delta
// and abort loop
// TODO need to make the distinction
// between methods that need to be
// re-implemented and methods that
// don't
found = true;
break loop;
}
}
}
}
}
}
this.addDelta(getElementType(type), IDelta.ADDED, found ? IDelta.METHOD_MOVED_DOWN : method.isDefaultMethod() ? IDelta.DEFAULT_METHOD : IDelta.METHOD, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, method.isDefaultMethod() ? (method.getModifiers() | Flags.AccDefaultMethod) : method.getModifiers(), this.type1, getKeyForMethod(method, type), new String[] {
Util.getDescriptorName(type), methodDisplayName });
} else {
this.addDelta(getElementType(type), IDelta.ADDED, found ? IDelta.OVERRIDEN_METHOD : IDelta.METHOD, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, method.getModifiers(), this.type1, getKeyForMethod(method, type), new String[] {
Util.getDescriptorName(type), methodDisplayName });
}
}
} else if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
// report non-API deltas for private and package-accessible methods
// as well:
this.addDelta(getElementType(type), IDelta.ADDED, method.isConstructor() ? IDelta.CONSTRUCTOR : IDelta.METHOD, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, method.getModifiers(), this.type1, getKeyForMethod(method, type), new String[] {
Util.getDescriptorName(type), methodDisplayName });
}
}
private String getKeyForMethod(IApiMethod method, IApiType type) {
StringBuffer buffer = new StringBuffer();
if (method.isConstructor()) {
String name = type.getName();
int index = name.lastIndexOf('.');
int dollarIndex = name.lastIndexOf('$');
if (dollarIndex != -1 && type.isMemberType()) {
buffer.append(type.getName().substring(dollarIndex + 1));
} else {
buffer.append(type.getName().substring(index + 1));
}
} else {
buffer.append(method.getName());
}
String genericSignature = method.getGenericSignature();
if (genericSignature != null) {
buffer.append(genericSignature);
} else {
buffer.append(method.getSignature());
}
return String.valueOf(buffer);
}
private static boolean isAPI(int visibility, IApiType memberTypeDescriptor) {
int access = memberTypeDescriptor.getModifiers();
return VisibilityModifiers.isAPI(visibility) && (Flags.isPublic(access) || Flags.isProtected(access));
}
private IApiTypeRoot getType(String typeName, IApiComponent component, IApiBaseline baseline) throws CoreException {
String packageName = Signatures.getPackageName(typeName);
IApiComponent[] components = baseline.resolvePackage(component, packageName);
if (components == null) {
String msg = MessageFormat.format(ComparatorMessages.ClassFileComparator_1, packageName, baseline.getName(), component.getSymbolicName());
if (ApiPlugin.DEBUG_CLASSFILE_COMPARATOR) {
System.err.println("TYPE LOOKUP: " + msg); //$NON-NLS-1$
}
reportStatus(new Status(IStatus.ERROR, component.getSymbolicName(), msg));
return null;
}
IApiTypeRoot result = Util.getClassFile(components, typeName);
if (result == null) {
String msg = MessageFormat.format(ComparatorMessages.ClassFileComparator_2, typeName, baseline.getName(), component.getSymbolicName());
if (ApiPlugin.DEBUG_CLASSFILE_COMPARATOR) {
System.err.println("TYPE LOOKUP: " + msg); //$NON-NLS-1$
}
reportStatus(new Status(IStatus.ERROR, component.getSymbolicName(), msg));
return null;
}
return result;
}
/**
* Returns the delta element type code for the given type. Translates a type
* to interface, class, emum or annotation.
*
* @param type
* @return delta element type
*/
private int getElementType(IApiType type) {
if (type.isAnnotation()) {
return IDelta.ANNOTATION_ELEMENT_TYPE;
}
if (type.isEnum()) {
return IDelta.ENUM_ELEMENT_TYPE;
}
if (type.isInterface()) {
return IDelta.INTERFACE_ELEMENT_TYPE;
}
return IDelta.CLASS_ELEMENT_TYPE;
}
/**
* Returns the delta element type code for the given method. Translates a
* method to constructor or method.
*
* @param method
* @return delta element type
*/
private int getElementType(IApiMethod method) {
if (method.isConstructor()) {
return IDelta.CONSTRUCTOR_ELEMENT_TYPE;
}
return IDelta.METHOD_ELEMENT_TYPE;
}
/**
* Returns the delta type code for the given method when it is the target of
* a remove/add. Translates a method to constructor or method.
*
* @param method
* @return delta type
*/
private int getTargetType(IApiMethod method) {
if (method.isConstructor()) {
return IDelta.CONSTRUCTOR;
}
return IDelta.METHOD;
}
/**
* Translates a member to its delta element type code.
*
* @param member
* @return delta element type code
*/
private int getElementType(IApiMember member) {
switch (member.getType()) {
case IApiElement.TYPE:
return getElementType((IApiType) member);
case IApiElement.METHOD:
return getElementType((IApiMethod) member);
default:
break;
}
return IDelta.FIELD_ELEMENT_TYPE;
}
}