Sunday, January 29, 2012

Re: native javascript functions manipulation in java - overlay types



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.

--
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.
To post to this group, send email to google-web-toolkit@googlegroups.com.
To unsubscribe from this group, send email to google-web-toolkit+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.

No comments:

Post a Comment