I am trying to feed a Java object into a script that would normally operate on the Document Object Model (DOM) of a web page. For the most part this functions as intended. However I have encountered a problem when dealing with attributes/properties of the DOM elements.
A particular property chain of interest is somediv.firstChild.href
. What I can't figure out is how to get the firstChild
property value dynamically. The simplest way I can think of at the moment is to use source.replaceAll("firstChild", "firstChild()");
to force the firstChild
property to invoke the function firstChild()
instead. However this will eventually open up a new can of worms.
How do I define an object that can be passed to a javascript function that can be operated on via the DOM?
Before diving into Java I had learnt C#. In C# the concept of setters and getters is quite prevalent. If this interface method were available in Java my problem would be solved.
public string firstChild {
get { return this.getFirstChild(); }
set { this.setFirstChild(value); }
}
The script is currently invoked by wrapping it in a function where I can pass in the window
and document
Java objects into the function's workspace.
document
is a special top-level version of SpoofedDomElement
(that extends it) but is functionally identical to the sample shown below. window
is another object with minimal functions that handle event listeners.
Javascript (snippet) to operate on DOM
var somediv = document.createElement('div');
somediv.style.display = "block"
somediv.innerHTML="<a href='/mywork/server/test.html'>The Test Server Homepage</a>";
var linkvalue = somediv.firstChild.href;
This snippet is stored as the string theOriginalSource
and used in the next section.
Java code to evaluate Javascript
String wrappedSource = "var scriptToInvoke = function(window, document){"
+ "\n" + theOriginalSource // from above
+ "\n};"
Object result = invocable.invokeFunction("scriptToInvoke", window, document);
This snippet wraps the javascript snippet so that I can pass in objects to use as window
and document
.
Java classes that spoof DOM elements
public class SpoofedDomElement {
public SpoofedDomElement firstChild;
public String id;
public String innerHtml;
public String href;
public SpoofedStyleProperties style = new SpoofedStyleProperties();
public String tagname;
...
}
public class SpoofedStyleProperties {
public String background = "transparent none repeat scroll 0% 0% auto padding-box border-box";
public String color = null;
public String display = "inline";
}
The above classes handle irrelevant parts of the code just fine (such as the assignment somediv.style.display = "block"
). But it starts to fall apart when handling the values of firstChild
or innerHtml
when either value is changed.
N.B. I include this section in all my questions to document what I have tried for future SO users who get here by Google. This might help someone reach a solution by aiding brainstorming.
I have attempted to use a framework (HtmlUnit) to evaluate the Javascript. But I couldn't control which Javascript snippets were executed.
The following are questions that I am currently researching to find a solution. If I find anything I will report back.
firstChild
as a function?It appears that you're trying to implement friendly access of script objects from Java code as well as trying to provide script-friendly API on top of java library/libraries.
For the first part [ script object access from Java ]
Apart from javax.script.Invocable interface, you can use JSObject. Nashorn exposes script objects as instances of jdk.nashorn.api.scripting.JSObject/.ScriptObjectMirror
For the second part [ friendlier access of Java objects from scripts ]
You can write script friendly wrappers in script itself using "JSAdapter".
Doc and Example:
If you'd prefer to do in Java, you can implement your own jdk.nashorn.api.scripting.JSObject/.AbstractJSObject.
Doc and Example:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-jsobject
Other nashorn specific script extensions may also be used to trap unknown property/method access in per object basis:
noSuchProperty hook in any script object:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-noSuchProperty
noSuchMethod hook in any script object:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-noSuchMethod
Object.bindProperties:
There script API extension can be used to bind properties of one object to another - the source object could be a Java object as well.
With jdk9, there is more flexible inter-language linking possible with Dynalink API [ http://openjdk.java.net/jeps/276 ]
See also:
https://blogs.oracle.com/sundararajan/entry/dynamic_linker_api_for_the
https://blogs.oracle.com/sundararajan/entry/writing_pluggable_dynalink_linker_and
https://blogs.oracle.com/sundararajan/entry/nashorn_javascript_access_to_python
There are dynalink samples "samples/dynalink" directory of Nashorn OpenJDK repository:
http://hg.openjdk.java.net/jdk9/dev/nashorn/file/4a6ee1185fc8/samples/dynalink