/*
* FindBugs Eclipse Plug-in.
* Copyright (C) 2003 - 2004, Peter Friese
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package de.tobject.findbugs.builder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.preference.IPreferenceStore;
import de.tobject.findbugs.FindBugsJob;
import de.tobject.findbugs.FindbugsPlugin;
import de.tobject.findbugs.preferences.FindBugsConstants;
import edu.umd.cs.findbugs.plugin.eclipse.util.MutexSchedulingRule;
/**
* The <code>FindBugsBuilder</code> performs a FindBugs run on a subset of the
* current project. It will either check all classes in a project or just the
* ones just having been modified.
*
* @author Peter Friese
* @version 1.0
* @since 25.9.2003
* @see IncrementalProjectBuilder
*/
public class FindBugsBuilder extends IncrementalProjectBuilder {
/** Controls debugging. */
public static boolean DEBUG;
/**
* Run the builder.
*
* @see IncrementalProjectBuilder#build
*/
@SuppressWarnings("rawtypes")
@Override
protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
monitor.subTask("Running FindBugs...");
switch (kind) {
case IncrementalProjectBuilder.FULL_BUILD: {
FindBugs2Eclipse.cleanClassClache(getProject());
if (FindbugsPlugin.getUserPreferences(getProject()).isRunAtFullBuild()) {
if (DEBUG) {
System.out.println("FULL BUILD");
}
doBuild(args, monitor, kind);
} else {
// TODO probably worth to cleanup?
// MarkerUtil.removeMarkers(getProject());
}
break;
}
case IncrementalProjectBuilder.INCREMENTAL_BUILD: {
if (DEBUG) {
System.out.println("INCREMENTAL BUILD");
}
doBuild(args, monitor, kind);
break;
}
case IncrementalProjectBuilder.AUTO_BUILD: {
if (DEBUG) {
System.out.println("AUTO BUILD");
}
doBuild(args, monitor, kind);
break;
}
default: {
FindbugsPlugin.getDefault()
.logWarning("UKNOWN BUILD kind" + kind);
doBuild(args, monitor, kind);
break;
}
}
return null;
}
/**
* Performs the build process. This method gets all files in the current
* project and has a <code>FindBugsVisitor</code> run on them.
*
* @param args
* A <code>Map</code> containing additional build parameters.
* @param monitor
* The <code>IProgressMonitor</code> displaying the build
* progress.
* @param kind
* kind the kind of build being requested, see
* IncrementalProjectBuilder
* @throws CoreException
*/
private void doBuild(final Map<?, ?> args, final IProgressMonitor monitor, int kind) throws CoreException {
boolean incremental = (kind != IncrementalProjectBuilder.FULL_BUILD);
IProject project = getProject();
IResource resource = project;
List<WorkItem> files;
if (incremental) {
IResourceDelta resourceDelta = getDelta(project);
boolean configChanged = !isConfigUnchanged(resourceDelta);
boolean fullBuildEnabled = FindbugsPlugin.getCorePreferences(getProject(), configChanged).isRunAtFullBuild();
if (configChanged && fullBuildEnabled) {
files = new ArrayList<WorkItem>();
files.add(new WorkItem(project));
} else {
files = ResourceUtils.collectIncremental(resourceDelta);
/*
* Here we expect to have only ONE file as a result of a
* post-save trigger. In this case incremental builder should
* run and analyse 1 file again. For some reason, JDT uses
* "AUTO" kind for such incremental compile. Unfortunately,
* Eclipse also triggers "AUTO" build on startup, if it detects
* that some project files are changed (I think also on team
* update operations). This causes sometimes a real startup
* slowdown for workspaces with many projects. Because we cannot
* distinguish between first and second build, and if
* "fullBuildEnabled" id OFF, we will analyse incrementally ONLY
* ONE SINGLE FILE. This is not nice, but there is no other ways
* today... May be we can use preferences to define "how many"
* is "incremental"...
*/
if (files.size() > 1) {
if (DEBUG) {
FindbugsPlugin.getDefault()
.logInfo(
"Incremental builder: too many resources to analyse for project " + project + ", files: "
+ files);
}
return;
} else if(files.size() == 1){
IResource corespondingResource = files.get(0).getCorespondingResource();
if(corespondingResource != null) {
resource = corespondingResource;
}
}
}
} else {
files = new ArrayList<WorkItem>();
files.add(new WorkItem(project));
}
work(resource, files, monitor);
}
/**
* Run a FindBugs analysis on the given resource as build job BUT not
* delaying the current Java build
*
* @param part
*
* @param resources
* The resource to run the analysis on.
* @param monitor
*/
protected void work(final IResource resource, final List<WorkItem> resources, IProgressMonitor monitor) {
IPreferenceStore store = FindbugsPlugin.getPluginPreferences(getProject());
boolean runAsJob = store.getBoolean(FindBugsConstants.KEY_RUN_ANALYSIS_AS_EXTRA_JOB);
FindBugsJob fbJob = new StartedFromBuilderJob("Finding bugs in " + resource.getName() + "...", resource, resources);
if(runAsJob) {
// run asynchronously, so there might be more similar jobs waiting to run
if (DEBUG) {
FindbugsPlugin.log("cancelSimilarJobs");
}
FindBugsJob.cancelSimilarJobs(fbJob);
if (DEBUG) {
FindbugsPlugin.log("scheduleAsSystem");
}
fbJob.scheduleAsSystem();
if (DEBUG) {
FindbugsPlugin.log("done scheduleAsSystem");
}
} else {
// run synchronously (in same thread)
if (DEBUG) {
FindbugsPlugin.log("running fbJob");
}
fbJob.run(monitor);
if (DEBUG) {
FindbugsPlugin.log("done fbJob");
}
}
}
private boolean isConfigUnchanged(IResourceDelta resourceDelta) {
return resourceDelta != null && resourceDelta.findMember(new Path(".project")) == null
&& resourceDelta.findMember(new Path(".classpath")) == null
&& resourceDelta.findMember(FindbugsPlugin.DEPRECATED_PREFS_PATH) == null
&& resourceDelta.findMember(FindbugsPlugin.DEFAULT_PREFS_PATH) == null;
}
private final static class StartedFromBuilderJob extends FindBugsJob {
private final List<WorkItem> resources;
private StartedFromBuilderJob(String name, IResource resource, List<WorkItem> resources) {
super(name, resource);
this.resources = resources;
}
@Override
protected boolean supportsMulticore(){
return MutexSchedulingRule.MULTICORE;
}
@Override
protected void runWithProgress(IProgressMonitor monitor) throws CoreException {
FindBugsWorker worker = new FindBugsWorker(getResource(), monitor);
worker.work(resources);
}
}
}