package edu.umd.cs.findbugs;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.objectweb.asm.ClassReader;
import edu.umd.cs.findbugs.ba.ClassNotFoundExceptionParser;
import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.IClassFactory;
import edu.umd.cs.findbugs.classfile.IClassPath;
import edu.umd.cs.findbugs.classfile.IClassPathBuilder;
import edu.umd.cs.findbugs.classfile.IClassPathBuilderProgress;
import edu.umd.cs.findbugs.classfile.ICodeBaseEntry;
import edu.umd.cs.findbugs.classfile.IErrorLogger;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
import edu.umd.cs.findbugs.classfile.engine.ClassParserUsingASM;
import edu.umd.cs.findbugs.classfile.impl.ClassFactory;
public class DiscoverSourceDirectories {
private static boolean DEBUG = SystemProperties.getBoolean("findbugs.dsd.debug");
public interface Progress extends IClassPathBuilderProgress {
public void startRecursiveDirectorySearch();
public void doneRecursiveDirectorySearch();
public void startScanningArchives(int numArchivesToScan);
public void doneScanningArchives();
public void startScanningClasses(int numClassesToScan);
public void finishClass();
public void doneScanningClasses();
}
private static class NoOpErrorLogger implements IErrorLogger {
@Override
public void reportMissingClass(ClassNotFoundException ex) {
}
@Override
public void reportMissingClass(ClassDescriptor classDescriptor) {
}
@Override
public void logError(String message) {
}
@Override
public void logError(String message, Throwable e) {
}
@Override
public void reportSkippedAnalysis(MethodDescriptor method) {
}
}
private static class NoOpProgress implements Progress {
@Override
public void startScanningArchives(int numArchivesToScan) {
}
@Override
public void doneScanningArchives() {
}
@Override
public void startScanningClasses(int numClassesToScan) {
}
@Override
public void finishClass() {
}
@Override
public void doneScanningClasses() {
}
@Override
public void finishArchive() {
}
@Override
public void startRecursiveDirectorySearch() {
}
@Override
public void doneRecursiveDirectorySearch() {
}
@Override
public void startArchive(String name) {
}
}
private Project project;
private String rootSourceDirectory;
private boolean scanForNestedArchives;
private IErrorLogger errorLogger;
private Progress progress;
private final List<String> discoveredSourceDirectoryList;
public DiscoverSourceDirectories() {
this.errorLogger = new NoOpErrorLogger();
this.progress = new NoOpProgress();
this.discoveredSourceDirectoryList = new LinkedList<String>();
}
public void setProject(Project project) {
this.project = project;
}
public void setRootSourceDirectory(String rootSourceDirectory) {
this.rootSourceDirectory = rootSourceDirectory;
}
public void setScanForNestedArchives(boolean scanForNestedArchives) {
this.scanForNestedArchives = scanForNestedArchives;
}
public void setErrorLogger(IErrorLogger errorLogger) {
this.errorLogger = errorLogger;
}
public void setProgress(Progress progress) {
this.progress = progress;
}
public List<String> getDiscoveredSourceDirectoryList() {
return Collections.unmodifiableList(discoveredSourceDirectoryList);
}
public void execute() throws CheckedAnalysisException, IOException, InterruptedException {
File dir = new File(rootSourceDirectory);
if (!dir.isDirectory()) {
throw new IOException("Path " + rootSourceDirectory + " is not a directory");
}
progress.startRecursiveDirectorySearch();
RecursiveFileSearch rfs = new RecursiveFileSearch(rootSourceDirectory, new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
});
rfs.search();
progress.doneRecursiveDirectorySearch();
List<String> candidateSourceDirList = rfs.getDirectoriesScanned();
IClassPath classPath = null;
try {
IClassFactory factory = ClassFactory.instance();
IClassPathBuilder builder = factory.createClassPathBuilder(errorLogger);
classPath = buildClassPath(builder, factory);
List<String> fullyQualifiedSourceFileNameList = findFullyQualifiedSourceFileNames(builder, classPath);
if (DEBUG) {
System.out.println("looking for " + fullyQualifiedSourceFileNameList.size() + " files");
}
findSourceDirectoriesForAllSourceFiles(fullyQualifiedSourceFileNameList, candidateSourceDirList);
} finally {
if (classPath != null) {
classPath.close();
}
}
}
private IClassPath buildClassPath(IClassPathBuilder builder, IClassFactory factory) throws InterruptedException, IOException,
CheckedAnalysisException {
progress.startScanningArchives(project.getFileCount());
for (String path : project.getFileList()) {
builder.addCodeBase(factory.createFilesystemCodeBaseLocator(path), true);
}
for (String path : project.getAuxClasspathEntryList()) {
builder.addCodeBase(factory.createFilesystemCodeBaseLocator(path), false);
}
IClassPath classPath = factory.createClassPath();
builder.build(classPath, progress);
progress.doneScanningArchives();
return classPath;
}
private String findFullyQualifiedSourceFileName(IClassPath classPath, ClassDescriptor classDesc) throws IOException,
CheckedAnalysisException {
try {
ICodeBaseEntry codeBaseEntry = classPath.lookupResource(classDesc.toResourceName());
ClassParserUsingASM classParser = new ClassParserUsingASM(new ClassReader(codeBaseEntry.openResource()), classDesc,
codeBaseEntry);
ClassInfo.Builder classInfoBuilder = new ClassInfo.Builder();
classParser.parse(classInfoBuilder);
ClassInfo classInfo = classInfoBuilder.build();
String packageName = classDesc.getPackageName();
String sourceFile = classInfo.getSource();
if (!"".equals(packageName)) {
packageName = packageName.replace('.', '/');
packageName += "/";
}
String fullyQualifiedSourceFile = packageName + sourceFile;
return fullyQualifiedSourceFile;
} catch (CheckedAnalysisException e) {
errorLogger.logError("Could scan class " + classDesc.toDottedClassName(), e);
throw e;
} finally {
progress.finishClass();
}
}
private List<String> findFullyQualifiedSourceFileNames(IClassPathBuilder builder, IClassPath classPath) {
List<ClassDescriptor> appClassList = builder.getAppClassList();
progress.startScanningClasses(appClassList.size());
List<String> fullyQualifiedSourceFileNameList = new LinkedList<String>();
for (ClassDescriptor classDesc : appClassList) {
try {
String fullyQualifiedSourceFileName = findFullyQualifiedSourceFileName(classPath, classDesc);
fullyQualifiedSourceFileNameList.add(fullyQualifiedSourceFileName);
} catch (IOException e) {
errorLogger.logError("Couldn't scan class " + classDesc.toDottedClassName(), e);
} catch (CheckedAnalysisException e) {
errorLogger.logError("Couldn't scan class " + classDesc.toDottedClassName(), e);
}
}
progress.doneScanningClasses();
return fullyQualifiedSourceFileNameList;
}
private void findSourceDirectoriesForAllSourceFiles(List<String> fullyQualifiedSourceFileNameList,
List<String> candidateSourceDirList) {
Set<String> sourceDirsFound = new HashSet<String>();
for (String fullyQualifiedSourceFileName : fullyQualifiedSourceFileNameList) {
checkCandidateSourceDirs: for (String candidateSourceDir : candidateSourceDirList) {
String path = candidateSourceDir + File.separatorChar + fullyQualifiedSourceFileName;
File f = new File(path);
if (DEBUG) {
System.out.print("Checking " + f.getPath() + "...");
}
boolean found = f.exists() && !f.isDirectory();
if (DEBUG) {
System.out.println(found ? "FOUND" : "not found");
}
if (found) {
if (sourceDirsFound.add(candidateSourceDir)) {
discoveredSourceDirectoryList.add(candidateSourceDir);
sourceDirsFound.add(candidateSourceDir);
}
break checkCandidateSourceDirs;
}
}
}
}
public static void main(String[] args) throws IOException, CheckedAnalysisException, InterruptedException {
if (args.length != 2) {
System.err.println("Usage: " + DiscoverSourceDirectories.class.getName() + " <project file> <root source dir>");
System.exit(1);
}
Project project = Project.readProject(args[0]);
IErrorLogger errorLogger = new IErrorLogger() {
@Override
public void reportMissingClass(ClassNotFoundException ex) {
String className = ClassNotFoundExceptionParser.getMissingClassName(ex);
if (className != null) {
logError("Missing class: " + className);
} else {
logError("Missing class: " + ex);
}
}
@Override
public void reportMissingClass(ClassDescriptor classDescriptor) {
logError("Missing class: " + classDescriptor.toDottedClassName());
}
@Override
public void logError(String message) {
System.err.println("Error: " + message);
}
@Override
public void logError(String message, Throwable e) {
logError(message + ": " + e.getMessage());
}
@Override
public void reportSkippedAnalysis(MethodDescriptor method) {
logError("Skipped analysis of method " + method.toString());
}
};
DiscoverSourceDirectories.Progress progress = new DiscoverSourceDirectories.Progress() {
@Override
public void startRecursiveDirectorySearch() {
System.out.print("Scanning directories...");
System.out.flush();
}
@Override
public void doneRecursiveDirectorySearch() {
System.out.println("done");
}
@Override
public void startScanningArchives(int numArchivesToScan) {
System.out.print("Scanning " + numArchivesToScan + " archives..");
System.out.flush();
}
@Override
public void doneScanningArchives() {
System.out.println("done");
}
@Override
public void startScanningClasses(int numClassesToScan) {
System.out.print("Scanning " + numClassesToScan + " classes...");
System.out.flush();
}
@Override
public void finishClass() {
System.out.print(".");
System.out.flush();
}
@Override
public void doneScanningClasses() {
System.out.println("done");
}
@Override
public void finishArchive() {
System.out.print(".");
System.out.flush();
}
@Override
public void startArchive(String name) {
}
};
DiscoverSourceDirectories discoverSourceDirectories = new DiscoverSourceDirectories();
discoverSourceDirectories.setProject(project);
discoverSourceDirectories.setRootSourceDirectory(args[1]);
discoverSourceDirectories.setErrorLogger(errorLogger);
discoverSourceDirectories.setProgress(progress);
discoverSourceDirectories.execute();
System.out.println("Found source directories:");
for (String srcDir : discoverSourceDirectories.getDiscoveredSourceDirectoryList()) {
System.out.println(" " + srcDir);
}
}
}