import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author qbit
*/
public class DynamicCompiler {
private final DynamicClassLoader classLoader;
public ClassLoader getClassLoader(){
return classLoader;
}
public DynamicCompiler(ClassLoader classLoader){
this.classLoader=new DynamicClassLoader(classLoader);
}
public Class<?> compile(String className,String src){
JavaCompiler compiler= ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<javax.tools.JavaFileObject> diagnosticCollector=new DiagnosticCollector<>();
var standardFileManager=compiler.getStandardFileManager(diagnosticCollector,null,null);
var javaFileManager=new JavaFileManager(standardFileManager);
var files= Lists.newArrayList(new JavaFileObject(className,src));
// var classpath= Joiner.on(File.pathSeparator).join(Stream.of(getParentClassloader().getURLs()).map(URL::getFile).collect(Collectors.toList()));
// var options=Lists.newArrayList("-encoding", Charsets.UTF_8.toString(),"-classpath",classpath);
var options=Lists.newArrayList("-encoding", Charsets.UTF_8.toString());
var task=compiler.getTask(null,javaFileManager,diagnosticCollector,options,null,files);
if(task.call()){
return javaFileManager.toClass(classLoader);
}else{
String message="compile "+className+" fail!";
System.err.println(message);
System.err.println(src);
diagnosticCollector.getDiagnostics().forEach(diagnostic -> {
System.err.println("Code:" + diagnostic.getCode());
System.err.println("Kind:" + diagnostic.getKind());
System.err.println("Position:" + diagnostic.getPosition());
System.err.println("Start Position:" + diagnostic.getStartPosition());
System.err.println("End Position:" + diagnostic.getEndPosition());
System.err.println("Source:" + diagnostic.getSource());
System.err.println("Message:" + diagnostic.getMessage(null));
System.err.println("LineNumber:" + diagnostic.getLineNumber());
System.err.println("ColumnNumber:" + diagnostic.getColumnNumber());
});
throw new RuntimeException(message);
}
}
private class JavaFileObject extends SimpleJavaFileObject{
private final CharSequence content;
protected JavaFileObject(String className, CharSequence content) {
super(URI.create("string:///" + className.replace('.', '/')
+ JavaFileObject.Kind.SOURCE.extension), JavaFileObject.Kind.SOURCE);
this.content = content;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return content;
}
}
private class JavaFileManager extends ForwardingJavaFileManager<javax.tools.JavaFileManager> {
private JavaClassObject javaFileObject;
protected JavaFileManager(javax.tools.JavaFileManager fileManager) {
super(fileManager);
}
@Override
public javax.tools.JavaFileObject getJavaFileForOutput(Location location, String className, javax.tools.JavaFileObject.Kind kind, FileObject sibling) throws IOException {
javaFileObject=new JavaClassObject(className,kind);
return javaFileObject;
}
public Class<?> toClass(DynamicClassLoader classLoader) {
return javaFileObject.toClass(classLoader);
}
}
private class JavaClassObject extends SimpleJavaFileObject{
private final ByteArrayOutputStream bos=new ByteArrayOutputStream();
private final String name;
protected JavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/')
+ kind.extension), kind);
this.name=name;
}
@Override
public OutputStream openOutputStream() throws IOException {
return bos;
}
Class<?> toClass(DynamicClassLoader dynamicClassLoader){
return dynamicClassLoader.defineClass(name,bos.toByteArray());
}
}
private class DynamicClassLoader extends URLClassLoader {
public DynamicClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
public Class<?> defineClass(String name,byte[] bytes){
return defineClass(name,bytes,0,bytes.length);
}
}
}