native javascript functions manipulation in java - overlay types

254 views
Skip to first unread message

Sebastian Gurin

unread,
Jan 26, 2012, 12:42:27 PM1/26/12
to google-we...@googlegroups.com
Hello all.

I'm trying my first javascript toolkit porting to java in gwt. I think JavaScript Overlay Types is exactly what I need. Everithing seems to be well supported with the exception of javascript functions...

Explanation. The objective behind Overlay Types as I understand is to easily and safely "present" a javascript object in java language. But javascript objects often require the pass of a function as function parameter, for example, subscribing to an event with a javascript function handler:

jQuery(el).click(function(){......});

My question is, can I represent a javascript function in java? for example with a java.lang.Runnable?

My intention is:

public class MyWrapp extends JavaScriptObject {
public void click(Runnable listener) {
and here handle somehow the function listener::run
as a java object functionObject and pass it via jsni like:
this.click(functionObject);
}
}


and then in my java code use the wrapper:

MyWrap wrapper = ...;
wrapper.click(new Runnable(){
Window.alert("clicked!");
});

Note:
In java2script (a java to javascript compiler similar to GWT), it was not so hard to do this because method functions are implemented inside context objects natively. i.e. In Runnable r, r.run is a javascript function natively, s onatively I can simply perform r.run(), taking care of pointing javascript context object to 'r'.

In GWT this is different, calling r.run() natively must be done with something like:

r.@org.my.Runnable::run()();

I'm still investigating possible sollutions to this problem. Also, I found that the new GWT DOM API (http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/gwt/dom/client/package-tree.html), that I think it is based on Overlay Types doesn't seem to have support for event registration or any task implying javascript function manipulation/presentation in java.

Well, any idea is most welcome. Regards


--
Sebastian Gurin <sgu...@softpoint.org>

Sebastian Gurin

unread,
Jan 27, 2012, 11:27:10 AM1/27/12
to google-we...@googlegroups.com
Ok I found more or less how to do what I want, the following registers a click handler in a native DOM object:

public class DomEventTest1 {

public static interface ClickHandler {
void notifyClick(Element source);
}

/** call this directly from your Entry point class */
public static void test(RootPanel rootPanel) {

//create a button using gwt DOM
ButtonElement button1 = Document.get().createPushButtonElement();
button1.setInnerHTML("clickme");
Document.get().getBody().appendChild(button1);

addClickHandler(button1, new ClickHandler() {
@Override
public void notifyClick(Element source) {
System.out.println("CLICKED");
}
});
}
public static native void addClickHandler(Element e, ClickHandler handler)/*-{
//dirty click handler registration, only for testing
e.onclick=function() {
handler.@org.sgx.gwtraphaljstest.client.js.test.DomEventTest1.ClickHandler::notifyClick(Lcom/google/gwt/dom/client/Element;)(this);
}
}-*/;

}


Now two quiestion about jsni.

The first question is: the statement
handler.@org.sgx.gwtraphaljstest.client.js.test.DomEventTest1.ClickHandler::notifyClick(Lcom/google/gwt/dom/client/Element;)(this);
is a valid javascript statement? or is it some internal gwt compiler language that is translated to javascript?

What I would like is to be able of represent any javascript function using java objects, like Runnable or other. The main problem for this is be able to call a java instance method from javascript having the java class name, method name and signature in strings. I would like something like:

public static native void callJavaInstMethod(Object javaThisEl, String className, String methodName, String methodSignature, Object[]params)/*-{
//and here do something like:
javaThisEl.@${className}::${methodName}(${methodSignature})(${params})
}-*/;

I tried to archieve something like this unssuccessfully with eval and other hacks. A method like this, will allow me to represent any javascript function using java objects. For example, instead of writing methods like addClickHandler by hand, I could use an Artificial AbstractRunnable class for represent javascript functions as java objects and do:

button1.addClickHandler(new AbstractRunnable1<Element>(){
public void run(Element e) {
System.out.println("CLICKED");
}
});

Any ideas on how to call a java instance method from native javascript having all the necesary information in Strings ?

Regards, and thanks in advance.

> --
> You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
> To post to this group, send email to google-we...@googlegroups.com.
> To unsubscribe from this group, send email to google-web-tool...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.
>


--
Sebastian Gurin <sgu...@softpoint.org>

Thomas Broyer

unread,
Jan 29, 2012, 6:23:33 AM1/29/12
to google-we...@googlegroups.com


On Friday, January 27, 2012 5:27:10 PM UTC+1, Sebastian Gurin wrote:
Ok I found more or less how to do what I want, the following registers a click handler in a native DOM object:

public class DomEventTest1 {
        
public static interface ClickHandler {
        void notifyClick(Element source);
}

/** call this directly from your Entry point class */
public static void test(RootPanel rootPanel) {
        
        //create a button using gwt DOM
        ButtonElement button1 = Document.get().createPushButtonElement();
        button1.setInnerHTML("clickme");
        Document.get().getBody().appendChild(button1);
        
        addClickHandler(button1, new ClickHandler() {        
                @Override
                public void notifyClick(Element source) {
                        System.out.println("CLICKED");
                }
        });
}
public static native void addClickHandler(Element e, ClickHandler handler)/*-{
        //dirty click handler registration, only for testing
        e.onclick=function() {                        
handler.@org.sgx.gwtraphaljstest.client.js.test.DomEventTest1.ClickHandler::notifyClick(Lcom/google/gwt/dom/client/Element;)(this);
        }
}-*/;


You should wrap your function in $entry() so that a) exceptions are routed to the GWT.UncaughtExceptionHandler and b) Scheduler.scheduleEntry and Scheduler.scheduleFinally run appropriately. Also, note that you could pass the event to your Java code as a NativeEvent:

e.onclick = $entry(function(e) {
   handler.@org.sgx.gwtraphaljstest.client.js.test.DomEventTest1.ClickHandler::notifyClick(Lcom/google/gwt/dom/client/NativeEvent;)(e);
}

(you might have to tweak the code for IE, which doesn't pass the event as an argument but uses window.event (or should it be $wnd.event?))

}


Now two quiestion about jsni.

The first question is: the statement
handler.@org.sgx.gwtraphaljstest.client.js.test.DomEventTest1.ClickHandler::notifyClick(Lcom/google/gwt/dom/client/Element;)(this);
is a valid javascript statement? or is it some internal gwt compiler language that is translated to javascript?


No, it's a special syntax for JSNI. It's not valid JavaScript.
 

What I would like is to be able of represent any javascript function using java objects, like Runnable or other. The main problem for this is be able to call a java instance method from javascript having the java class name, method name and signature in strings. I would like something like:

public static native void callJavaInstMethod(Object javaThisEl, String className, String methodName, String methodSignature, Object[]params)/*-{
        //and here do something like:
        javaThisEl.@${className}::${methodName}(${methodSignature})(${params})
}-*/;

I tried to archieve something like this unssuccessfully with eval and other hacks. A method like this, will allow me to represent any javascript function using java objects. For example, instead of writing methods like addClickHandler by hand, I could use an Artificial AbstractRunnable class for represent javascript functions as java objects and do:

button1.addClickHandler(new AbstractRunnable1<Element>(){
        public void run(Element e) {
                System.out.println("CLICKED");
        }
});

Any ideas on how to call a java instance method from native javascript having all the necesary information in Strings ?


The GWT compiler will obfuscate all class names and method names, so it's not going to work. Only solution would be to create a map, something like:

   map['org.sgx.gwtraphaljstest.client.js.test.DomEventTest1.ClickHandler::notifyClick(Lcom/google/gwt/dom/client/Element;)'] =
      someObject.@org.sgx.gwtraphaljstest.client.js.test.DomEventTest1.ClickHandler::notifyClick(Lcom/google/gwt/dom/client/Element;);

(note that we don't call the method, we only reference it!)
Then:

   var fn = map[className + '::' + methodName + '(' + methodSignature + ')'];
   fn.apply(javaThisEl, params);

(but note that params would have to be a JS array if you want this to work in DevMode, as a Java array will be an opaque object in JS)

Or of course you could do it using switch/case or if/else:
   switch (className + '::' + methodName) {
      case 'org.sgx.gwtraphaljstest.client.js.test.DomEventTest1.ClickHandler::notifyClick':
         javaThisEl.@org.sgx.gwtraphaljstest.client.js.test.DomEventTest1.ClickHandler::notifyClick(Lcom/google/gwt/dom/client/Element;)(params[0]);
         break;
   }

However, I'd suggest you try to find another approach, as the compiler won't be able to prune unused code and produce an optimally optimized code.

Sebastian Gurin

unread,
Jan 31, 2012, 7:47:45 AM1/31/12
to google-we...@googlegroups.com
Thomas Thank you very much for your answers. You helped me al lot with my first porting of a javascript toolkit to GWT: my own version of raphaeljs.com (a cool javascript library for doing vector graphics) to gwt:

project page
http://code.google.com/p/raphael4gwt/

demo with java sources available:
http://cancerbero.vacau.com/gwt/raphael4gwtGallery/

This library was written by me in few days and from scratch.

Initially I wanted to create a layer for "doing javascript in java", so I can write javascript in java without jsni (but paying a little overhead price), like something like this:

JavaScriptObject o1 = getNativeObject();
JsUtils.put(o1, "property1", "value1");
JsUtils.put(o1, "onclick", JsUtils.FUNC(new Runnable1(){
public JavaScriptObject run1(NativeEvent e) {
...
}
});

JsUtil.put performs obj[1]=b, jsUtil.FUNC converts a Runnable in a javascript function, etc

But this seems to be impossiblein gwt. More, looking at generated sources, I see gwt compiler do a beautifull job optimizing jsni based code. Also I'm feeling very confortably programing overlay types in eclipse with gwt eclipse plugin. So I discarded my initial idea of "doing javascript in java", and I'm working with "normal" overlay types.

Thanks again, I will notify the group when my gwt4raphael library supports 100% of the raphaeljs api (neverthelessm,it is very usable right now)

Regards, and thank you again.

> --
> You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.

> To view this discussion on the web visit https://groups.google.com/d/msg/google-web-toolkit/-/INg0raG3T48J.

Alain Ekambi

unread,
Jan 31, 2012, 8:25:17 AM1/31/12
to google-we...@googlegroups.com
Any difference between your library and  http://code.google.com/p/raphaelgwt/  ? 

2012/1/31 Sebastian Gurin <sgu...@softpoint.org>

Sebastian Gurin

unread,
Jan 31, 2012, 10:07:55 AM1/31/12
to google-we...@googlegroups.com
Yes there are some differences:

* raphaelgwt is based in raphaeljs 1.0 and mine in raphael2.0.

* raphaelgwt has limited support for doing native stuff it is base on gwt widgets for solving some problems. Specially there are no native event support (events are managed at gwt widget level). My library, raphael4gwt is not based on gwt widgets at all and support 100% of raphaeljs api with overlay types for zero overhead. You can put a raphael canvas object inside a widget, but contrary to raphaelgwt, the java api is only for the raphael canvas and shapes, not widgets.

last section of the wiki explains my motivations a little more: http://code.google.com/p/raphael4gwt/wiki/MainDocumentation

Nevertheless, I'm thinking on contributing some things to raphaelgwt, specially native support for event handling.

I will anounce raphael4gwt in this group once I get a more polished version.

Regards.


--
Sebastian Gurin <sgu...@softpoint.org>

Reply all
Reply to author
Forward
0 new messages