Java允许用户创建和执行算法

时间:2014-11-26 14:50:52

标签: java algorithm graph

我正在创建一个程序(可执行文件),允许用户使用图形并在其上执行算法。基本上用户应该能够创建一个新的算法,例如Dijkstra的修改,并在图表上进行尝试。但是,我不确定什么是最好的编程方式。

目前我正在考虑允许用户编写java代码并将其保存在单独的目录中。但是,我不知道这是否可能,因为你必须在执行程序时实时编译java代码。这种方法可行吗?

另一种方法是创建一个解释器并允许用户编写伪代码,然后将其保存并转换为程序可以理解的内容。但是,如果可以完成上一个方法,那么它可能比这个方法更容易。

第一种方法可行吗?如果是这样,它比第二个好吗?或者有更简单的方法吗?

3 个答案:

答案 0 :(得分:1)

您可以使用现有的解释器,而不是创建解释器并重新发明轮子,例如:

对于每个库,您可以选择将某些类作为变量公开给基础语言。

答案 1 :(得分:0)

Java虚拟机具有嵌入式JavaScript engine,您可以将其用于您的目的。您可以在运行时加载应用程序并执行脚本。此外,您可以轻松地注入自己的对象,实现您需要的任何功能。

答案 2 :(得分:0)

有几个选项,每个选项都有不同的努力和可能性。您可以编写自己的解释器,或使用现有的iterpreter,或使用不同的条带化语言并使用其中一个内置脚本引擎。

但是,回到初衷,让用户编写 Java代码,还有一个选项:当安装了JDK时,可以使用Java工具JDK。 (请注意,这不适用于 JRE )。

特别是,您可以使用JavaCompiler类动态编译Java类。

我明确地 NOT 想要说这是您问题的最合适的解决方案。我只是想说这是一个选项,你确实可以动态编译Java代码。

当我想要链接到一个示例(或复制并粘贴一个,带有归因)时,我注意到我在网上找到的示例plainly don't workused temporary files,或者仅关注关于JavaCompiler的某些特定方面及其用法(想象一下这里的博客和StackOverflow答案列表)。

我做了找到一个MCVE,它展示了如何编译一个类的源代码(甚至更少:多个类!),并加载生成的类,完全在 - 记忆。所以我创建了这样一个实用程序类:InMemoryCompiler类可以接收类名列表和相应的源代码,并从类名返回一个映射到Class<?>个对象。 可以那么简单,应该就这么简单。也许有人觉得这很有用。

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;

public class JavaCompilerExample
{
    public static void main(String[] args)
    {
        String classNameA = "ExampleClassA";
        String sourceA = 
            "public class " + classNameA + " {" + "\n" +
            "    public static void main(String args[]) {" + "\n" +
            "        System.out.println(\"Hello, compiler!\");" + "\n" +
            "        ExampleClassB b = new ExampleClassB();" + "\n" +
            "        b.someMethod();" + "\n" +
            "    }" + "\n" + 
            "}" + "\n";

        String classNameB = "ExampleClassB";
        String sourceB = 
            "public class " + classNameB + " {" + "\n" +
            "    public void someMethod() {" + "\n" +
            "        System.out.println(\"Some method was called\");" + "\n" +
            "    }" + "\n" + 
            "}" + "\n";

        InMemoryCompiler c = new InMemoryCompiler();
        Map<String, Class<?>> classes = c.compile(
            Arrays.asList(classNameA, classNameB), 
            Arrays.asList(sourceA, sourceB));

        try
        {
            Class<?> exampleClass = classes.get(classNameA);
            Method m = exampleClass.getDeclaredMethod("main",
                new Class[] { String[].class });
            m.invoke(null, (Object) null);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

class InMemoryCompiler
{
    private final Map<String, ByteArrayJavaFileObject> classFileObjects;
    private final JavaCompiler javaCompiler;
    private final JavaFileManager fileManager;
    private final ClassLoader classLoader;

    InMemoryCompiler()
    {
        classFileObjects =
            new LinkedHashMap<String, ByteArrayJavaFileObject>();
        javaCompiler = ToolProvider.getSystemJavaCompiler();

        JavaFileManager standardFileManager = javaCompiler
            .getStandardFileManager(null, Locale.ENGLISH, null);
        fileManager = new CompiledFileManager(standardFileManager);
        classLoader = new CompiledClassLoader();
    }

    public Map<String, Class<?>> compile(
        List<String> classNames, List<String> sources)
    {
        List<JavaFileObject> javaFileObjects = new ArrayList<JavaFileObject>();
        for (int i = 0; i < classNames.size(); i++)
        {
            String className = classNames.get(i);
            String source = sources.get(i);
            javaFileObjects.add(new StringJavaFileObject(className, source));
        }
        CompilationTask compilationTask = javaCompiler.getTask(
            new PrintWriter(System.out), fileManager, null, null, null, 
            javaFileObjects);
        compilationTask.call();

        Map<String, Class<?>> classes = new LinkedHashMap<String, Class<?>>();
        for (int i = 0; i < classNames.size(); i++)
        {
            String className = classNames.get(i);
            try
            {
                Class<?> c = classLoader.loadClass(className);
                classes.put(className, c);
            }
            catch (ClassNotFoundException e)
            {
                System.out.println(e);
            }
        }
        return classes;
    }

    private class CompiledFileManager 
        extends ForwardingJavaFileManager<JavaFileManager>
    {
        CompiledFileManager(JavaFileManager fileManager)
        {
            super(fileManager);
        }

        @Override
        public JavaFileObject getJavaFileForOutput(Location location,
            String className, javax.tools.JavaFileObject.Kind kind,
            FileObject sibling) throws IOException
        {
            ByteArrayJavaFileObject javaFileObject = 
                new ByteArrayJavaFileObject(className);
            classFileObjects.put(className, javaFileObject);
            return javaFileObject;
        }
    }

    private class CompiledClassLoader extends ClassLoader
    {
        @Override
        public Class<?> findClass(String name)
        {
            byte[] b = classFileObjects.get(name).getBytes();
            return defineClass(name, b, 0, b.length);
        }
    }

    private class ByteArrayJavaFileObject extends SimpleJavaFileObject
    {
        private final ByteArrayOutputStream stream;

        public ByteArrayJavaFileObject(String name)
        {
            super(URI.create("bytes:///" + name), Kind.CLASS);
            stream = new ByteArrayOutputStream();
        }

        @Override
        public OutputStream openOutputStream() throws IOException
        {
            return stream;
        }

        public byte[] getBytes()
        {
            return stream.toByteArray();
        }
    }

    private class StringJavaFileObject extends SimpleJavaFileObject
    {
        private final String code;

        StringJavaFileObject(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;
        }
    }

}

(注意:此类中几乎没有任何错误处理。可以使用DiagnosticListener等进行扩展,但这不是主要意图。