package org.checkerframework.eclipse.actions; import com.sun.tools.javac.util.Pair; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.checkerframework.eclipse.CheckerPlugin; import org.checkerframework.eclipse.javac.CheckersRunner; import org.checkerframework.eclipse.javac.CommandlineJavacRunner; import org.checkerframework.eclipse.javac.JavacError; import org.checkerframework.eclipse.javac.JavacRunner; import org.checkerframework.eclipse.util.*; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; //TODO: RENAME THIS TO CHECKER JOB public class CheckerWorker extends Job { private final IJavaProject project; private final String checkerNames; private String[] sourceFiles; private final String javacJreVersion = "1.8.0"; private final boolean useJavacRunner; private final boolean hasQuals; /** * This constructor is intended for use from an incremental builder that has a list of updated * source files to check * * @param project * @param sourceFiles * @param checkerNames */ public CheckerWorker( IJavaProject project, String[] sourceFiles, String checkerNames, boolean hasQuals) { super("Running checker on " + sourceFiles.toString()); this.project = project; this.sourceFiles = sourceFiles; this.checkerNames = checkerNames; this.useJavacRunner = shouldUseJavacRunner(); this.hasQuals = hasQuals; } public CheckerWorker(List<IJavaElement> elements, String checkerNames, boolean hasQuals) { super("Running checker on " + PluginUtil.join(",", elements)); this.project = elements.get(0).getJavaProject(); this.checkerNames = checkerNames; this.useJavacRunner = shouldUseJavacRunner(); this.hasQuals = hasQuals; try { this.sourceFiles = ResourceUtils.sourceFilesOf(elements).toArray(new String[] {}); } catch (CoreException e) { CheckerPlugin.logException(e, e.getMessage()); } } private boolean shouldUseJavacRunner() { // int expectedLength = "1.x.x".length(); // final String jreVersion = // System.getProperties().getProperty("java.runtime.version").substring(0, expectedLength); // return jreVersion.equals(javacJreVersion); return false; } @Override protected IStatus run(IProgressMonitor monitor) { try { work(monitor); } catch (Throwable e) { CheckerPlugin.logException(e, "Analysis exception"); return Status.CANCEL_STATUS; } return Status.OK_STATUS; } private void work(final IProgressMonitor pm) throws CoreException { if (checkerNames != null) { pm.beginTask( "Running checker(s) " + checkerNames.toString() + " on " + sourceFiles.toString(), 10); } else { pm.beginTask("Running custom single checker " + " on " + sourceFiles.toString(), 10); } pm.setTaskName("Removing old markers"); MarkerUtil.removeMarkers(project.getResource()); pm.worked(1); pm.setTaskName("Running checker"); List<JavacError> callJavac = runChecker(); pm.worked(6); pm.setTaskName("Updating problem list"); markErrors(project, callJavac); pm.worked(3); pm.done(); } private List<JavacError> runChecker() throws JavaModelException { final Pair<String, String> classpaths = classPathOf(project); final CheckersRunner runner; if (useJavacRunner) { runner = new JavacRunner( sourceFiles, checkerNames.split(","), classpaths.fst + File.pathSeparator + classpaths.snd, hasQuals); } else { runner = new CommandlineJavacRunner( sourceFiles, checkerNames.split(","), classpaths.fst, classpaths.snd, hasQuals); } runner.run(); return runner.getErrors(); } /** * Mark errors for this project in the appropriate files * * @param project */ private void markErrors(IJavaProject project, List<JavacError> errors) { for (JavacError error : errors) { if (error.file == null) { continue; } IResource file = ResourceUtils.getFile(project, error.file); if (file == null) continue; MarkerUtil.addMarker( error.message, project.getProject(), file, error.lineNumber, error.errorKey, error.errorArguments, error.startPosition, error.endPosition); } } private Pair<List<String>, List<String>> pathOf(IClasspathEntry cp, IJavaProject project) throws JavaModelException { int entryKind = cp.getEntryKind(); switch (entryKind) { case IClasspathEntry.CPE_SOURCE: return new Pair<List<String>, List<String>>( Arrays.asList(new String[] {ResourceUtils.outputLocation(cp, project)}), new ArrayList<String>()); case IClasspathEntry.CPE_LIBRARY: return new Pair<List<String>, List<String>>( Arrays.asList(new String[] {Paths.absolutePathOf(cp)}), new ArrayList<String>()); case IClasspathEntry.CPE_PROJECT: return projectPathOf(cp); case IClasspathEntry.CPE_CONTAINER: List<String> resultPaths = new ArrayList<String>(); List<String> resultBootPaths = new ArrayList<String>(); IClasspathContainer c = JavaCore.getClasspathContainer(cp.getPath(), project); if (c.getKind() == IClasspathContainer.K_DEFAULT_SYSTEM || c.getKind() == IClasspathContainer.K_SYSTEM) { for (IClasspathEntry entry : c.getClasspathEntries()) { if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { resultBootPaths.add( entry.getPath().makeAbsolute().toFile().getAbsolutePath()); } } } else { for (IClasspathEntry entry : c.getClasspathEntries()) { if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { resultPaths.add( entry.getPath().makeAbsolute().toFile().getAbsolutePath()); } } } return new Pair<List<String>, List<String>>(resultPaths, resultBootPaths); case IClasspathEntry.CPE_VARIABLE: return pathOf(JavaCore.getResolvedClasspathEntry(cp), project); } return new Pair<List<String>, List<String>>( new ArrayList<String>(), new ArrayList<String>()); } /** * Returns the project's classpath in a format suitable for javac * * @param project * @return the project's classpath as a string * @throws JavaModelException */ private Pair<String, String> classPathOf(IJavaProject project) throws JavaModelException { Pair<List<String>, List<String>> paths = classPathEntries(project); return new Pair<String, String>( PluginUtil.join(File.pathSeparator, paths.fst), PluginUtil.join(File.pathSeparator, paths.snd)); } private Pair<List<String>, List<String>> classPathEntries(IJavaProject project) throws JavaModelException { final Pair<List<String>, List<String>> results = new Pair<List<String>, List<String>>( new ArrayList<String>(), new ArrayList<String>()); for (IClasspathEntry cp : project.getResolvedClasspath(true)) { Pair<List<String>, List<String>> paths = pathOf(cp, project); results.fst.addAll(paths.fst); results.snd.addAll(paths.snd); } return results; } private Pair<List<String>, List<String>> projectPathOf(IClasspathEntry entry) throws JavaModelException { final IProject project = ResourceUtils.workspaceRoot().getProject(entry.getPath().toOSString()); return classPathEntries(JavaCore.create(project)); } }