blob: 3405fb4f7d0cde83b21602ead096e9ed462a1130 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.tools.apichecker;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
/**
* encapsulates a class that produces the diff between two api's.
*/
public class ApiDiffGenerator {
public static final String DELIMITER = " ";
@SuppressWarnings("unchecked")
public static HashSet<String> findCommonElements(HashSet<String> s1,
HashSet<String> s2, String name) {
HashSet<String> intersection = (HashSet<String>) s1.clone();
intersection.retainAll(s2);
s1.removeAll(intersection);
s2.removeAll(intersection);
return intersection;
}
/**
* The two types might belong to different typeOracles.
*/
public static boolean isFirstTypeAssignableToSecond(JType firstType,
JType secondType) {
// getJNISignature() does TypeErasure
if (firstType.getJNISignature().equals(secondType.getJNISignature())) {
return true;
}
JClassType classType1 = firstType.isClassOrInterface();
JClassType classType2 = secondType.isClassOrInterface();
if (classType1 == null || classType2 == null) {
return false;
}
TypeOracle newApiTypeOracle = classType2.getOracle();
// get the appropriate classObject in the newApi
JClassType firstClassType = newApiTypeOracle.findType(classType1.getQualifiedSourceName());
JClassType secondClassType = newApiTypeOracle.findType(classType2.getQualifiedSourceName());
// The types might not necessarily exist in the newApi
if (firstClassType == null || secondClassType == null) {
return false;
}
return firstClassType.isAssignableTo(secondClassType);
}
HashMap<String, ApiPackageDiffGenerator> intersectingPackages = new HashMap<String, ApiPackageDiffGenerator>();
HashSet<String> missingPackageNames = null;
ApiContainer newApi = null;
ApiContainer oldApi = null;
ApiDiffGenerator(ApiContainer newApi, ApiContainer oldApi) {
this.newApi = newApi;
this.oldApi = oldApi;
}
public void cleanApiDiff() {
Iterator<ApiPackageDiffGenerator> tempIterator = intersectingPackages.values().iterator();
while (tempIterator.hasNext()) {
tempIterator.next().cleanApiDiff();
}
}
/**
* Compares 2 APIs for source compatibility. Algorithm: First find packages
* that are in one but not in another. Then, look at at classes in the common
* packages. Look at public classes.
*
*/
public void computeApiDiff() throws NotFoundException {
HashSet<String> newApiPackageNames = newApi.getApiPackageNames();
missingPackageNames = oldApi.getApiPackageNames();
HashSet<String> intersection = findCommonElements(newApiPackageNames,
missingPackageNames, "ROOT");
// Inspect each of the classes in each of the packages in the intersection
Iterator<String> tempIterator = intersection.iterator();
while (tempIterator.hasNext()) {
String packageName = tempIterator.next();
ApiPackageDiffGenerator temp = new ApiPackageDiffGenerator(packageName,
this);
intersectingPackages.put(packageName, temp);
temp.computeApiDiff();
}
}
public ApiClassDiffGenerator findApiClassDiffGenerator(JClassType classType) {
String className = classType.getQualifiedSourceName();
int i = className.length() - 1;
while (i >= 0) {
int dot = className.lastIndexOf('.', i);
String pkgName = "";
String typeName = className;
if (dot != -1) {
pkgName = className.substring(0, dot);
typeName = className.substring(dot + 1);
i = dot - 1;
} else {
i = -1;
}
ApiClassDiffGenerator result = findApiClassDiffGenerator(pkgName,
typeName);
if (result != null) {
return result;
}
}
return null;
}
// TODO(amitmanjhi): cache the results
/**
* Finds a type given its package-relative name. For nested classes, use its
* source name rather than its binary name (that is, use a "." rather than a
* "$").
*
* @return <code>null</code> if the type is not found
*/
public ApiClassDiffGenerator findApiClassDiffGenerator(String pkgName,
String typeName) {
ApiPackageDiffGenerator pkg = findApiPackageDiffGenerator(pkgName);
if (pkg != null) {
ApiClassDiffGenerator type = pkg.findApiClassDiffGenerator(pkgName + "."
+ typeName);
if (type != null) {
return type;
}
}
return null;
}
public ApiPackageDiffGenerator findApiPackageDiffGenerator(String key) {
return intersectingPackages.get(key);
}
public ApiContainer getNewApiContainer() {
return newApi;
}
public ApiContainer getOldApiContainer() {
return oldApi;
}
public String printApiDiff() {
StringBuffer sb = new StringBuffer();
Iterator<String> missingPackagesIterator = missingPackageNames.iterator();
while (missingPackagesIterator.hasNext()) {
sb.append(missingPackagesIterator.next() + DELIMITER
+ ApiChange.Status.MISSING + "\n");
}
Iterator<ApiPackageDiffGenerator> tempIterator = intersectingPackages.values().iterator();
while (tempIterator.hasNext()) {
sb.append(tempIterator.next().printApiDiff());
}
return sb.toString();
}
}