package ru.naumen.gintonic.project.builder;
import static org.eclipse.core.resources.IResourceDelta.ADDED;
import static org.eclipse.core.resources.IResourceDelta.CHANGED;
import static org.eclipse.core.resources.IResourceDelta.REMOVED;
import java.util.List;
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.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.JavaCore;
import ru.naumen.gintonic.GinTonicPlugin;
import ru.naumen.gintonic.guice.GuiceIndex;
import ru.naumen.gintonic.guice.GuiceIndexSerializer;
import ru.naumen.gintonic.guice.GuiceIndexState;
import ru.naumen.gintonic.guice.GuiceModule;
import ru.naumen.gintonic.guice.analyzer.GuiceAnalyzer;
import ru.naumen.gintonic.utils.DateUtils;
import ru.naumen.gintonic.utils.IPathUtils;
import ru.naumen.gintonic.utils.IProjectUtils;
/**
* The {@link GinTonicProjectBuilder} creates and updates the the {@link GuiceIndex}
*
* @author tmajunke
*/
public class GinTonicProjectBuilder extends IncrementalProjectBuilder {
private static GuiceAnalyzer guiceIndexer = new GuiceAnalyzer();
public GinTonicProjectBuilder() {
super();
}
@Override
protected IProject[] build(int kind, @SuppressWarnings("rawtypes") Map args, IProgressMonitor monitor)
throws CoreException {
IProject project = getProject();
switch (kind) {
case IncrementalProjectBuilder.FULL_BUILD:
fullBuild(project, monitor);
break;
case IncrementalProjectBuilder.AUTO_BUILD:
incrementalBuild(project, monitor);
break;
case IncrementalProjectBuilder.CLEAN_BUILD:
cleanBuild();
break;
case IncrementalProjectBuilder.INCREMENTAL_BUILD:
incrementalBuild(project, monitor);
break;
default:
throw new RuntimeException("Unknown build type " + kind + "");
}
return null;
}
private void cleanBuild() {
GinTonicPlugin.logInfo("Clean build triggered!");
GuiceIndex.rebuild();
GuiceIndexSerializer.clear();
}
private void incrementalBuild(IProject project, IProgressMonitor monitor) throws CoreException {
IResourceDelta delta = getDelta(project);
delta.accept(new IndexGuiceModulesDeltaBuild(project, monitor));
}
private void fullBuild(IProject project, IProgressMonitor monitor) throws CoreException {
/* Reset the identified guice modules on full build. */
monitor.subTask("Indexing project " + project.getName() + ".");
long now = System.currentTimeMillis();
GuiceIndex guiceIndex = GuiceIndex.get();
guiceIndex.removeGuiceModulesByProjectName(project.getName());
int nrOfGuiceModulesBefore = guiceIndex.getNrOfGuiceModules();
List<IFile> files = IProjectUtils.getFilesInProjectSkippingBinary(project);
for (IFile iFile : files) {
if (monitor.isCanceled()) {
guiceIndex.setBuildState(GuiceIndexState.BUILD_INCOMPLETE);
GinTonicPlugin
.logWarning("Indexing Guice modules canceled by user request leaving the index in an incomplete state.");
break;
}
GuiceModule guiceModule = guiceIndexer.index(project, iFile);
if (guiceModule != null) {
guiceIndex.addGuiceModuleDontLog(guiceModule);
}
}
if (guiceIndex.getBuildState() != GuiceIndexState.BUILD_INCOMPLETE) {
guiceIndex.setBuildState(GuiceIndexState.BUILD_COMPLETE);
}
long then = System.currentTimeMillis();
long elapsed = then - now;
int nrOfGuiceModulesAdded = guiceIndex.getNrOfGuiceModules() - nrOfGuiceModulesBefore;
String message = "Indexing " + project.getName() + " finished in " + DateUtils.formatMilliseconds(elapsed)
+ " (+" + nrOfGuiceModulesAdded + " modules => " + guiceIndex.getNrOfGuiceModules()
+ " modules total).";
GinTonicPlugin.logInfo(message);
monitor.done();
}
static class IndexGuiceModulesDeltaBuild implements IResourceDeltaVisitor {
private IProject project;
private final IProgressMonitor monitor;
private IndexGuiceModulesDeltaBuild(IProject project, IProgressMonitor monitor) {
this.project = project;
this.monitor = monitor;
}
private static boolean VISIT_CHILDREN = true;
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
IResource resource = delta.getResource();
int resourceType = resource.getType();
/*
* We only process file changes... Wait! What if someone deletes a
* folder? No problem as we also get a notification for every
* deleted file.
*/
if (resourceType != IResource.FILE) {
return VISIT_CHILDREN;
}
IFile file = (IFile) resource;
IJavaElement javaElement = JavaCore.create(file);
if (javaElement == null) {
return VISIT_CHILDREN;
}
if (javaElement.getElementType() == IJavaElement.COMPILATION_UNIT) {
GuiceIndex guiceIndex = GuiceIndex.get();
GuiceModule guiceModule;
switch (delta.getKind()) {
case ADDED:
guiceModule = guiceIndexer.index(project, (IFile) resource);
if (guiceModule != null) {
monitor.subTask("Added Guice module " + guiceModule.getPrimaryTypeNameFullyQualified()
+ " to index.");
guiceIndex.addGuiceModule(guiceModule, true);
}
break;
case REMOVED:
removeModuleFromIndex(delta, guiceIndex);
break;
case CHANGED:
/*
* We have to check the module as maybe it's contents have
* significant changes (e.g removed an install(Module)
* statement).
*/
guiceModule = guiceIndexer.index(project, (IFile) resource);
if (guiceModule != null) {
monitor.subTask("Updated Guice module " + guiceModule.getPrimaryTypeNameFullyQualified() + ".");
guiceIndex.updateGuiceModule(guiceModule);
} else {
/*
* Maybe we could not analyze the module then we must
* remove it from the index. Otherwise we work on stale
* data.
*/
removeModuleFromIndex(delta, guiceIndex);
}
break;
}
}
return VISIT_CHILDREN;
}
public void removeModuleFromIndex(final IResourceDelta delta, GuiceIndex guiceIndex) {
IPath projectRelativePath = delta.getProjectRelativePath();
String javaClasspathString = IPathUtils.getRelativePathToJavaClasspathString(projectRelativePath);
guiceIndex.removeGuiceModule(javaClasspathString, true);
monitor.subTask("Removed Guice module " + javaClasspathString + " from index.");
}
}
}