1) Adds command-line flags to allow the user to specify a reference jar and gwt dev
and user jars. These changes were necessary so that gwt-incubator and other
projects using gwt-trunk could use api-checker.
2) Cleans up the code for processing the command-line flags (it uses ToolBase
now) and the config file.
3) Adds the capability to get a list of compilation units from a jar file.
4) Updates the build files as per the above changes.
Patch by: amitmanjhi
Review by: spoon (desk review)
git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4343 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/build.xml b/build.xml
index 48c1335..d6761b0 100755
--- a/build.xml
+++ b/build.xml
@@ -83,6 +83,8 @@
</copy>
<java failonerror="true" fork="true"
classname="com.google.gwt.tools.apichecker.ApiCompatibilityChecker">
+ <jvmarg line="-Xmx512m" />
+ <sysproperty key="gwt.devjar" value="${gwt.dev.staging.jar}" />
<classpath>
<pathelement location="${gwt.build.out}/tools/api-checker/bin"/>
<pathelement location="${gwt.build.out}/dev/core/bin"/>
@@ -90,6 +92,7 @@
<pathelement location="${gwt.tools.lib}/eclipse/jdt-3.3.1.jar"/>
<pathelement path="${java.class.path}"/>
</classpath>
+ <arg value="-configFile"/>
<arg file="${gwt.build.out}/userApi.conf"/>
</java>
</target>
diff --git a/tools/api-checker/build.xml b/tools/api-checker/build.xml
index 3259342..8afcaea 100755
--- a/tools/api-checker/build.xml
+++ b/tools/api-checker/build.xml
@@ -54,6 +54,7 @@
<mkdir dir="${junit.out}/reports" />
<junit dir="${junit.out}" fork="yes" printsummary="yes" haltonfailure="true">
+ <sysproperty key="gwt.devjar" value="${gwt.dev.staging.jar}" />
<classpath>
<pathelement location="test" />
<pathelement location="${gwt.build.out}/dev/core/bin"/>
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java
index 71bd2e2..8e2c6e5 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiCompatibilityChecker.java
@@ -16,19 +16,37 @@
package com.google.gwt.tools.apichecker;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.dev.javac.CompilationUnit;
+import com.google.gwt.dev.javac.impl.FileCompilationUnit;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.util.tools.ArgHandlerFlag;
+import com.google.gwt.util.tools.ArgHandlerString;
+import com.google.gwt.util.tools.ToolBase;
+import java.io.BufferedInputStream;
import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
+import java.util.Properties;
import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
/**
* {@link ApiCompatibilityChecker} Main class to check if the new API is
@@ -67,46 +85,349 @@
* adding the 'final' keyword to the API member.
*
*/
-public class ApiCompatibilityChecker {
-
- // TODO(amitmanjhi): use ToolBase for command-line processing
+public class ApiCompatibilityChecker extends ToolBase {
// TODO(amitmanjhi): check gwt's dev/core/src files. Would need the ability to
// build TypeOracle from class files
- // TODO(amitmanjhi): ignore API breakages due to impl package. More generally,
- // white-list of packages that should not be checked.
-
// TODO(amitmanjhi): better handling of exceptions and exception-chaining.
+ static class StaticCompilationUnit extends CompilationUnit {
+
+ private final String source;
+ private final String typeName;
+
+ public StaticCompilationUnit(String typeName, String source) {
+ this.typeName = typeName;
+ this.source = source;
+ }
+
+ @Override
+ public String getDisplayLocation() {
+ return "/mock/" + typeName;
+ }
+
+ @Override
+ public long getLastModified() {
+ return 0;
+ }
+
+ @Override
+ public String getSource() {
+ return String.valueOf(source);
+ }
+
+ @Override
+ public String getTypeName() {
+ return typeName;
+ }
+
+ @Override
+ public boolean isGenerated() {
+ return false;
+ }
+
+ @Override
+ public boolean isSuperSource() {
+ return false;
+ }
+ }
+
+ /**
+ * Abstract internal class that specifies a set of {@link CompilationUnit}.
+ */
+ private abstract static class CompilationUnits {
+ protected final TreeLogger logger;
+
+ CompilationUnits(TreeLogger logger) {
+ this.logger = logger;
+ }
+
+ public abstract Set<CompilationUnit> getCompilationUnits()
+ throws NotFoundException, IOException, UnableToCompleteException;
+
+ // TODO (amitmanjhi): remove this code. use TypeOracle functionality
+ // instead.
+ protected String extractPackageName(Reader reader) throws IOException {
+ BufferedReader br = new BufferedReader(reader);
+ String str = null;
+ while ((str = br.readLine()) != null) {
+ if (str.indexOf("package") != 0) {
+ continue;
+ }
+ String parts[] = str.split("[\b\t\n\r ]+");
+ if ((parts.length == 2) && parts[0].equals("package")) {
+ return parts[1].substring(0, parts[1].length() - 1); // the ; char
+ }
+ }
+ return null;
+ }
+
+ protected File getFileFromName(String tag, String pathName)
+ throws FileNotFoundException {
+ File file = new File(pathName);
+ if (!file.exists()) {
+ throw new FileNotFoundException(tag + "file " + pathName + " not found");
+ }
+ return file;
+ }
+
+ // TODO (amitmanjhi): remove this code. use TypeOracle functionality
+ // instead.
+ protected boolean isValidPackage(String packageName, String filePath) {
+ logger.log(TreeLogger.SPAM, "packageName = " + packageName
+ + ", filePath = " + filePath, null);
+ if (packageName == null) {
+ return false;
+ }
+ int lastSlashPosition = filePath.lastIndexOf("/");
+ if (lastSlashPosition == -1) {
+ return false;
+ }
+ String dirPath = filePath.substring(0, lastSlashPosition);
+ String packageNameAsPath = packageName.replace('.', '/');
+ logger.log(TreeLogger.SPAM, "packageNameAsPath " + packageNameAsPath
+ + ", dirPath = " + dirPath, null);
+ return dirPath.endsWith(packageNameAsPath);
+ }
+ }
+
+ /**
+ * Class that specifies a set of {@link CompilationUnit} read from jar files.
+ */
+ private static class JarFileCompilationUnits extends CompilationUnits {
+ private final Set<String> excludedPaths;
+ private final Set<String> includedPaths;
+ private final JarFile jarFiles[];
+ private Set<CompilationUnit> units = null;
+
+ JarFileCompilationUnits(JarFile[] jarFiles, Set<String> includedPaths,
+ Set<String> excludedPaths, TreeLogger logger) {
+ super(logger);
+ this.jarFiles = jarFiles;
+ this.includedPaths = includedPaths;
+ this.excludedPaths = excludedPaths;
+ }
+
+ @Override
+ public Set<CompilationUnit> getCompilationUnits() throws IOException {
+ if (units != null) {
+ return units;
+ }
+
+ units = new HashSet<CompilationUnit>();
+ for (JarFile jarFile : jarFiles) {
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry jarEntry = entries.nextElement();
+ String fileName = jarEntry.toString();
+ if (fileName.endsWith(".java") && isIncluded(fileName)) {
+ // add this compilation unit
+ String fileContent = getFileContentsFromJar(jarFile, jarEntry);
+ String packageName = extractPackageName(new StringReader(
+ fileContent));
+ if (packageName == null) {
+ logger.log(TreeLogger.WARN, "Not adding file = " + fileName
+ + ", because packageName = null", null);
+ } else {
+ if (isValidPackage(packageName, fileName)) {
+ // Add if package and fileNames are okay
+ units.add(new StaticCompilationUnit(packageName + "."
+ + getClassName(fileName), fileContent));
+ logger.log(TreeLogger.DEBUG, "adding pkgName = " + packageName
+ + ", file = " + fileName, null);
+ } else {
+ logger.log(TreeLogger.SPAM, " not adding file " + fileName,
+ null);
+ }
+ }
+ }
+ }
+ }
+ return units;
+ }
+
+ String getFileContentsFromJar(JarFile jarFile, JarEntry jarEntry)
+ throws IOException {
+ StringBuffer fileContent = new StringBuffer();
+ InputStream is = jarFile.getInputStream(jarEntry);
+ BufferedInputStream bis = new BufferedInputStream(is);
+ int length = 500;
+ byte buf[] = new byte[length];
+ int count = 0;
+ while ((count = bis.read(buf, 0, length)) != -1) {
+ fileContent.append(new String(buf, 0, count));
+ buf = new byte[length];
+ }
+ bis.close();
+ is.close();
+ return fileContent.toString();
+ }
+
+ private String getClassName(String fileName) {
+ int index = fileName.lastIndexOf("/");
+ int endOffset = 0;
+ if (fileName.endsWith(".java")) {
+ endOffset = 5;
+ }
+ return fileName.substring(index + 1, fileName.length() - endOffset);
+ }
+
+ private boolean isIncluded(String fileName) {
+ int index = fileName.length();
+ do {
+ fileName = fileName.substring(0, index);
+ if (includedPaths.contains(fileName)) {
+ return true;
+ }
+ if (excludedPaths.contains(fileName)) {
+ return false;
+ }
+ index = fileName.lastIndexOf("/");
+ } while (index != -1);
+ return false;
+ }
+ }
+
+ /**
+ * Class that specifies a set of {@link CompilationUnit} read from the
+ * file-system.
+ */
+ private static class SourceFileCompilationUnits extends CompilationUnits {
+ private final Set<String> excludedPaths;
+ private final Set<File> includedPaths;
+ private Set<CompilationUnit> units = null;
+
+ SourceFileCompilationUnits(String dirRoot,
+ Set<String> includedPathsAsString, Set<String> excludedPathsAsString,
+ TreeLogger logger) throws FileNotFoundException, IOException {
+ super(logger);
+ if (dirRoot == null) {
+ dirRoot = "";
+ }
+ includedPaths = new HashSet<File>();
+ for (String includedPath : includedPathsAsString) {
+ includedPaths.add(getFileFromName("source file: ", dirRoot
+ + includedPath));
+ }
+
+ excludedPaths = new HashSet<String>();
+ for (String excludedPath : excludedPathsAsString) {
+ excludedPaths.add(getFileFromName("excluded file: ",
+ dirRoot + excludedPath).getCanonicalPath());
+ }
+ }
+
+ @Override
+ public Set<CompilationUnit> getCompilationUnits() throws NotFoundException,
+ IOException, UnableToCompleteException {
+ if (units != null) {
+ return units;
+ }
+
+ units = new HashSet<CompilationUnit>();
+ for (File sourceFile : includedPaths) {
+ updateCompilationUnitsInPath(sourceFile);
+ }
+ return units;
+ }
+
+ private boolean isExcludedFile(String fileName) {
+ String pattern = "file:";
+ if (fileName.indexOf(pattern) == 0) {
+ fileName = fileName.substring(pattern.length());
+ }
+ return excludedPaths.contains(fileName);
+ }
+
+ private void updateCompilationUnitsInPath(File sourcePathEntry)
+ throws NotFoundException, IOException, UnableToCompleteException {
+ logger.log(TreeLogger.SPAM, "entering addCompilationUnitsInPath, file = "
+ + sourcePathEntry, null);
+ File[] files = sourcePathEntry.listFiles();
+ if (files == null) { // No files found.
+ return;
+ }
+
+ for (int i = 0; i < files.length; i++) {
+ final File file = files[i];
+ logger.log(TreeLogger.SPAM, "deciding the fate of file " + file, null);
+ // Ignore files like .svn and .cvs
+ if (file.getName().startsWith(".") || file.getName().equals("CVS")) {
+ continue;
+ }
+ if (isExcludedFile(file.getCanonicalPath())) {
+ // do not process the subtree
+ logger.log(TreeLogger.DEBUG,
+ "not traversing " + file.toURI().toURL(), null);
+ continue;
+ }
+ if (file.isFile()) {
+ String pkgName = null;
+ if (file.getName().endsWith("java")) {
+ pkgName = extractPackageName(new FileReader(file));
+ if (pkgName == null) {
+ logger.log(TreeLogger.WARN, "Not adding file = "
+ + file.toString() + ", because packageName = null", null);
+ } else {
+ if (isValidPackage(pkgName,
+ sourcePathEntry.toURI().toURL().toString())) {
+ // Add if the package and fileNames are okay
+ units.add(new FileCompilationUnit(file, pkgName));
+ logger.log(TreeLogger.DEBUG, "adding pkgName = " + pkgName
+ + ", file = " + file.toString(), null);
+ } else {
+ logger.log(TreeLogger.SPAM, " not adding file "
+ + file.toURI().toURL(), null);
+ }
+ }
+ }
+ } else {
+ // Recurse into subDirs
+ updateCompilationUnitsInPath(file);
+ }
+ }
+ }
+ }
+
// currently doing only source_compatibility. true by default.
public static final boolean API_SOURCE_COMPATIBILITY = true;
- // prints which class the member was declared in plus the string message in
- // ApiChange, false by default
- public static final boolean DEBUG = false;
-
- // prints the API of the two containers, false by default.
- public static final boolean DEBUG_PRINT_ALL_API = false;
-
- // these two parameters print APIs common in the two repositories. Should be
- // false by default.
- public static final boolean PRINT_COMPATIBLE = false;
-
- public static final boolean PRINT_COMPATIBLE_WITH = false;
- // for debugging. To see if TypeOracle builds
- public static final boolean PROCESS_EXISTING_API = true;
-
- public static final boolean PROCESS_NEW_API = true;
- // true by default
- public static final boolean REMOVE_NON_SUBCLASSABLE_ABSTRACT_CLASS_FROM_API = true;
-
- public static final boolean FILTER_DUPLICATES = true;
+ /**
+ * prints which class the member was declared in plus the string message in
+ * ApiChange, false by default
+ */
+ public static boolean DEBUG = false;
public static final boolean DEBUG_DUPLICATE_REMOVAL = false;
- // Tweak for log output.
- public static final TreeLogger.Type type = TreeLogger.WARN;
+ /**
+ * Flag for filtering duplicates, true by default. Just for debugging in rare
+ * cases.
+ */
+ public static final boolean FILTER_DUPLICATES = true;
+
+ /**
+ * Print APIs common in the two repositories. Should be false by default.
+ */
+ public static final boolean PRINT_COMPATIBLE = false;
+ /**
+ * Print APIs common in the two repositories. Should be false by default.
+ */
+ public static final boolean PRINT_COMPATIBLE_WITH = false;
+
+ /**
+ * Flag for debugging whether typeOracle builds.
+ */
+ public static final boolean PROCESS_EXISTING_API = true;
+
+ /**
+ * Flag for debugging whether typeOracle builds.
+ */
+ public static final boolean PROCESS_NEW_API = true;
+
+ // true by default
+ public static final boolean REMOVE_NON_SUBCLASSABLE_ABSTRACT_CLASS_FROM_API = true;
// remove duplicates by default
public static Collection<ApiChange> getApiDiff(ApiContainer newApi,
@@ -115,45 +436,70 @@
return getApiDiff(apiDiff, whiteList, FILTER_DUPLICATES);
}
- // Call APIBuilders for each of the 2 source trees
public static void main(String args[]) {
-
try {
- ApiContainer newApi = null, existingApi = null;
+ ApiCompatibilityChecker checker = new ApiCompatibilityChecker();
+ if (checker.processArgs(args)) {
+ ApiContainer newApi = null, existingApi = null;
- if (args.length < 1) {
- printHelp();
- System.exit(-1);
- }
+ AbstractTreeLogger logger = new PrintWriterTreeLogger();
+ logger.setMaxDetail(checker.type);
+ logger.log(TreeLogger.INFO, "gwtDevJar = " + checker.gwtDevJar
+ + ", userJar = " + checker.gwtUserJar + ", refjars = "
+ + checker.refJars + ", logLevel = " + checker.type
+ + ", printAllApi = " + checker.printAllApi, null);
- AbstractTreeLogger logger = new PrintWriterTreeLogger();
- logger.setMaxDetail(type);
- if (PROCESS_NEW_API) {
- newApi = new ApiContainer(args[0], "_new", logger);
- if (ApiCompatibilityChecker.DEBUG_PRINT_ALL_API) {
- logger.log(TreeLogger.INFO, newApi.getApiAsString()); // print the API
+ Set<String> excludedPackages = checker.getSetOfExcludedPackages(checker.configProperties);
+ if (PROCESS_NEW_API) {
+ Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+ units.addAll(new SourceFileCompilationUnits(
+ checker.configProperties.getProperty("dirRoot_new"),
+ checker.getConfigPropertyAsSet("sourceFiles_new"),
+ checker.getConfigPropertyAsSet("excludedFiles_new"), logger).getCompilationUnits());
+ units.addAll(checker.getGwtCompilationUnits(logger));
+ newApi = new ApiContainer(
+ checker.configProperties.getProperty("name_new"), units,
+ excludedPackages, logger);
+ if (checker.printAllApi) {
+ logger.log(TreeLogger.INFO, newApi.getApiAsString());
+ }
}
- }
- if (PROCESS_EXISTING_API) {
- existingApi = new ApiContainer(args[0], "_old", logger);
- if (ApiCompatibilityChecker.DEBUG_PRINT_ALL_API) {
- logger.log(TreeLogger.INFO, existingApi.getApiAsString());
+ if (PROCESS_EXISTING_API) {
+ Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+ if (checker.refJars == null) {
+ units.addAll(new SourceFileCompilationUnits(
+ checker.configProperties.getProperty("dirRoot_old"),
+ checker.getConfigPropertyAsSet("sourceFiles_old"),
+ checker.getConfigPropertyAsSet("excludedFiles_old"), logger).getCompilationUnits());
+ } else {
+ units.addAll(new JarFileCompilationUnits(checker.refJars,
+ checker.getConfigPropertyAsSet("sourceFiles_old"),
+ checker.getConfigPropertyAsSet("excludedFiles_old"), logger).getCompilationUnits());
+ }
+ units.addAll(checker.getGwtCompilationUnits(logger));
+ existingApi = new ApiContainer(
+ checker.configProperties.getProperty("name_old"), units,
+ excludedPackages, logger);
+ if (checker.printAllApi) {
+ logger.log(TreeLogger.INFO, existingApi.getApiAsString());
+ }
+ }
+
+ if (PROCESS_NEW_API && PROCESS_EXISTING_API) {
+ Collection<ApiChange> apiDifferences = getApiDiff(newApi,
+ existingApi, checker.whiteList);
+ for (ApiChange apiChange : apiDifferences) {
+ System.out.println(apiChange);
+ }
+ if (apiDifferences.size() == 0) {
+ System.out.println("API compatibility check SUCCESSFUL");
+ } else {
+ System.out.println("API compatibility check FAILED");
+ }
+ System.exit(apiDifferences.size() == 0 ? 0 : 1);
}
}
- if (PROCESS_NEW_API && PROCESS_EXISTING_API) {
- Collection<ApiChange> apiDifferences = getApiDiff(newApi, existingApi,
- readWhiteListFromFile(args[0]));
- for (ApiChange apiChange : apiDifferences) {
- System.out.println(apiChange);
- }
- if (apiDifferences.size() == 0) {
- System.out.println("API compatibility check SUCCESSFUL");
- } else {
- System.out.println("API compatibility check FAILED");
- }
- System.exit(apiDifferences.size() == 0 ? 0 : 1);
- }
} catch (Exception e) {
// intercepting all exceptions in main, because I have to exit with -1 so
// that the build breaks.
@@ -163,44 +509,6 @@
}
}
- public static void printHelp() {
- StringBuffer sb = new StringBuffer();
- sb.append("java ApiCompatibilityChecker configFile\n");
- sb.append("The ApiCompatibilityChecker tool requires a config file as an argument. ");
- sb.append("The config file must specify two repositories of java source files: ");
- sb.append("'_old' and '_new', which are to be compared for API source compatibility.\n");
- sb.append("An optional whitelist is present at the end of ");
- sb.append("the config file. The format of the whitelist is same as the output of ");
- sb.append("the tool without the whitelist.\n");
- sb.append("Each repository is specified by the following four properties:\n");
- sb.append("name specifies how the api should be refered to in the output\n");
- sb.append("dirRoot optional argument that specifies the base directory of all other file/directory names\n");
- sb.append("sourceFiles a colon-separated list of files/directories that specify the roots of the the filesystem trees to be included.\n");
- sb.append("excludeFiles a colon-separated lists of files/directories that specify the roots of the filesystem trees to be excluded");
-
- sb.append("\n\n");
- sb.append("Example api.conf file:\n");
- sb.append("name_old gwtEmulator");
- sb.append("\n");
- sb.append("dirRoot_old ./");
- sb.append("\n");
- sb.append("sourceFiles_old dev/core/super:user/super:user/src");
- sb.append("\n");
- sb.append("excludeFiles_old user/super/com/google/gwt/junit");
- sb.append("\n\n");
-
- sb.append("name_new gwtEmulatorCopy");
- sb.append("\n");
- sb.append("dirRoot_new ../gwt-14/");
- sb.append("\n");
- sb.append("sourceFiles_new dev/core:user/super:user/src");
- sb.append("\n");
- sb.append("excludeFiles_new user/super/com/google/gwt/junit");
- sb.append("\n\n");
-
- System.out.println(sb.toString());
- }
-
// interface for testing, since do not want to build ApiDiff frequently
static Collection<ApiChange> getApiDiff(ApiDiffGenerator apiDiff,
Set<String> whiteList, boolean removeDuplicates) throws NotFoundException {
@@ -244,6 +552,330 @@
return apiChangeList;
}
+ private Properties configProperties;
+
+ private JarFile gwtDevJar;
+ private JarFile gwtUserJar;
+
+ // prints the API of the two containers, false by default.
+ private boolean printAllApi = false;
+ private JarFile refJars[];
+ // default log output
+ private TreeLogger.Type type = TreeLogger.WARN;
+ private Set<String> whiteList;
+
+ protected ApiCompatibilityChecker() {
+
+ /*
+ * register handlers for gwtDevJar, gwtUserJar, refJar, logLevel,
+ * printAllApi, configProperties
+ */
+
+ // handler for gwtDevJar
+ registerHandler(new ArgHandlerString() {
+
+ @Override
+ public String getPurpose() {
+ return "Path of the gwt dev jar";
+ }
+
+ @Override
+ public String getTag() {
+ return "-gwtDevJar";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"gwt_dev_jar_path"};
+ }
+
+ @Override
+ public boolean setString(String str) {
+ gwtDevJar = getJarFromString(str);
+ return gwtDevJar != null;
+ }
+ });
+
+ // handler for gwtUserJar
+ registerHandler(new ArgHandlerString() {
+
+ @Override
+ public String getPurpose() {
+ return "Path of the gwt user jar";
+ }
+
+ @Override
+ public String getTag() {
+ return "-gwtUserJar";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"gwt_user_jar_path"};
+ }
+
+ @Override
+ public boolean setString(String str) {
+ gwtUserJar = getJarFromString(str);
+ return gwtUserJar != null;
+ }
+ });
+
+ // handler for refJar
+ registerHandler(new ArgHandlerString() {
+ @Override
+ public String getPurpose() {
+ return "Path of the reference jar";
+ }
+
+ @Override
+ public String getTag() {
+ return "-refJar";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"reference_jar_path"};
+ }
+
+ @Override
+ public boolean setString(String str) {
+ String refJarStrings[] = str.split(":");
+ refJars = new JarFile[refJarStrings.length];
+ int count = 0;
+ for (String refJarString : refJarStrings) {
+ refJars[count++] = getJarFromString(refJarString);
+ if (refJars[count - 1] == null) {
+ return false; // bail-out early
+ }
+ }
+ return refJars != null;
+ }
+ });
+
+ // handler for logLevel
+ registerHandler(new ArgHandlerString() {
+
+ @Override
+ public String getPurpose() {
+ return "Sets the log level of the TreeLogger";
+ }
+
+ @Override
+ public String getTag() {
+ return "-logLevel";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ String values[] = new String[TreeLogger.Type.values().length];
+ int count = 0;
+ for (TreeLogger.Type tempType : TreeLogger.Type.values()) {
+ values[count++] = tempType.name();
+ }
+ return values;
+ }
+
+ @Override
+ public boolean setString(String str) {
+ for (TreeLogger.Type tempType : TreeLogger.Type.values()) {
+ if (tempType.name().equals(str)) {
+ type = tempType;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ });
+
+ // handler for printAllApi
+ registerHandler(new ArgHandlerFlag() {
+
+ @Override
+ public String getPurpose() {
+ return "Prints all api";
+ }
+
+ @Override
+ public String getTag() {
+ return "-printAllApi";
+ }
+
+ @Override
+ public boolean setFlag() {
+ printAllApi = true;
+ return true;
+ }
+
+ });
+
+ // handler for configFile
+ registerHandler(new ArgHandlerString() {
+
+ @Override
+ public String getPurpose() {
+ return "Path of the configuration file";
+ }
+
+ @Override
+ public String getTag() {
+ return "-configFile";
+ }
+
+ @Override
+ public String[] getTagArgs() {
+ return new String[] {"config_file_path"};
+ }
+
+ @Override
+ public boolean isRequired() {
+ return true;
+ }
+
+ @Override
+ public boolean setString(String str) {
+ setPropertiesAndWhitelist(str);
+ return configProperties != null && whiteList != null;
+ }
+ });
+ }
+
+ @Override
+ public void printHelp() {
+ super.printHelp();
+ StringBuffer sb = new StringBuffer();
+ sb.append("\n");
+ sb.append("The config file must specify two repositories of java source files: ");
+ sb.append("'_old' and '_new', which are to be compared for API source compatibility.\n");
+ sb.append("An optional whitelist is present at the end of ");
+ sb.append("the config file. The format of the whitelist is same as the output of ");
+ sb.append("the tool without the whitelist.\n");
+ sb.append("Each repository is specified by the following four properties:\n");
+ sb.append("name specifies how the api should be refered to in the output\n");
+ sb.append("dirRoot optional argument that specifies the base directory of all other file/directory names\n");
+ sb.append("sourceFiles a colon-separated list of files/directories that specify the roots of the the filesystem trees to be included.\n");
+ sb.append("excludeFiles a colon-separated lists of files/directories that specify the roots of the filesystem trees to be excluded");
+
+ sb.append("\n\n");
+ sb.append("Example api.conf file:\n");
+ sb.append("name_old gwtEmulator");
+ sb.append("\n");
+ sb.append("dirRoot_old ./");
+ sb.append("\n");
+ sb.append("sourceFiles_old dev/core/super:user/super:user/src");
+ sb.append("\n");
+ sb.append("excludeFiles_old user/super/com/google/gwt/junit");
+ sb.append("\n\n");
+
+ sb.append("name_new gwtEmulatorCopy");
+ sb.append("\n");
+ sb.append("dirRoot_new ../gwt-14/");
+ sb.append("\n");
+ sb.append("sourceFiles_new dev/core:user/super:user/src");
+ sb.append("\n");
+ sb.append("excludeFiles_new user/super/com/google/gwt/junit");
+ sb.append("\n\n");
+
+ System.err.println(sb.toString());
+ }
+
+ protected JarFile getJarFromString(String str) {
+ try {
+ return new JarFile(str);
+ } catch (IOException ex) {
+ System.err.println("exception in getting jar from name "
+ + ex.getMessage());
+ return null;
+ }
+ }
+
+ protected void setPropertiesAndWhitelist(String fileName)
+ throws IllegalArgumentException {
+ try {
+ // load config properties
+ FileInputStream fis = new FileInputStream(fileName);
+ configProperties = new Properties();
+ configProperties.load(fis);
+ fis.close();
+
+ // load whitelist
+ FileReader fr = new FileReader(fileName);
+ whiteList = readWhiteListFromFile(fr);
+ fr.close();
+ } catch (IOException ex) {
+ System.err.println("Have you specified the path of the config file correctly?");
+ throw new IllegalArgumentException(ex);
+ }
+ }
+
+ private Set<String> getConfigPropertyAsSet(String key) {
+ Set<String> set = new HashSet<String>();
+ String propertyValue = configProperties.getProperty(key);
+ if (propertyValue == null) {
+ return set;
+ }
+ for (String element : propertyValue.split(":")) {
+ element = element.trim();
+ set.add(element);
+ }
+ return set;
+ }
+
+ private Set<CompilationUnit> getGwtCompilationUnits(TreeLogger logger)
+ throws FileNotFoundException, IOException, NotFoundException,
+ UnableToCompleteException {
+ Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+ if (gwtDevJar == null || gwtUserJar == null) {
+ if (gwtDevJar != null) {
+ System.err.println("Argument gwtUserJar must be provided for gwtDevJar to be used");
+ }
+ if (gwtUserJar != null) {
+ System.err.println("Argument gwtDevJar must be provided for gwtUserJar to be used");
+ }
+ return units;
+ }
+ // gwt-user.jar
+ Set<String> gwtIncludedPaths = new HashSet<String>(
+ Arrays.asList(new String[] {"com/google/gwt"}));
+ Set<String> gwtExcludedPaths = new HashSet<String>(
+ Arrays.asList(new String[] {
+ "com/google/gwt/benchmarks",
+ "com/google/gwt/i18n/rebind",
+ "com/google/gwt/i18n/tools",
+ "com/google/gwt/json",
+ "com/google/gwt/junit",
+ "com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java",
+ "com/google/gwt/user/rebind", "com/google/gwt/user/server",
+ "com/google/gwt/user/tools",}));
+ CompilationUnits cu = new JarFileCompilationUnits(
+ new JarFile[] {gwtUserJar}, gwtIncludedPaths, gwtExcludedPaths, logger);
+ units.addAll(cu.getCompilationUnits());
+
+ // gwt-dev-*.jar
+ gwtIncludedPaths = new HashSet<String>(Arrays.asList(new String[] {
+ "com/google/gwt/core/client",
+ "com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang",
+ "com/google/gwt/lang",}));
+ cu = new JarFileCompilationUnits(new JarFile[] {gwtDevJar},
+ gwtIncludedPaths, null, logger);
+ units.addAll(cu.getCompilationUnits());
+ return units;
+ }
+
+ /*
+ * excludedPackages is used in only one place: to determine whether some class
+ * is an api class or not
+ */
+ private Set<String> getSetOfExcludedPackages(Properties config) {
+ String allExcludedPackages = config.getProperty("excludedPackages");
+ if (allExcludedPackages == null) {
+ allExcludedPackages = "";
+ }
+ String excludedPackagesArray[] = allExcludedPackages.split(":");
+ return new HashSet<String>(Arrays.asList(excludedPackagesArray));
+ }
+
/**
* Each whiteList element is an {@link ApiElement} and
* {@link ApiChange.Status} separated by space. For example,
@@ -252,13 +884,8 @@
* method on {@link ApiElement}.
*
*/
- private static Set<String> readWhiteListFromFile(String fileName)
- throws IOException {
- if (fileName == null) {
- throw new IllegalArgumentException("fileName is null");
- }
+ private Set<String> readWhiteListFromFile(FileReader fr) throws IOException {
Set<String> hashSet = new HashSet<String>();
- FileReader fr = new FileReader(fileName);
BufferedReader br = new BufferedReader(fr);
String str = null;
while ((str = br.readLine()) != null) {
diff --git a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java
index 8aeb887..40319b4 100644
--- a/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java
+++ b/tools/api-checker/src/com/google/gwt/tools/apichecker/ApiContainer.java
@@ -27,26 +27,17 @@
import com.google.gwt.dev.javac.CompilationUnitInvalidator;
import com.google.gwt.dev.javac.JdtCompiler;
import com.google.gwt.dev.javac.TypeOracleMediator;
-import com.google.gwt.dev.javac.impl.FileCompilationUnit;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.IOException;
-import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import java.util.Set;
-import java.util.Vector;
/**
* {@link ApiContainer} Encapsulates an API.
@@ -57,91 +48,32 @@
private final Map<JClassType, Boolean> apiClassCache = new HashMap<JClassType, Boolean>();
private final Map<String, ApiPackage> apiPackages = new HashMap<String, ApiPackage>();
- private final Set<String> excludedFiles = new HashSet<String>();
- private final Set<String> excludedPackages = new HashSet<String>();
+ private final Set<String> excludedPackages;
private final TreeLogger logger;
-
private final String name;
- private int numFilesCount = 0;
- private Collection<File> sourceTrees = null;
private final TypeOracle typeOracle;
/**
- * A public constructor to create an {@code ApiContainer} from a config file.
+ * A public constructor used for programmatic invocation and testing.
*
- * @param fileName the config file
- * @param suffix The code looks for the values of the properties: dirRoot,
- * name, sourceFiles, excludedFiles, ending in "suffix"
- * @param logger The logger for the code.
+ * @param name Api name
+ * @param units a set of CompilationUnits
+ * @param excludedPackages a set of excludedPackages
+ * @param logger TreeLogger for logging messages
* @throws IllegalArgumentException if one of the arguments is illegal
* @throws UnableToCompleteException if there is a TypeOracle exception
*/
- public ApiContainer(String fileName, String suffix, TreeLogger logger)
- throws IllegalArgumentException, UnableToCompleteException {
+ ApiContainer(String name, Set<CompilationUnit> units,
+ Set<String> excludedPackages, TreeLogger logger)
+ throws UnableToCompleteException {
+ this.name = name;
this.logger = logger;
- FileInputStream fis = null;
- if (fileName == null) {
- throw new IllegalArgumentException("fileName is null");
- }
+ logger.log(TreeLogger.INFO, "name = " + name + ", units.size = " + units.size(), null);
try {
- fis = new FileInputStream(fileName);
- Properties config = new Properties();
- config.load(fis);
- String apiName = config.getProperty("name" + suffix);
- String allSourceFiles = config.getProperty("sourceFiles" + suffix);
- String allExcludedFiles = config.getProperty("excludedFiles" + suffix);
- if (allExcludedFiles == null) {
- allExcludedFiles = "";
- }
- String allExcludedPackages = config.getProperty("excludedPackages");
- if (allExcludedPackages == null) {
- allExcludedPackages = "";
- }
-
- if (apiName == null || allSourceFiles == null) {
- throw new IllegalArgumentException(
- "in apiContainer constructor, either name (" + apiName
- + ") or sourceFiles (" + allSourceFiles + ") is null");
- }
- logger.log(TreeLogger.DEBUG, "read from config file " + fileName
- + ", name = " + apiName + ", allSourceFiles = " + allSourceFiles
- + ", allExcludedFiles = " + allExcludedFiles
- + ", allExcludedPackages = " + allExcludedPackages, null);
-
- String dirRoot = config.getProperty("dirRoot" + suffix);
- if (dirRoot == null) {
- dirRoot = "";
- }
- String sourceFilesArray[] = allSourceFiles.split(":");
- Collection<File> fileCollection = new Vector<File>();
- for (String tempStr : sourceFilesArray) {
- tempStr = tempStr.trim();
- checkFileExistence("source file: ", dirRoot + tempStr);
- fileCollection.add(new File(dirRoot + tempStr));
- }
- logger.log(TreeLogger.DEBUG, "fileCollection " + fileCollection, null);
- this.sourceTrees = fileCollection;
-
- String excludedFilesArray[] = allExcludedFiles.split(":");
- for (String excludedFile : excludedFilesArray) {
- checkFileExistence("excluded file: ", dirRoot + excludedFile);
- }
- generateCanonicalFileSet(excludedFilesArray, dirRoot, excludedFiles);
-
- String excludedPackagesArray[] = allExcludedPackages.split(":");
- excludedPackages.addAll(Arrays.asList(excludedPackagesArray));
- this.name = apiName;
- this.typeOracle = createTypeOracleFromSources();
- initializeApiPackages();
- } catch (MalformedURLException e1) {
- throw new IllegalArgumentException(e1);
+ this.typeOracle = getTypeOracleFromCompilationUnits(units);
} catch (FileNotFoundException e2) {
- if (fis == null) {
- System.err.println("Have you specified the path of the config file correctly?");
- } else {
- System.err.println("Do you have a reference version of the API checked out?");
- }
+ System.err.println("Do you have a reference version of the API checked out?");
throw new IllegalArgumentException(e2);
} catch (IOException e3) {
throw new IllegalArgumentException(e3);
@@ -149,19 +81,7 @@
logger.log(TreeLogger.ERROR, "logged a NotFoundException", e4);
throw new UnableToCompleteException();
}
- }
-
- /**
- * Another public constructor. Used for programmatic invocation and testing.
- *
- * @param name
- * @param logger
- * @param typeOracle
- */
- ApiContainer(String name, TreeLogger logger, TypeOracle typeOracle) {
- this.name = name;
- this.logger = logger;
- this.typeOracle = typeOracle;
+ this.excludedPackages = excludedPackages;
initializeApiPackages();
}
@@ -241,65 +161,6 @@
return isApiClass(classType) && isSubclassable(classType);
}
- private void addCompilationUnitsInPath(Set<CompilationUnit> units,
- File sourcePathEntry) throws NotFoundException, IOException,
- UnableToCompleteException {
- logger.log(TreeLogger.SPAM, "entering addCompilationUnitsInPath, file = "
- + sourcePathEntry, null);
- File[] files = sourcePathEntry.listFiles();
- if (files == null) {
- // No files found.
- return;
- }
-
- for (int i = 0; i < files.length; i++) {
- final File file = files[i];
- logger.log(TreeLogger.SPAM, "deciding the fate of file " + file, null);
- // Ignore files like .svn and .cvs
- if (file.getName().startsWith(".") || file.getName().equals("CVS")) {
- continue;
- }
- if (isExcludedFile(file.getCanonicalPath())) {
- // do not process the subtree
- logger.log(TreeLogger.DEBUG, "not traversing "
- + file.toURL().toString(), null);
- continue;
- }
- if (file.isFile()) {
- String pkgName = null;
- if (file.getName().endsWith("java")) {
- pkgName = extractPackageNameFromFile(file);
- if (pkgName == null) {
- logger.log(TreeLogger.WARN, "Not adding file = " + file.toString()
- + ", because packageName = null", null);
- } else {
- logger.log(TreeLogger.DEBUG, "adding pkgName = " + pkgName
- + ", file = " + file.toString(), null);
- }
- }
- if (isValidPackage(pkgName, sourcePathEntry.toURL().toString())) {
- // Add if it's a source file and the package and fileNames are okay
- CompilationUnit unit = new FileCompilationUnit(file, pkgName);
- units.add(unit);
- numFilesCount++;
- } else {
- logger.log(TreeLogger.SPAM, " not adding file " + file.toURL(), null);
- }
- } else {
- // Recurse into subDirs
- addCompilationUnitsInPath(units, file);
- }
- }
- }
-
- private void checkFileExistence(String tag, String pathName)
- throws FileNotFoundException {
- File tempFile = new File(pathName);
- if (!tempFile.exists()) {
- throw new FileNotFoundException(tag + "file " + pathName + " not found");
- }
- }
-
/**
* Assumption: Clients may subclass an API class, but they will not add their
* class to the package.
@@ -330,13 +191,9 @@
return false;
}
- private TypeOracle createTypeOracleFromSources() throws NotFoundException,
- IOException, UnableToCompleteException {
- numFilesCount = 0;
- Set<CompilationUnit> units = new HashSet<CompilationUnit>();
- for (File tempFile : sourceTrees) {
- addCompilationUnitsInPath(units, tempFile);
- }
+ private TypeOracle getTypeOracleFromCompilationUnits(
+ Set<CompilationUnit> units) throws NotFoundException, IOException,
+ UnableToCompleteException {
JdtCompiler.compile(units);
if (CompilationUnitInvalidator.invalidateUnitsWithErrors(logger, units)) {
logger.log(TreeLogger.ERROR, "Unable to build typeOracle for "
@@ -347,55 +204,11 @@
TypeOracleMediator mediator = new TypeOracleMediator();
mediator.refresh(logger, units);
logger.log(TreeLogger.INFO, "API " + name
- + ", Finished with building typeOracle, added " + numFilesCount
+ + ", Finished with building typeOracle, added " + units.size()
+ " files", null);
return mediator.getTypeOracle();
}
- private String extractPackageNameFromFile(File file) {
- if (!file.exists()) {
- return null;
- }
- String fileName = null;
- try {
- fileName = file.toURL().toString();
- FileReader fr = new FileReader(file);
- BufferedReader br = new BufferedReader(fr);
- String str = null;
- while ((str = br.readLine()) != null) {
- if (str.indexOf("package") != 0) {
- continue;
- }
- String parts[] = str.split("[\b\t\n\r ]+");
- if ((parts.length == 2) && parts[0].equals("package")) {
- return parts[1].substring(0, parts[1].length() - 1); // the ; char
- }
- }
- return null;
- } catch (Exception ex) {
- logger.log(TreeLogger.ERROR,
- "error in parsing and obtaining the packageName from file "
- + fileName + "error's message " + ex.getMessage(), ex);
- return null;
- }
- }
-
- /**
- * Convert a set into a HashMap for faster lookups.
- */
- private void generateCanonicalFileSet(String strArray[], String dirRoot,
- Set<String> result) throws IOException {
- if (strArray == null) {
- return;
- }
- for (String str : strArray) {
- str = str.trim();
- File tempFile = new File(dirRoot + str);
- str = tempFile.getCanonicalPath();
- result.add(str);
- }
- }
-
private boolean hasPublicOrProtectedConstructor(JClassType classType) {
JConstructor[] constructors = classType.getConstructors();
for (JConstructor constructor : constructors) {
@@ -431,6 +244,8 @@
}
}
+
+
private boolean isAnySubtypeAnApiClass(JClassType classType) {
JClassType subTypes[] = classType.getSubtypes();
for (JClassType tempType : subTypes) {
@@ -469,14 +284,6 @@
return false;
}
- private boolean isExcludedFile(String fileName) {
- String pattern = "file:";
- if (fileName.indexOf(pattern) == 0) {
- fileName = fileName.substring(pattern.length());
- }
- return excludedFiles.contains(fileName);
- }
-
/**
* @return returns true if classType is public AND an outer class
*/
@@ -488,20 +295,4 @@
return !classType.isFinal() && hasPublicOrProtectedConstructor(classType);
}
- private boolean isValidPackage(String packageName, String filePath) {
- logger.log(TreeLogger.SPAM, "packageName = " + packageName
- + ", filePath = " + filePath, null);
- if (packageName == null) {
- return false;
- }
- int lastSlashPosition = filePath.lastIndexOf("/");
- if (lastSlashPosition == -1) {
- return false;
- }
- String dirPath = filePath.substring(0, lastSlashPosition);
- String packageNameAsPath = packageName.replace('.', '/');
- logger.log(TreeLogger.SPAM, "packageNameAsPath " + packageNameAsPath
- + ", dirPath = " + dirPath, null);
- return dirPath.endsWith(packageNameAsPath);
- }
}
diff --git a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java
index cc4cba9..54117a9 100644
--- a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java
+++ b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiCompatibilityTest.java
@@ -18,12 +18,14 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.dev.javac.CompilationUnit;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
-import com.google.gwt.tools.apichecker.ApiContainerTest.StaticCompilationUnit;
+import com.google.gwt.tools.apichecker.ApiCompatibilityChecker.StaticCompilationUnit;
import junit.framework.TestCase;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -59,7 +61,7 @@
getSourceForRuntimeException()),};
}
- private static char[] getSourceForApiClass() {
+ private static String getSourceForApiClass() {
StringBuffer sb = new StringBuffer();
sb.append("package test.apicontainer;\n");
sb.append("public class ApiClass extends NonApiClass {\n");
@@ -67,10 +69,10 @@
sb.append("\tpublic void checkParametersAndReturnTypes(java.lang.Object x) throws java.lang.Throwable { };\n");
sb.append("\tpublic final void checkParametersAndReturnTypesFinalVersion(java.lang.Object x) throws java.lang.Throwable { };\n");
sb.append("};\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForNonApiClass() {
+ private static String getSourceForNonApiClass() {
StringBuffer sb = new StringBuffer();
sb.append("package test.apicontainer;\n");
sb.append("class NonApiClass extends java.lang.Object {\n");
@@ -81,10 +83,10 @@
sb.append("\tprotected ApiClassInNonApiClass() { }\n");
sb.append("\t}\n");
sb.append("}\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForObject() {
+ private static String getSourceForObject() {
StringBuffer sb = new StringBuffer();
sb.append("package java.lang;\n");
sb.append("public class Object {\n");
@@ -97,10 +99,10 @@
sb.append("\tprivate int internalField = 0;\n");
sb.append("\tprotected static int protectedField=2;\n");
sb.append("}\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForOneMoreApiClass() {
+ private static String getSourceForOneMoreApiClass() {
StringBuffer sb = new StringBuffer();
sb.append("package test.apicontainer;\n");
sb.append("public class OneMoreApiClass extends java.lang.Object {\n");
@@ -108,32 +110,32 @@
sb.append("\tprotected final void checkOverloadedMethodAccounted(test.apicontainer.ApiClass b) throws java.lang.Throwable { }\n");
sb.append("\tpublic void testUncheckedExceptions() throws RuntimeException { }\n");
sb.append("};\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForRuntimeException() {
+ private static String getSourceForRuntimeException() {
StringBuffer sb = new StringBuffer();
sb.append("package java.lang;\n");
sb.append("public class RuntimeException extends Throwable {\n");
sb.append("}\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForTestClass() {
+ private static String getSourceForTestClass() {
StringBuffer sb = new StringBuffer();
sb.append("package test.nonapipackage;\n");
sb.append("class TestClass extends java.lang.Object {\n");
sb.append("\tpublic void method() { }\n");
sb.append("}\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForThrowable() {
+ private static String getSourceForThrowable() {
StringBuffer sb = new StringBuffer();
sb.append("package java.lang;\n");
sb.append("public class Throwable extends Object {\n");
sb.append("}\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
ApiContainer api1 = null;
@@ -145,15 +147,14 @@
AbstractTreeLogger logger = new PrintWriterTreeLogger();
logger.setMaxDetail(TreeLogger.ERROR);
- api1 = new ApiContainer("Api1", logger,
- ApiContainerTest.getNewTypeOracleFromCompilationUnits(
- ApiContainerTest.getScuArray(), logger));
- apiSameAs1 = new ApiContainer("Api2", logger,
- ApiContainerTest.getNewTypeOracleFromCompilationUnits(
- ApiContainerTest.getScuArray(), logger));
- api2 = new ApiContainer("Api2", logger,
- ApiContainerTest.getNewTypeOracleFromCompilationUnits(getScuArray(),
- logger));
+ api1 = new ApiContainer("Api1", new HashSet<CompilationUnit>(
+ Arrays.asList(ApiContainerTest.getScuArray())), new HashSet<String>(),
+ logger);
+ apiSameAs1 = new ApiContainer("ApiSameAs1", new HashSet<CompilationUnit>(
+ Arrays.asList(ApiContainerTest.getScuArray())), new HashSet<String>(),
+ logger);
+ api2 = new ApiContainer("Api2", new HashSet<CompilationUnit>(
+ Arrays.asList(getScuArray())), new HashSet<String>(), logger);
}
// setup is called before every test*. To avoid the overhead of setUp() each
diff --git a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java
index bf21c88..8bba24c 100644
--- a/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java
+++ b/tools/api-checker/test/com/google/gwt/tools/apichecker/ApiContainerTest.java
@@ -17,12 +17,10 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.javac.CompilationUnit;
-import com.google.gwt.dev.javac.JdtCompiler;
-import com.google.gwt.dev.javac.TypeOracleMediator;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.tools.apichecker.ApiCompatibilityChecker.StaticCompilationUnit;
import junit.framework.TestCase;
@@ -31,51 +29,10 @@
import java.util.Set;
/**
- * Test ApiContainer.
+ * Test {@link ApiContainer}.
*/
public class ApiContainerTest extends TestCase {
- static class StaticCompilationUnit extends CompilationUnit {
- private final char[] source;
- private final String typeName;
-
- public StaticCompilationUnit(String typeName, char[] source) {
- this.typeName = typeName;
- this.source = source;
- }
-
- @Override
- public String getDisplayLocation() {
- return "/mock/" + typeName;
- }
-
- @Override
- public long getLastModified() {
- return 0;
- }
-
- @Override
- public String getSource() {
- return String.valueOf(source);
- }
-
- @Override
- public String getTypeName() {
- return typeName;
- }
-
- @Override
- public boolean isGenerated() {
- return false;
- }
-
- @Override
- public boolean isSuperSource() {
- return false;
- }
- }
-
- @SuppressWarnings("unused")
class TestA {
public TestA(String args) {
}
@@ -104,19 +61,6 @@
}
}
- // TODO (amitmanjhi): Try using UnitTestTreeLogger to capture log messages
- public static TypeOracle getNewTypeOracleFromCompilationUnits(
- StaticCompilationUnit tempScuArray[], AbstractTreeLogger logger)
- throws UnableToCompleteException {
-
- TypeOracleMediator mediator = new TypeOracleMediator();
- Set<CompilationUnit> units = new HashSet<CompilationUnit>(
- Arrays.asList(tempScuArray));
- JdtCompiler.compile(units);
- mediator.refresh(logger, units);
- return mediator.getTypeOracle();
- }
-
public static StaticCompilationUnit[] getScuArray() {
return new StaticCompilationUnit[] {
new StaticCompilationUnit("test.apicontainer.ApiClass",
@@ -136,7 +80,7 @@
new ApiAbstractMethod[0])[0]).getMethod();
}
- private static char[] getSourceForApiClass() {
+ private static String getSourceForApiClass() {
StringBuffer sb = new StringBuffer();
sb.append("package test.apicontainer;\n");
sb.append("public class ApiClass extends NonApiClass {\n");
@@ -144,10 +88,10 @@
sb.append("\tpublic java.lang.Object checkParametersAndReturnTypes(ApiClass a) { return this; };\n");
sb.append("\tpublic final java.lang.Object checkParametersAndReturnTypesFinalVersion(ApiClass a) { return this; };\n");
sb.append("};\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForNewObject() {
+ private static String getSourceForNewObject() {
StringBuffer sb = new StringBuffer();
sb.append("package java.lang;\n");
sb.append("public class Object {\n");
@@ -156,10 +100,10 @@
sb.append("}\n");
sb.append("class Temp {\n");
sb.append("}");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForNonApiClass() {
+ private static String getSourceForNonApiClass() {
StringBuffer sb = new StringBuffer();
sb.append("package test.apicontainer;\n");
sb.append("class NonApiClass extends java.lang.Object {\n");
@@ -172,10 +116,10 @@
sb.append("\tprivate AnotherApiClassInNonApiClass() { }\n");
sb.append("\t}\n");
sb.append("}\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForObject() {
+ private static String getSourceForObject() {
StringBuffer sb = new StringBuffer();
sb.append("package java.lang;\n");
sb.append("public class Object {\n");
@@ -189,33 +133,33 @@
sb.append("\tprivate int internalField = 0;\n");
sb.append("\tprotected int protectedField=2;\n");
sb.append("}\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForOneMoreApiClass() {
+ private static String getSourceForOneMoreApiClass() {
StringBuffer sb = new StringBuffer();
sb.append("package test.apicontainer;\n");
sb.append("public class OneMoreApiClass extends java.lang.Object {\n");
sb.append("\tprotected final void checkOverloadedMethodAccounted(test.apicontainer.OneMoreApiClass b) { }\n");
sb.append("\tpublic void testUncheckedExceptions () { }\n");
sb.append("};\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForTest() {
+ private static String getSourceForTest() {
StringBuffer sb = new StringBuffer();
sb.append("package java.newpackage;\n");
sb.append("public class Test { }\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
- private static char[] getSourceForTestClass() {
+ private static String getSourceForTestClass() {
StringBuffer sb = new StringBuffer();
sb.append("package test.nonapipackage;\n");
sb.append("class TestClass extends java.lang.Object {\n");
sb.append("\tpublic void method() { }\n");
sb.append("}\n");
- return sb.toString().toCharArray();
+ return sb.toString();
}
ApiContainer apiCheck = null;
@@ -230,13 +174,15 @@
public void setUp() throws UnableToCompleteException {
AbstractTreeLogger logger = new PrintWriterTreeLogger();
logger.setMaxDetail(com.google.gwt.core.ext.TreeLogger.ERROR);
- apiCheckLoop = new ApiContainer("ApiClassTest", logger,
- getNewTypeOracleFromCompilationUnits(
- new StaticCompilationUnit[] {new StaticCompilationUnit(
- "java.lang.Object", getSourceForNewObject()),}, logger));
-
- apiCheck = new ApiContainer("ApiContainerTest", logger,
- getNewTypeOracleFromCompilationUnits(getScuArray(), logger));
+ apiCheckLoop = new ApiContainer(
+ "ApiClassTest",
+ new HashSet<CompilationUnit>(
+ Arrays.asList(new StaticCompilationUnit[] {new StaticCompilationUnit(
+ "java.lang.Object", getSourceForNewObject()),})),
+ new HashSet<String>(), logger);
+ apiCheck = new ApiContainer("ApiContainerTest",
+ new HashSet<CompilationUnit>(Arrays.asList(getScuArray())),
+ new HashSet<String>(), logger);
}
public void testEverything() {