Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transform string into code in Java

Tags:

java

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?


EDIT

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.

like image 260
Lukas Novicky Avatar asked Jul 28 '14 12:07

Lukas Novicky


People also ask

How do I convert a string to a number?

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.


4 Answers

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.

like image 160
Joop Eggen Avatar answered Oct 14 '22 03:10

Joop Eggen


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.

like image 45
Aaron Digulla Avatar answered Oct 14 '22 05:10

Aaron Digulla


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();
}
}
like image 31
Narmer Avatar answered Oct 14 '22 05:10

Narmer


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));
    }

}
like image 31
slavik Avatar answered Oct 14 '22 03:10

slavik