java/ch/wlkl/wsh/JavaCmpLoad.java
package ch.wlkl.wsh;
import static ch.wlkl.wsh.Top.sFail;
import java.io.File;
import java.io.FileInputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import javax.tools.Tool;
public class JavaCmpLoad<T> extends ClassLoader {
public final JavaCmpLoad<?> parent;
public final File root;
public final boolean temporary;
public final Object pckg;
public final Class<T> superCla;
private int uniqueNum = 0;
private Compile compile = makeCompile();
public JavaCmpLoad(JavaCmpLoad<?> pa, Class<T> sup) {
this(pa, null, null, null, sup);
}
@SuppressWarnings("unchecked")
public JavaCmpLoad(JavaCmpLoad<?> par, String rt, Boolean tmp, Object pac, Class<T> sup) {
root = Ut.canonical(new File(rt != null ? rt : par != null ? par.root.getAbsolutePath() : "."));
temporary = tmp != null ? tmp : par != null ? par.temporary : false;
pckg = pac != null ? pac : par != null ? par.pckg : "temporary.shell";
superCla = sup != null ? sup : par != null ? (Class<T>) par.superCla : null;
while (par != null && par.parent != null)
par = par.parent;
parent = par;
if (! (root.exists() && root.isDirectory()))
root.mkdirs();
}
public String toString() {
return this.getClass().getName() + "(" + (parent == null ? "uniqueNum=" + uniqueNum : "parent.uniqueNum=" + parent.uniqueNum)+ ", root=" + root + ", temporary=" + temporary + ")";
}
public static String className(Object o) {
try {
return (String) o;
} catch (ClassCastException e) {
return ((Class<?>) o).getSimpleName();
}
}
/** the imports for each class, an element i of the array is translated to the following source:
* <ul>
* <li> import ((String) i) ;
* <li> import (((Class) i).getName()) ;
* <li> import (((Package) i).getName()).* ;
* </ul> */
public static CharSequence[] classSource(Object pk, Object[] imports, String cla, Object sup, Object[] implem, CharSequence... body) {
ArrayList<CharSequence> l = new ArrayList<CharSequence>();
String pr = null;
l.add("package " + pk + ';');
for (Object i: imports) {
try {
pr = (String) i;
} catch (ClassCastException e1) {
try {
pr = ((Class<?>) i).getName();
} catch (ClassCastException e2) {
try {
pr = ((Package) i).getName() + ".*";
} catch (ClassCastException e3) {
sFail("bad javaImport[?] " + i);
}
}
}
l.add("\nimport " + pr + ';');
}
l.add("\npublic class " + cla);
if (sup != null && ! sup.equals(""))
l.add(" extends " + className(sup));
pr = " implements ";
for (Object e : implem) {
if (e != null && ! e.equals("")) {
l.add(pr + className(e));
pr = ", ";
}
}
l.add(" {");
for (CharSequence s: body) {
if (s != null) {
if (s.length() > 0 && s.charAt(0) != '\n')
l.add("\n");
l.add(s);
}
}
l.add("\n}");
return l.toArray(new CharSequence[l.size()]);
}
public CharSequence[] classSource(Object[] imports, String cla, Object[] implem, CharSequence... body) {
return classSource(pckg, imports, cla, superCla, implem, body);
}
@SuppressWarnings("unchecked")
public <C> Class<? extends C> makeClass(File rt, Boolean tmp, Object pk, String cla, Class<C> sup, CharSequence... source) {
int dx = cla.lastIndexOf('.');
rt = rt == null ? root : rt;
sup = sup == null ? (Class<C>) superCla : sup;
if (dx >= 0) {
pk = cla.substring(0, dx);
cla = cla.substring(dx+1);
}
tmp = tmp != null ? tmp : pk != null ? false : temporary;
pk = pk == null ? pckg : pk;
File dir = new File(rt, pk.toString().replace('.', '/'));
File srcFile = new File(dir, cla + ".java");
File claFile = new File(dir, cla + ".class");
if (! dir.isDirectory())
if (! dir.mkdirs())
sFail("could not create directory " + dir);
try {
srcFile.delete();
claFile.delete();
PrintStream ps = new PrintStream(srcFile);
for(CharSequence s : source)
ps.print(s);
ps.println("");
ps.close();
// String args = "-source 5.0 -warn:-unchecked,unused " + srcFile.getAbsolutePath();
Compile compile = makeCompile();
boolean res = compile.compile(srcFile, "");
// boolean res = org.eclipse.jdt.internal.compiler.batch.Main.compile(args);
// int res = com.sun.tools.javac.Main.compile(args, new java.io.PrintWriter(System.out));
if (! res )
sFail("compile rc " + res + " errors for class " + pk + '.' + cla);
FileInputStream f = new FileInputStream(claFile);
int len = f.available();
byte[] byteCode = new byte[len];
f.read(byteCode);
f.close();
Class<? extends C> cl = defineClass(pk.toString() + '.' + cla, byteCode, 0, len).asSubclass(sup);
if (tmp) {
srcFile.delete();
claFile.delete();
}
return cl;
} catch (Exception e) {
e.printStackTrace();
sFail("define class " + pk + '.' + cla + " in dir " + dir);
return null;
}
}
public boolean compile(File src, String opt) {
try {
return compile.compile(src, opt);
} catch (Exception e) {
System.out.println("error: compile caught exception: " + e);
return false;
}
}
public Class<? extends T> makeClass(String cla, CharSequence... source) {
return makeClass(null, null, null, cla, null, source);
}
public int uniqueNum() {
return parent == null ? ++uniqueNum : parent.uniqueNum();
}
public static String uniqueName(String pref, int i) {
return pref + i;
}
public String uniqueName(String pref) {
return uniqueName(pref, uniqueNum());
}
public Compile makeCompile () {
String s = "";
try {
return new CompileEclipse();
} catch (Throwable e) {
s += "\nEclipse: " + e;
}
try {
return new CompileSun();
} catch (Throwable e) {
s += "\nSun: " + e;
}
try {
return new CompileTool();
} catch (Throwable e) {
s += "\nTool: " + e;
}
sFail("no compiler found " + s);
return null;
}
abstract class Compile extends Top {
abstract public boolean compile(File src, String opt) throws Exception;
}
class CompileTool extends Compile {
public boolean compile(File src, String opt) throws Exception {
String[] args = ("-source 8 -Xlint:-unchecked -d " + root + ' ' + opt + ' ' +src.getAbsolutePath()).split(" +");
Tool comp = javax.tools.ToolProvider.getSystemJavaCompiler();
int res = comp.run(null, null, null, args);
return res == 0;
}
}
class CompileEclipse extends Compile {
final Method met;
public CompileEclipse() throws Throwable {
// compiler im März12 in /usr/share/eclipse362/plugins/org.eclipse.jdt.core_3.6.2.v_A76_R36x.jar
// compiler im Jan13 in /usr/lib/eclipse/plugins/org.eclipse.jdt.core_3.7.3.dist.jar
met = Class.forName("org.eclipse.jdt.internal.compiler.batch.Main").getDeclaredMethod("compile", new Class[] {String.class});
if (met.getReturnType() != Boolean.TYPE)
fail("bad returnType " + met.getReturnType() + " for method " + met);
}
public boolean compile(File src, String opt) throws Exception {
String args = "-source 8.0 -warn:-unchecked,unused " + opt + ' ' + src.getAbsolutePath();
return (Boolean) met.invoke(null, (Object) args);
}
}
class CompileSun extends Compile {
final Method met;
public CompileSun()throws Throwable {
met = Class.forName("com.sun.tools.javac.Main").getDeclaredMethod("compile", String[].class, java.io.PrintWriter.class ); //, new Class[] {String.class});
if (met.getReturnType() != Integer.TYPE)
fail("bad returnType " + met.getReturnType() + " for method " + met);
}
public boolean compile(File src, String opt) throws Exception {
return 0 == (Integer) met.invoke(null, new String[] {src.getAbsolutePath()}, new java.io.PrintWriter(System.out));
}
}
}