/**
* Copyright (c) 2014 Takari, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package io.takari.maven.plugins.compile.javac;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import org.apache.maven.plugin.MojoExecutionException;
import io.takari.incrementalbuild.MessageSeverity;
import io.takari.incrementalbuild.Output;
import io.takari.incrementalbuild.Resource;
import io.takari.maven.plugins.compile.CompilerBuildContext;
import io.takari.maven.plugins.compile.ProjectClasspathDigester;
@Named(CompilerJavac.ID)
public class CompilerJavac extends AbstractCompilerJavac {
public static final String ID = "javac";
private static JavaCompiler compiler;
static synchronized JavaCompiler getSystemJavaCompiler() throws MojoExecutionException {
if (compiler == null) {
compiler = ToolProvider.getSystemJavaCompiler();
}
if (compiler == null) {
throw new MojoExecutionException("No compiler is provided in this environment. " + "Perhaps you are running on a JRE rather than a JDK?");
}
return compiler;
}
@Inject
public CompilerJavac(CompilerBuildContext context, ProjectClasspathDigester digester) {
super(context, digester);
}
@Override
public int compile(Map<File, Resource<File>> sources) throws MojoExecutionException, IOException {
if (sources.isEmpty()) {
return 0;
}
final JavaCompiler compiler = getSystemJavaCompiler();
final StandardJavaFileManager javaFileManager = compiler.getStandardFileManager(null, null, getSourceEncoding());
try {
compile(compiler, javaFileManager, sources);
} finally {
javaFileManager.flush();
javaFileManager.close();
}
return sources.size();
}
private void compile(JavaCompiler compiler, StandardJavaFileManager javaFileManager, Map<File, Resource<File>> sources) throws IOException {
final DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
final Iterable<? extends JavaFileObject> javaSources = javaFileManager.getJavaFileObjectsFromFiles(sources.keySet());
final Map<File, Output<File>> outputs = new HashMap<File, Output<File>>();
final Iterable<String> options = getCompilerOptions();
final RecordingJavaFileManager recordingFileManager = new RecordingJavaFileManager(javaFileManager, getSourceEncoding()) {
@Override
protected void record(File inputFile, File outputFile) {
outputs.put(outputFile, context.processOutput(outputFile));
}
};
Writer stdout = new PrintWriter(System.out, true);
final JavaCompiler.CompilationTask task = compiler.getTask(stdout, // Writer out
recordingFileManager, // file manager
diagnosticCollector, // diagnostic listener
options, //
null, // Iterable<String> classes to process by annotation processor(s)
javaSources);
final boolean success = task.call();
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnosticCollector.getDiagnostics()) {
final JavaFileObject source = diagnostic.getSource();
final MessageSeverity severity = toSeverity(diagnostic.getKind(), success);
final String message = diagnostic.getMessage(null);
if (isShowWarnings() || severity != MessageSeverity.WARNING) {
if (source != null) {
File file = FileObjects.toFile(source);
if (file != null) {
Resource<File> resource = sources.get(file);
if (resource == null) {
resource = outputs.get(file);
}
if (resource != null) {
resource.addMessage((int) diagnostic.getLineNumber(), (int) diagnostic.getColumnNumber(), message, severity, null);
} else {
log.warn("Unexpected java {} resource {}", source.getKind(), source.toUri().toASCIIString());
}
} else {
log.warn("Unsupported compiler message on {} resource {}: {}", source.getKind(), source.toUri(), message);
}
} else {
context.addPomMessage(message, severity, null);
}
}
}
}
private MessageSeverity toSeverity(Diagnostic.Kind kind, boolean success) {
// javac appears to report errors even when compilation was success.
// I was only able to reproduce this with annotation processing on java 6
// for consistency with forked mode, downgrade errors to warning here too
if (success && kind == Kind.ERROR) {
kind = Kind.WARNING;
}
MessageSeverity severity;
switch (kind) {
case ERROR:
severity = MessageSeverity.ERROR;
break;
case NOTE:
severity = MessageSeverity.INFO;
break;
default:
severity = MessageSeverity.WARNING;
break;
}
return severity;
}
@Override
protected String getCompilerId() {
return ID;
}
}