martes, 4 de diciembre de 2012

Guidelines for writing GWT Overlay types

Guidelines for writing GWT Overlay types

This post is related with an older post Eclipse templates for overlay types about the same topic. You can find eclipse java code templates for code proposed here in that section. Now, that I have my ideas more clear and tested this is a summary.

When writing overlay types we have to take in consideration two main ideas:

  • we are writing a Java class that will be used by Java users
  • we are overlaying a JavaScript object with an API designed to be used in JavaScript

The following presents some guidelines learned and adopted in my GWT libraries porting javascript libraries like YUI and raphaël to GWT using 100% overlay types for a zero overhead Java API implementation.

Constructor

Use a static method with a simple name like create() or newInstance() that return an empty object or an object with some desired initialization state. Example:

public class MyOverlay extends JavaScriptObject {
 protected MyOverlay(){}
 public static native final create()/*-{
  return {}; 
 }-*/;
}

Properties

In Java we use the Bean convention for accessing obejct attributes / properties with getters and setters methods:

public class MyOverlay extends JavaScriptObject {
 protected MyOverlay(){}
 public static native final create()/*-{
  return {}; 
 }-*/;
 public native final String getColor()/*-{
  return this.color; 
 }-*/;
 public native final void setColor(String color)/*-{
  this.color=color; 
 }-*/;
}
End User example code:
MyOverlay o = MyOverlay.create(); 
o.setColor("red"); 
aController.method1(o);

Nevertheless, we must remember that "we are overlaying a JavaScript object with an API designed to be used in JavaScript". In JavaScript we normally are able to define an entire object in a single statement. For accomplish this in Java we need our setters methods to return the this object so we can perform method chaning. Also, but not so important, se omit the prefixes get/set for there methods

public class MyOverlay extends JavaScriptObject {
 protected MyOverlay(){}
 public static native final create()/*-{
  return {}; 
 }-*/;
 public native final String color()/*-{
  return this["color"]; 
 }-*/;
 public native final MyOverlay color(String color)/*-{
  this["color"]=color; 
  return this; 
 }-*/;
}

End User example code:

aController.method1(MyOverlay.create().color("red")); 

Much more similar to its JavaScript common counterpart aController.method1({color: "red"}), don't you think ?

Some notes:

  • We use setters and getters without the prefixes "get" or "set", so our method names are the same as the property name. This make it easy to use IDE tools like eclipse Java Code templates for generating the getter/setter code automatically.
  • In JSNI code we use string literals this["color"] instead variable names like this.color so the closure compiler (available optionally in GWT > 2.5) don't mess up JavaScript Object property names.

For those Java convention lovers I think it wouldnt cost too much to have "normal" java bean getters and setters AND this kind of shorter accessor methods for those who like to define object more "a la" javascript.

About Performance

Unfortunately the proposed code style here adds some overhead to the JavaScript code output produced by the GWT compiler. In the following example, I have created 2 JSOs, one called NormalBean that follows the Java Bean getter/setter conventions and other one called JsFriendlyBean that uses the solution proposed here with setters that return 'this' for method chaining. Then I use them both and compared the resulting JavaScript output code by GWT compiler using -style PRETTY and -optimize 9 compiler options. This is the result:

Java Sources:

NormalBean normalBean = NormalBean.create();
normalBean.setColor("turttlered1");
normalBean.setAge(13);

JsFriendlyBeam jsFriendlyBeam = JsFriendlyBeam.create()
 .color("turttlered2").age(14); 

Window.alert(normalBean + " - " + jsFriendlyBeam);

JavaScript output:

normalBean = {};
normalBean['color'] = 'turttlered1';
normalBean['age'] = 13;
jsFriendlyBeam = $age($color({}, 'turttlered2'), 14);
$wnd.alert(normalBean + ' - ' + jsFriendlyBeam);
...
function $color(this$static, val){
  this$static['color'] = val;
  return this$static;
}
function $age(this$static, val){
  this$static['age'] = val;
  return this$static;
}

As you can see, the setters of the NormalBean are inlined in JavaScript with zero-overhead while the setters of JsFriendlyBean are not. In the later, JavaScript functions $age() and $color() are created and called and that adds little but some overhead.

I cannot find a workaround for this. Tried with single JSNI statements like the followings but the result is the same:

public native final JsFriendlyBeam age(int val) /*-{
 return (this["age"]=val, this); 
}-*/;
and
public native final JsFriendlyBeam age(int val) /*-{
 return (this["age"] = val || true) ? this : null;
}-*/;

If anybody has an idea about how this setters can be written so they are inlined in JavaScritp output, pleas share it with me :)