I got a strange case. In the given database, I got a record that has a VARCHAR
field, so in my entity model I added this field, plus getters and setters. Now is the point where the fun starts:
The string below is in fact a body of a method. It looks like this:
if (score <= 0.7) {return 0;} else if (score <=0.8) {return 1;} else if (score <=0.9) {return 2;} else {return 3}
and now — I need to make this string into a method, and to tell the truth I have no clue how to achieve this. This actual method needs to get from me a score as a double, and return an integer value depending on given score.
Anyone?
I know that the easiest way is to not to use solutions like this, but it's not my call, besides, there's a plenty of records like this in the database, and every record is different. We cannot move this out. I have to deal with this — so please, solution ideas only, and I promise that I will do all the hating, and saying it's stupid :)
Trust me, I am, and I do, and I will be doing complaining for quite some time.
You convert a string to a number by calling the Parse or TryParse method found on numeric types ( int , long , double , and so on), or by using methods in the System. Convert class. It's slightly more efficient and straightforward to call a TryParse method (for example, int.
Look at the java scripting API
int eval(String code) {
code = "function f () {" + code + "} f()"; // Or otherwise
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.put("score", 1.4);
Number num = (Number) engine.eval(code);
return num.intValue();
}
Here I assumed, that JavaScript syntax is possible; had to fill in score
, you might
somewhere intercept all unknown free variables.
You should look at the Java Scripting API. There are a couple of projects which implement this API to allow you to add the ability to run scripts your Java code.
The best way to do it is using the Scripting API as most answerers already pointed out. You can run JavaScript from there. If you want to dare more and try to run Java code (and get pointed at as an eretic) you can try to compile code with JavaCompiler
and the run it using a class loader.
BEWARE: This is one of the most ORRIFIC ways to use Java. You should use this only to debug and only as last hope. In no way at all you should use this in a production environment.
This will create a DontTryThisAtHome.java
file in your classpath. Remeber to use this strategy only for fun and very few other cases:
public class MyClass{
public static void main(String[] args) throws Exception {
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager sjfm = jc.getStandardFileManager(null, null, null);
File jf = new File("DontTryThisAtHome.java");
PrintWriter pw = new PrintWriter(jf);
pw.println("public class DontTryThisAtHome{"
+ "static final int score = " + <your_score> + ";"
+ "public static void main(){}"
+ "public int getValue(){"
+ " return score<=0.7?0:score<=0.8?1: score<=0.9?2:3"
+ "}
+ "}");
pw.close();
Iterable<? extends JavaFileObject> fO = sjfm.getJavaFileObjects(jf);
if(!jc.getTask(null,sjfm,null,null,null,fO).call()) {
throw new Exception("compilation failed");
}
URL[] urls = new URL[]{new File("").toURI().toURL()};
URLClassLoader ucl = new URLClassLoader(urls);
Object o= ucl.loadClass("DontTryThisAtHome").newInstance();
int result = (int) o.getClass().getMethod("getValue").invoke(o);
ucl.close();
}
}
I think a better solution is to change your architecture, but If you are forced to it try the following :) This code you can find here
public class DynamicProxy {
public interface CalculateScore {
double calculate(double score);
}
private static class JavaSourceFromString extends SimpleJavaFileObject {
final String code;
JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
private static class JavaClassObject extends SimpleJavaFileObject {
protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
public JavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
}
public byte[] getBytes() {
return bos.toByteArray();
}
@Override
public OutputStream openOutputStream() throws IOException {
return bos;
}
}
private static class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
private JavaClassObject classObject;
private final String className;
public ClassFileManager(StandardJavaFileManager standardManager, String className) {
super(standardManager);
this.className = className;
}
@Override
public ClassLoader getClassLoader(Location location) {
return new SecureClassLoader(DynamicProxy.class.getClassLoader()) {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.contains(className)) {
byte[] b = classObject.getBytes();
return super.defineClass(name, classObject.getBytes(), 0, b.length);
}
return super.findClass(name);
}
};
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, javax.tools.JavaFileObject.Kind kind, FileObject sibling) throws IOException {
classObject = new JavaClassObject(className, kind);
return classObject;
}
}
private static class MyInvocationHandler implements InvocationHandler {
private final String className;
private final String classBody;
public MyInvocationHandler(String implClassName, String classBody) {
this.className = implClassName;
this.classBody = classBody;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> clazz = compileClass(className, classBody);
return method.invoke(clazz.newInstance(), args);
}
}
private static Class<?> compileClass(String className, String classBody) throws Throwable {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
JavaFileObject file = new JavaSourceFromString(className, classBody);
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
JavaFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null), className);
CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
boolean success = task.call();
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
System.out.println(diagnostic.getCode());
System.out.println(diagnostic.getKind());
System.out.println(diagnostic.getPosition());
System.out.println(diagnostic.getStartPosition());
System.out.println(diagnostic.getEndPosition());
System.out.println(diagnostic.getSource());
System.out.println(diagnostic.getMessage(null));
}
if (success) {
return fileManager.getClassLoader(null).loadClass(className);
}
return null;
}
@SuppressWarnings("unchecked")
public static <T> T newProxyInstance(Class<T> clazz, String className, String classBody) {
ClassLoader parentLoader = DynamicProxy.class.getClassLoader();
return (T) Proxy.newProxyInstance(parentLoader, new Class[] { clazz }, new MyInvocationHandler(className, classBody));
}
public static CalculateScore create(String body) {
StringWriter writer = new StringWriter();
PrintWriter out = new PrintWriter(writer);
out.println("public class CalculateScoreImpl implements pl.softech.stackoverflow.javac.DynamicProxy.CalculateScore {");
out.println(" public double calculate(double score) {");
out.println(body);
out.println(" }");
out.println("}");
out.close();
return newProxyInstance(CalculateScore.class, "CalculateScoreImpl", writer.toString());
}
public static void main(String[] args) {
CalculateScore calculator = create("if (score <= 0.7) {return 0;} else if (score <=0.8) {return 1;} else if (score <=0.9) {return 2;} else {return 3; }");
System.out.println(calculator.calculate(0.3));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With