/*
Copyright (c) 2009-2013 Olivier Chafik, All Rights Reserved
This file is part of JNAerator (http://jnaerator.googlecode.com/).
JNAerator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
JNAerator 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with JNAerator. If not, see <http://www.gnu.org/licenses/>.
*/
package com.ochafik.lang.compiler;
import java.util.*;
import java.util.regex.Pattern;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import javax.tools.*;
import javax.tools.Diagnostic.Kind;
import com.ochafik.io.IOUtils;
import com.nativelibs4java.jalico.Adapter;
import com.ochafik.util.string.RegexUtils;
import com.ochafik.util.string.StringUtils;
public class CompilerUtils {
public static class CompilationError extends IOException {
private static final long serialVersionUID = 4002559714569792833L;
public final String compilerClass;
//public final String bootclasspath;
public final List<Diagnostic<? extends JavaFileObject>> diagnostics;
public final Map<String, MemoryJavaFile> inputs;
private CompilationError(String text, List<Diagnostic<? extends JavaFileObject>> diagnostics, Map<String, MemoryJavaFile> inputs, String compilerClass/*, String bootclasspath*/) {
super(text);
this.diagnostics = diagnostics;
this.inputs = inputs;
this.compilerClass = compilerClass;
//this.bootclasspath = bootclasspath;
}
public static void throwErrors(List<Diagnostic<? extends JavaFileObject>> diagnostics, Map<String, MemoryJavaFile> inputs, String compilerClass/*, String bootclasspath*/) throws CompilationError, IOException {
List<Diagnostic<? extends JavaFileObject>> errors = new ArrayList<Diagnostic<? extends JavaFileObject>>(diagnostics.size());
StringBuilder sb = new StringBuilder();
for (final Diagnostic<? extends JavaFileObject> diagnostic : diagnostics) {
if (diagnostic == null) {
continue;
}
if (diagnostic.getKind() == Kind.ERROR) {
errors.add(diagnostic);
sb.append("Error in " + (diagnostic.getSource() == null ? "?" : diagnostic.getSource().toUri()) + " at line " + diagnostic.getLineNumber() + ", col " + diagnostic.getColumnNumber() + " :\n\t" + diagnostic.getMessage(Locale.getDefault()) + "\n");//.toUri());
if (diagnostic.getSource() != null) {
sb.append(RegexUtils.regexReplace(Pattern.compile("\n"), "\n" + diagnostic.getSource().getCharContent(true), new Adapter<String[], String>() {
int line = 0;
@Override
public String adapt(String[] value) {
line++;
return "\n" + line + ":" + (diagnostic.getLineNumber() == line ? ">>>" : "") + "\t\t";
}
}) + "\n");
}
}
//System.out.println("Error on line " + diagnostic.getLineNumber() + ":" + diagnostic.getColumnNumber() + " in " + (diagnostic.getSource() == null ? "<unknown source>" : diagnostic.getSource().getName()) + ": " + diagnostic.getMessage(Locale.getDefault()));
}
if (errors.isEmpty()) {
return;
}
System.err.println("INPUTS = " + inputs);
throw new CompilationError(sb.toString(), errors, inputs, compilerClass/*, bootclasspath*/);
}
}
public static String getClassPath(Class<?> c, File cacheDirectory) throws MalformedURLException, IOException {
URL resource = c.getResource(c.getSimpleName() + ".class");
if (resource != null) {
String resstr = resource.toString();
// if (resstr.contains("Prog/"))
// resstr = "jar:http://ochafik.com/Java/jnaerator.jar!/...";
if (resstr.matches("jar:.*!.*")) {
resstr = resstr.substring("jar:".length(), resstr.indexOf("!"));
} else {
String p = '/' + c.getName().replace('.', '/') + ".class";
if (resstr.endsWith(p)) {
resstr = resstr.substring(0, resstr.length() - p.length());
}
}
return getLocalFile(new URL(resstr), cacheDirectory).toString();
}
/*
if (resource != null) {
String resstr = resource.toString();
if (resstr.matches("jar:file:.*!.*"))
return resstr.substring("jar:file:".length(), resstr.indexOf("!"));
else if (resstr.matches("jar:http:.*!.*"))
return resstr.substring("jar:".length(), resstr.indexOf("!"));
else {
String p = '/' + c.getName().replace('.', '/') + ".class";
if (resstr.endsWith(p))
return resstr.substring(0, resstr.length() - p.length());
}
}*/
return null;
}
public static Set<String> getClassPaths(File cacheDirectory, Class<?>... cs) throws MalformedURLException, IOException {
Set<String> ret = new TreeSet<String>();
for (Class<?> c : cs) {
String cp;
if (c == null || (cp = getClassPath(c, cacheDirectory)) == null) {
continue;
}
ret.add(cp);
}
return ret;
}
static Map<String, File> localURLCaches = new LinkedHashMap<String, File>();
static File getLocalFile(URL remoteFile, File cacheDirectory) throws IOException {
if ("file".equals(remoteFile.getProtocol())) {
return new File(URLDecoder.decode(remoteFile.getFile(), "utf-8"));
}
String remoteStr = remoteFile.toString();
File f = localURLCaches.get(remoteStr);
if (f == null) {
String fileName = new File(remoteStr).getName();
URLConnection con = null;
try {
con = remoteFile.openConnection();
} catch (IOException ex) {
ex.printStackTrace();
}
if (cacheDirectory != null) {
f = new File(cacheDirectory, fileName);
if (f.exists() && (con == null || f.lastModified() > con.getLastModified())) {
System.out.println("Reusing cached file " + f);
if (con != null) {
con.getInputStream().close();
}
return f;
}
} else {
f = File.createTempFile(fileName, ".jar");
f.deleteOnExit();
}
System.out.print("Downloading file " + remoteFile + " to " + f);
InputStream in = new BufferedInputStream(remoteFile.openStream());
try {
OutputStream out = new BufferedOutputStream(new FileOutputStream(f));
try {
//System.out.print("Downloading file '" + remoteStr + "'...");
long length = IOUtils.readWrite(in, out);
System.out.println(" OK (" + length + " bytes)");
localURLCaches.put(remoteStr, f.getAbsoluteFile());
} finally {
out.close();
}
} finally {
in.close();
}
}
return f;
}
public static void compile(JavaCompiler compiler, MemoryFileManager fileManager, DiagnosticCollector<JavaFileObject> diagnostics, String sourceCompatibility, File cacheDirectory, Class<?>... classpathHints) throws MalformedURLException, IOException {
//JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//System.out.println("compiler = " + (compiler == null ? "<none found>" : compiler.getClass().getName()));
String bootclasspath = StringUtils.implode(getClassPaths(cacheDirectory, String.class), File.pathSeparator);
String classpath = StringUtils.implode(getClassPaths(cacheDirectory, classpathHints), File.pathSeparator);
// System.out.println("bootclasspath = " + bootclasspath);
// System.out.println("classpath = " + classpath);
Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects();
List<String> options = sourceCompatibility == null ? null : Arrays.asList(
"-target", sourceCompatibility,
"-source", sourceCompatibility,
"-bootclasspath", bootclasspath,
"-classpath", classpath);
// DebugUtils.println(fileManager.inputs.values());
compiler.getTask(null, fileManager, diagnostics, options, null, fileObjects).call();
// for (final Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
// if (diagnostic == null)
// continue;
// //diagnostic.getKind()
// //System.out.format("Error on line %d in %d%n", diagnostic.getLineNumber(), diagnostic.getSource());//.toUri());
// if (diagnostic.getKind() == Kind.ERROR) {
// System.err.println("\n" + diagnostic.getSource().toUri() + ":");
// System.err.println(RegexUtils.regexReplace(Pattern.compile("\n"), "\n" + diagnostic.getSource().getCharContent(true), new Adapter<String[], String>() {
// int line = 0;
//
// @Override
// public String adapt(String[] value) {
// line++;
// return "\n" + line + ":" + (diagnostic.getLineNumber() == line ? ">>>" : "") +"\t\t";
// }
// }));
// }
//// System.out.println("Error on line " + diagnostic.getLineNumber() + ":" + diagnostic.getColumnNumber() + " in " + (diagnostic.getSource() == null ? "<unknown source>" : diagnostic.getSource().getName()) + ": " + diagnostic.getMessage(Locale.getDefault()));
// }
}
public static JavaCompiler getJavaCompiler(boolean preferJavac) throws FileNotFoundException {
JavaCompiler compiler;
if (preferJavac) {
compiler = ToolProvider.getSystemJavaCompiler();
if (compiler != null) {
return compiler;
}
}
try {
compiler = (JavaCompiler) Class.forName("org.eclipse.jdt.internal.compiler.tool.EclipseCompiler").newInstance();
} catch (Exception e) {
compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
throw new FileNotFoundException("No Java compiler found (not run from JDK, no Eclipse Compiler in classpath)");
}
}
return compiler;
}
public static void main2(String[] args) {
try {
String jarOut = args.length == 0 ? "out.jar" : args[0];
JavaCompiler compiler = getJavaCompiler(false);
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
MemoryFileManager fileManager = new MemoryFileManager(compiler.getStandardFileManager(diagnostics, null, null));
fileManager.addSourceInput("test/Main.java", "package test; public class Main { }");
fileManager.close();
compile(compiler, fileManager, diagnostics, null, null);
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
//diagnostic.getKind()
//System.out.format("Error on line %d in %d%n", diagnostic.getLineNumber(), diagnostic.getSource());//.toUri());
System.out.format("Error on line " + diagnostic.getLineNumber() + ":" + diagnostic.getLineNumber() + " in " + diagnostic.getSource());//.toUri());
}
boolean outputSources = true;
System.out.println("Writing " + jarOut + (outputSources ? " with" : " without") + " sources");
fileManager.writeJar(new FileOutputStream(jarOut), outputSources, null);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}