package org.checkerframework.eclipse.javac;
import com.sun.source.util.JavacTask;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.file.JavacFileManager;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;
import org.checkerframework.eclipse.CheckerPlugin;
import org.checkerframework.eclipse.prefs.CheckerPreferences;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import org.osgi.framework.Bundle;
/**
* This class is used to run the checker from the Sun Compiler API rather than using the
* commandline.
*
* @author asumu
*/
public class JavacRunner implements CheckersRunner {
public static final String CHECKERS_JAR_LOCATION = "lib/checkers.jar";
public static final String JAVAC_LOCATION = "lib/javac.jar";
public static final String JDK_LOCATION = "lib/jdk.jar";
public static final List<String> IMPLICIT_ARGS =
Arrays.asList("checkers.nullness.quals.*", "checkers.interning.quals.*");
private final Iterable<String> fileNames;
private final Iterable<String> processors;
private final String classpath;
private final DiagnosticCollector<JavaFileObject> collector;
private final boolean hasQuals;
public JavacRunner(
String[] fileNames, String[] processors, String classpath, boolean hasQuals) {
this.collector = new DiagnosticCollector<JavaFileObject>();
this.fileNames = Arrays.asList(fileNames);
this.processors = Arrays.asList(processors);
this.classpath = classpath;
this.hasQuals = hasQuals;
}
/**
* Runs the compiler on the selected files using the given processor
*
* @param fileNames files that need to be type-checked
* @param processors Type processors to run
* @param classpath The classpath to reference in compilation
*/
public void run() {
Iterable<String> opts;
opts = getOptions(processors, classpath);
// The following code uses the compiler's internal APIs, which are
// volatile. (see warning in JavacTool source)
JavacTool tool = JavacTool.create();
JavacFileManager manager = null; // tool.getStandardFileManager(collector, null, null);
Iterable<? extends JavaFileObject> fileObjs =
manager.getJavaFileObjectsFromStrings(fileNames);
CheckerPlugin.getDefault();
MessageConsole console = CheckerPlugin.findConsole();
MessageConsoleStream stream = console.newMessageStream();
Writer writer = new OutputStreamWriter(stream);
JavacTask task = tool.getTask(writer, manager, collector, opts, null, fileObjs);
task.call();
manager.close();
}
private Iterable<String> getOptions(Iterable<String> processors, String classpath) {
List<String> opts = new ArrayList<String>();
opts.add("-verbose");
opts.add("-proc:only");
try {
opts.add(
"-Xbootclasspath/p:"
+ getLocation(JAVAC_LOCATION)
+ ":"
+ getLocation(JDK_LOCATION)
+ ":");
} catch (IOException e) {
CheckerPlugin.logException(e, e.getMessage());
}
opts.add("-XprintProcessorInfo");
opts.add("-Xprefer:source");
// Build the processor arguments, comma separated
StringBuilder processorStr = new StringBuilder();
Iterator<String> itr = processors.iterator();
while (itr.hasNext()) {
processorStr.append(itr.next());
if (itr.hasNext()) {
processorStr.append(",");
}
}
opts.add("-processor");
opts.add(processorStr.toString());
// Processor options
addProcessorOptions(opts);
// Classpath
opts.add("-cp");
opts.add(classpath);
return opts;
}
/**
* Add options for type processing from the preferences
*
* @param opts
*/
private void addProcessorOptions(List<String> opts) {
IPreferenceStore store = CheckerPlugin.getDefault().getPreferenceStore();
String skipUses = store.getString(CheckerPreferences.PREF_CHECKER_A_SKIP_CLASSES);
if (!skipUses.isEmpty()) {
opts.add("-AskipUses=" + skipUses);
}
String lintOpts = store.getString(CheckerPreferences.PREF_CHECKER_A_LINT);
if (!lintOpts.isEmpty()) {
opts.add("-Alint=" + lintOpts);
}
if (store.getBoolean(CheckerPreferences.PREF_CHECKER_A_WARNS)) opts.add("-Awarns");
if (store.getBoolean(CheckerPreferences.PREF_CHECKER_A_NO_MSG_TEXT))
opts.add("-Anomsgtext");
if (store.getBoolean(CheckerPreferences.PREF_CHECKER_A_SHOW_CHECKS))
opts.add("-Ashowchecks");
if (store.getBoolean(CheckerPreferences.PREF_CHECKER_A_FILENAMES)) opts.add("-Afilenames");
if (store.getBoolean(CheckerPreferences.PREF_CHECKER_IMPLICIT_IMPORTS) && hasQuals) {
StringBuilder builder = new StringBuilder();
for (String annClass : IMPLICIT_ARGS) {
builder.append(annClass);
builder.append(":");
}
// chop off the last :
builder.setLength(builder.length() - 1);
builder.trimToSize();
// TODO: this is disabled for now since it doesn't work
// opts.add("-J-Djsr308_imports=\"" + builder.toString() + "\"");
}
}
private String getLocation(String path) throws IOException {
Bundle bundle = Platform.getBundle(CheckerPlugin.PLUGIN_ID);
Path javacJAR = new Path(path);
URL javacJarURL = FileLocator.toFileURL(FileLocator.find(bundle, javacJAR, null));
return javacJarURL.getPath();
}
public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() {
return collector.getDiagnostics();
}
public List<JavacError> getErrors() {
List<Diagnostic<? extends JavaFileObject>> diagnostics = getDiagnostics();
List<JavacError> javacErrors = new ArrayList<JavacError>();
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics) {
if (diagnostic.getSource() != null) {
javacErrors.add(new JavacError(diagnostic));
} else { //TODO: TEST PRINTING THIS TO THE CONSOLE
System.out.println(
"No source for diagnostic at: "
+ diagnostic.getLineNumber()
+ " Message "
+ diagnostic.getMessage(null));
}
}
return javacErrors;
}
}