Teach apicheck about interface methods.

If a method is declared by an interface implemented directly by a class,
or indirectly by any of its superclasses, removing a duplicate declaration
is not an API change.

(This fixes the master build, which is currently broken because getOption
and setOption were removed from DatagramSocketImpl because they were
duplicate declarations of the same methods in the implemented interface
SocketOptions.)
This commit is contained in:
Elliott Hughes 2009-11-09 23:49:43 -08:00
parent 9dd765ff2c
commit 03ead3365d
2 changed files with 41 additions and 7 deletions

View file

@ -26,8 +26,19 @@ public class ApiInfo {
mPackages = new HashMap<String, PackageInfo>();
mAllClasses = new HashMap<String, ClassInfo>();
}
public ClassInfo findClass(String name) {
return mAllClasses.get(name);
}
private void resolveInterfaces() {
for (ClassInfo c : mAllClasses.values()) {
c.resolveInterfaces(this);
}
}
public boolean isConsistent(ApiInfo otherApi) {
resolveInterfaces();
boolean consistent = true;
for (PackageInfo pInfo : mPackages.values()) {
if (otherApi.getPackages().containsKey(pInfo.name())) {

View file

@ -26,7 +26,8 @@ public class ClassInfo {
private boolean mIsFinal;
private String mDeprecated;
private String mScope;
private List<String> mInterfaces;
private List<String> mInterfaceNames;
private List<ClassInfo> mInterfaces;
private HashMap<String, MethodInfo> mMethods;
private HashMap<String, FieldInfo> mFields;
private HashMap<String, ConstructorInfo> mConstructors;
@ -48,7 +49,8 @@ public class ClassInfo {
mIsFinal = isFinal;
mDeprecated = deprecated;
mScope = visibility;
mInterfaces = new ArrayList<String>();
mInterfaceNames = new ArrayList<String>();
mInterfaces = new ArrayList<ClassInfo>();
mMethods = new HashMap<String, MethodInfo>();
mFields = new HashMap<String, FieldInfo>();
mConstructors = new HashMap<String, ConstructorInfo>();
@ -109,6 +111,18 @@ public class ClassInfo {
return null;
}
// Find a superinterface declaration of the given method.
public MethodInfo interfaceMethod(MethodInfo candidate) {
for (ClassInfo interfaceInfo : mInterfaces) {
for (MethodInfo mi : interfaceInfo.mMethods.values()) {
if (mi.matches(candidate)) {
return mi;
}
}
}
return (mSuperClass != null) ? mSuperClass.interfaceMethod(candidate) : null;
}
public boolean isConsistent(ClassInfo cl) {
cl.mExistsInBoth = true;
mExistsInBoth = true;
@ -120,18 +134,18 @@ public class ClassInfo {
+ " changed class/interface declaration");
consistent = false;
}
for (String iface : mInterfaces) {
for (String iface : mInterfaceNames) {
boolean found = false;
for (ClassInfo c = cl; c != null && !found; c = c.mSuperClass) {
found = c.mInterfaces.contains(iface);
found = c.mInterfaceNames.contains(iface);
}
if (!found) {
Errors.error(Errors.REMOVED_INTERFACE, cl.position(),
"Class " + qualifiedName() + " no longer implements " + iface);
}
}
for (String iface : cl.mInterfaces) {
if (!mInterfaces.contains(iface)) {
for (String iface : cl.mInterfaceNames) {
if (!mInterfaceNames.contains(iface)) {
Errors.error(Errors.ADDED_INTERFACE, cl.position(),
"Added interface " + iface + " to class "
+ qualifiedName());
@ -150,6 +164,9 @@ public class ClassInfo {
* fulfills the API requirement.
*/
MethodInfo mi = mInfo.containingClass().overriddenMethod(mInfo);
if (mi == null) {
mi = mInfo.containingClass().interfaceMethod(mInfo);
}
if (mi == null) {
Errors.error(Errors.REMOVED_METHOD, mInfo.position(),
"Removed public method " + mInfo.qualifiedName());
@ -256,9 +273,15 @@ public class ClassInfo {
return consistent;
}
public void resolveInterfaces(ApiInfo apiInfo) {
for (String interfaceName : mInterfaceNames) {
mInterfaces.add(apiInfo.findClass(interfaceName));
}
}
public void addInterface(String name) {
mInterfaces.add(name);
mInterfaceNames.add(name);
}
public void addMethod(MethodInfo mInfo) {