package edu.ucsd.arcum.interpreter.query;
import static edu.ucsd.arcum.ArcumPlugin.DEBUG;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Display;
import edu.ucsd.arcum.EclipseUtil;
import edu.ucsd.arcum.util.StringUtil;
// ProjectTraverser
public class ProjectTraverser
{
public interface ICompilationUnitVisitor
{
void visitCompilationUnit(CompilationUnit compilationUnit);
}
private static Map<String, CompilationUnit> cachedParsedASTs;
static {
cachedParsedASTs = new HashMap<String, CompilationUnit>();
}
private final IProject project;
private final IJavaProject javaProject;
private final String message;
private IProgressMonitor monitor;
private int totalFilesSeen;
// initialized when runTraversal is called
private IResourceProxyVisitor resourceVisitor;
private IRunnableWithProgress runnable;
public ProjectTraverser(IProject project, String message) {
this.project = project;
this.javaProject = JavaCore.create(project);
this.message = message;
this.monitor = null;
this.totalFilesSeen = 0;
}
public void runTraversal(ICompilationUnitVisitor visitor) {
this.resourceVisitor = new ProjectTraverser.ResourceVisitor(visitor);
this.runnable = new ProjectTraverser.RunnableWithProgress();
Display.getDefault().syncExec(new Runnable() {
public void run() {
ProgressMonitorDialog dialog;
dialog = new ProgressMonitorDialog(EclipseUtil.getShell());
try {
ProjectTraverser.this.totalFilesSeen = 0;
dialog.run(true, false, ProjectTraverser.this.runnable);
if (DEBUG)
System.out.printf("Saw %d files%n", totalFilesSeen);
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
public static void markSourceFileDirty(String filePath) {
if (DEBUG) {
if (cachedParsedASTs.containsKey(filePath)) {
System.out.printf("Deleting cached parse for %s%n", filePath);
}
}
cachedParsedASTs.remove(filePath);
}
private class ResourceVisitor implements IResourceProxyVisitor
{
private ICompilationUnitVisitor visitor;
public ResourceVisitor(ICompilationUnitVisitor visitor) {
this.visitor = visitor;
}
@Override public boolean visit(IResourceProxy proxy) throws CoreException {
if (proxy.getType() == IResource.FILE) {
IFile file = (IFile)proxy.requestResource();
if (isJavaSourceFile(file)) {
++totalFilesSeen;
if (monitor != null) {
monitor.subTask(file.getName());
}
CompilationUnit compilationUnit = parseJavaSource(file);
visitor.visitCompilationUnit(compilationUnit);
if (monitor != null) {
monitor.worked(1);
}
}
}
return true;
}
}
private class RunnableWithProgress implements IRunnableWithProgress
{
@Override public void run(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException
{
try {
project.accept(new IResourceProxyVisitor() {
public boolean visit(IResourceProxy resource) throws CoreException {
if (resource.getType() == IResource.FILE) {
IFile file = (IFile)resource.requestResource();
if (isJavaSourceFile(file)) {
++totalFilesSeen;
}
}
return true;
}
}, 0);
// URGENT: for real, need to see if project has errors
String userMessage;
userMessage = String.format("Arcum: Analyzing \"%s\"", project.getName());
if (message != null && !message.equals("")) {
userMessage = String.format("%s - %s", userMessage, message);
}
monitor.beginTask(userMessage, totalFilesSeen);
ProjectTraverser.this.monitor = monitor;
project.accept(ProjectTraverser.this.resourceVisitor, 0);
monitor.done();
}
catch (CoreException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
private boolean isJavaSourceFile(IFile file) throws CoreException {
IContentDescription contentDescription = file.getContentDescription();
if (contentDescription != null) {
IContentType type = contentDescription.getContentType();
String id = type.getId();
if (id.equals(JavaCore.JAVA_SOURCE_CONTENT_TYPE)) {
boolean onClasspath = javaProject.isOnClasspath(file);
return onClasspath;
}
}
return false;
}
private CompilationUnit parseJavaSource(IFile file) {
String filePath = file.toString();
CompilationUnit result = cachedParsedASTs.get(filePath);
if (result == null) {
IJavaProject project = JavaCore.create(file.getProject());
ASTParser parser = ASTParser.newParser(AST.JLS3);
ICompilationUnit source = JavaCore.createCompilationUnitFrom(file);
parser.setProject(project); // NOTE: this line might not be needed
parser.setSource(source); // sets compiler options too
parser.setResolveBindings(true);
result = (CompilationUnit)parser.createAST(null);
cachedParsedASTs.put(filePath, result);
if (DEBUG) {
AST ast = result.getAST();
System.out.printf("Parsed %s, result=%d [%s]%n", file.toString(), System
.identityHashCode(result), StringUtil.firstLine(result.toString()));
}
}
return result;
}
}