I am developing a Graphics package in Java that allows the user to draw triangles, squares, circles, etc. and group/move/manipulate them in order to make animated stick-figure movies. I have an interface that allows users to add figures with GUI buttons, drop-downs, mouse-clicks and the like, and also a text area that users can enter commands like add new triangle((0,0),(50,0),(50,50)). I am writing a lexer/parser to make sense of these commands and execute them. I have a Triangle class and a Point class and an ArrayList of Triangle objects called triangles. The command text above is fairly easy to parse, but some of the potential commands are more complicated and time-consuming. The example above would have the effect of the following code being executed (again this is a simple example. The more complex examples would involve multiple, harder-to-parse commands)...

triangles.add(new Triangle(new Point(0,0), new Point(50,0), new Point(50,50));

What I'd like to know is if it is somehow possible for the user to type the above into the text area, hit the Submit button, and then instead of having to write the parsing code myself, call the javac command and have it compile the code, then execute it somehow. I've been thinking of ways to do it, but they all seem rather inefficient. I had the thought of creating a new .java text file that created a class that contained a main function containing the line of code above, then execute a shell command to compile that class using javac, then another shell command that executed it using the java command. If this was C++, I could compile it into a DLL and then load it from my main process or thread or use some shared memory so Serialization might not be required, but I don't how to do that in Java, so I was thinking of Serializing that Triangle and sending that Serialized Triangle data to my main program via a socket or something.

I feel confident that if there is a way to do what I want, it's not by doing it the way above. Is there a better way to do it? Can it be done at all? Should I just stop wanting to do it and simply resign myself to creating my own language and run-time parser?

Recommended Answers

All 7 Replies

Have a look at the JavaScript support in Java - it allows you to run javascript (and other scripting languages) dynamically from your Java program while sharing varaiables and objects between the two. So you can pass javascript in a String along with any Java values or objects, execute it, and get any resulting values or objects returned to your program. Eg the user can type object creation statements in javascript syntax that you execute to add those objects to an existing java collection.
Google Nashorn which is the name of the Java 8 scripting implementation (don't ask me why).

OK, I will check that out. That's brand new to me. I used to, long ago, know some Javascript, but to say I'm rusty would be an understatement. Someone told me long ago that all Java and Javascript had in common was the name "Java", but I guess that's not true?

I do like the potential. My "idea" seemed like a really convoluted, inefficient, resource-heavy, and with a serious delay (i.e. Java compile time). A scripting solution solves at least some of that since there is no compilation. I'll do some research and I'm sure I'll be back with more questions than I started with. :).

It's true that the syntax etc of JavaScript is only slighly like that of Java - but unless your users are Java coders that's irrelevant. If they need to learn a syntax for their commands, then JS is easier to learn than Java.
I have used the Nashorn thing to execute arbitrary arithmetical expressions rather than write a math parser of my own, and I was impressed by how easy and how well-integrated it is. For your app it's the way you can share objects between your Java code and the user's JS that looks like the massive win-win.

OK, here's another gift

    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("nashorn");

    // create a list 
    List<java.awt.Point> list = new ArrayList<>();

    // expose list as a global variable to the engine
    engine.put("list", list);

    String script =
                 // boilerplate...
                 "var Point = Java.type(\"java.awt.Point\");" +
                 // user input...
                 "list.add(new Point(1,2));";

    // evaluate JavaScript code and access the list
    engine.eval(script);

    System.out.println(list);
commented: Helpful example. Thank you. +5

Very nice. Thank you. This opens up a whole bunch of possibilities that I did not realize were out there as far as integrating scripting into my Java code. I've done it before, but always in a very clumsy way. I've been doing some experimentation and this is working with my code.

    public boolean dostuff2()
    {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        CanvasPanel cp = CanvasPanel.getInstance();
        ArrayList<CFObject> objects = cp.objects;

        // expose list as a global variable to the engine
        engine.put("objects", objects);
        String script =
                     "var CFTriangle = Java.type(\"gui1a.CFTriangle\");" +
                     "var Point = Java.type(\"gui1a.Point\");" +
                     // user input...
                     "var p1 = new Point(50, 50);" +
                     "var p2 = new Point(50, 150);" +
                     "var p3 = new Point(150, 150);" +
                     "var p4 = new Point(150, 50);" +
                     "var triangle1 = new CFTriangle(p1, p2, p3);" +
                     "var triangle2 = new CFTriangle(p4, p2, p3);" +
                     "objects.add(triangle1);" +
                     "objects.add(triangle2);";

        try 
        {
            engine.eval(script);
        } 
        catch (ScriptException ex) 
        {
            Logger.getLogger(Command.class.getName()).log(Level.SEVERE, null, ex);
        }

        cp.repaint();
        return true;
    }

What doesn't work is changing code like this...

var p1 = new Point(50, 50)

to regular Java code like this...

Point p1 = new Point(50, 50)

I'm just starting to read the tutorials and at least for now, I'm more than a little confused about how this all works, but that's to be expected at this stage of my learning curve. I'm a little bit nervous about the "global" part. If I am changing the variable scope of a variable, I want to know, plus I am using the same variable names for generic stuff (i.e. triangle1, pt1, etc.) and if it's somehow binding by name, that could be a problem. It seems like the script needs to be written as Javascript, not Java code, yet it's somehow running on a Java Virtual Machine?

Anyway, lots of new stuff (to me), so lots of reading ahead, in a good way. But once I replaced the word "Point" with "var", everything just seemed to fall into place, at least so far. This is WAAAAY better than my idea.

It seems like the script needs to be written as Javascript, not Java code, yet it's somehow running on a Java Virtual Machine?

Yes, that's right. Nashorn compiles the JavaScript script into JVM bytecode on the fly, and the JVM executes it. (There are many languages that can be compiled into JVM bytecode - https://en.wikipedia.org/wiki/List_of_JVM_languages )

The "Global" comment is a bit misleading. engine.put has zero effect on your Java variables' scope. All it does is to create a mapping between a JavaScript var name and a Java variable name so the JS code can access the Java variable when the JS is executed in the same JVM

Excellent. Thanks for the tips. Won't mark it solved just quite yet, but it's certainly looking that way!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.