Background to QxWT and GWT-Generator integration


One of the very nice feature of the GWT-Compiler is that one can plug oneself into it and create classes dynamically. At the last 2 evenings I digged a bit into it because I was in need of creating qooxdoo-class dynamically.

Short background to qooxdoo

Before I start explaining the small generator we first need to understand how qooxdoo works and how QxWT interfaces with it.

Let’s at first take a look at the JavaScript world and how a Label-Widget is defined (this is not 100% the real JS-Code but only shows the parts we are interested in for now):

qx.Class.define("qx.ui.basic.Label",
  extend : qx.ui.core.Widget,

  properties : {
    value : {
      check : "String",
      event : "changeValue",
      nullable : true
    }
    // more properties
  }
  // more definitions
);

The above defines a class mydemo.Person which has a property name which is of type String and can be null and in addition when the value is changed an event changeName is delivered to interested parties.

When using this newly defined class in plain JavaScript one can use it like this:

var l = new qx.ui.basic.Label();
l.setValue("Tom Schindl");
alert(p.getValue());

This works because qooxdoo dynamically generates accessor-methods for properties.

Short background to QxWT

Now the whole thing about GWT is that you don’t have to have JavaScript knowledge (though understanding the main JavaScript concepts is helps naturally) to write Web-Applications and if you want to work with a “native” library like qooxdoo you need some library which exposes the native-objects and data-types as Java-Objects/data-types and that’s exactly what QxWT does for qooxdoo.

The process is quite easy. QxWT defines a Java-Class for every JavaScript-class the qooxdoo framework defines itself. So you’ll find in the downloaded sources a class org.ufacekit.qx.wrapper.ui.basic.QxLabel, org.ufacekit.qx.wrapper.ui.core.QxWidget, … and the class definition looks like this:

public class QxLabel extends QxWidget implements IQxStringForm {
}

When we compare the inheritance of the JavaScript and Java-Classes we’ll notice that QxWT reflects the JavaScript inheritance 1:1 to Java.

qooxdoo QxWT

The next important thing we need to know is how to restore Native-JavaScript-Objects in our Java-Class. To make this possible GWT provides us a class named com.google.gwt.core.client.JavaScriptObject. The central point for QxWT to restore this native object is in the class QxObject because fairly all classes in QxWT inherit from there:

public class QxObject implements IQxObject, IMQxBinding {
   private JavaScriptObject nativeObject;
   // ...
   /**
    * <p>
    * <b>PLEASE NOTE: Only for internal use</b>
    * </p>
    * Create a new QxWT wrapping a native Qooxdoo-Object
    * 
    * @param nativeObject
    *            the native widget to wrap
    */
    public QxObject(JavaScriptObject nativeObject) {
      this.nativeObject = nativeObject;
      QxHelper.setAssociatedObject(nativeObject, this);
    }

    /**
     * @return Get the wrapped Qooxdoo-Instance
     */
    public JavaScriptObject getNativeObject() {
      return nativeObject;
    }
}

Please note that we also store a reference to the Java-Object in the Java-Script-Object (Line 15) like this:

public class QxHelper {
  /**
   * Associate a Java-Object with a JavaScript one
   * 
   * @param o
   *            the js object
   * @param obj
   *            the java object
   */
  public static native void setAssociatedObject(JavaScriptObject o, Object obj) /*-{
    o._gwt = obj;
  }-*/;
}

because sometimes we need to find our way back from JavaScript to Java.

The only thing we need to do now is to create an qooxdoo-Instance whenever we create a Java-Instance of a QxWT-Class which is naturally done best in the constructor:

public class QxLabel extends QxWidget implements IQxStringForm {
  public QxLabel() {
    super(createNativeObject());
  }

  private static native JavaScriptObject createNativeObject() /*-{
    return new $wnd.qx.ui.basic.Label();
  }-*/;
}

You might have noticed this your own but when binding to native code inside your Java-Classes you simply define the method as native and write your JavaScript inside the /*-{ }-*/ comment.

The last step in the QxWT-Port is to provide wrapper methods for all API-methods defined by the qooxdoo class which looks like this for the value attribute of QxLabel:

public class QxLabel extends QxWidget implements IQxStringForm {
  public native void setValue(String value) /*-{
    var self = this.@org.ufacekit.qx.wrapper.core.QxObject::getNativeObject()();
    self.setValue(value);
  }-*/;

  public native String getValue() /*-{
    var self = this.@org.ufacekit.qx.wrapper.core.QxObject::getNativeObject()();
    return self.getValue();
  }-*/;
}

As you see we are retrieving the native-qooxdoo-instance through the getNativeObject()-call and afterwards simply call the desired method on the JavaScript-object.

The GWT-Generator

The above described implementation works well and is used in QxWT for all classes which are defined by qooxdoo but it fails if you need to create your own qooxdoo classes e.g. to present your domain model. You might ask why you want to define you domain model in JavaScript if you are working in Java where you can define it as well.

The reason to define the Domain-Model in terms of qooxdoo-classes is to leverage the databinding possibilities coming with qooxdoo itself (there’s upcoming for support for Eclipse Databinding for QxWT but sometimes it might be more natural to use the possibilities coming with qooxdoo itself).

But writing your domain classes in JavaScript and writing a wrapper violates the no JavaScript policy GWT sold you as an advantage. And now at this very point the GWT-Generator steps in. If you look at the code I demonstrated above you’ll notice that the process of creating qooxdoo-class and the java-wrapper could be automated by simply defining some meta-data in your Java-Code and generate the needed bits from it.

So here we go to bring meta informations into Java-Code Java5 introduced the concept of annotations. So we define 2 of them:

@Target(ElementType.TYPE)
public @interface QxClass {
  /**
   * @return Java super class
   */
  String javaSuperClass() default "org.ufacekit.qx.wrapper.core.QxObject";
  /**
   * @return javascript base class
   */
  String jsBaseClass() default "qx.core.Object";

  /**
   * @return the properties
   */
  QxProperty[] properties();
}

to annotate interfaces who will be implemented the generated classes and

public @interface QxProperty {
  /**
   * @return name of the property
   */
  String name();

  /**
   * @return the value to check for e.g. String, ...
   */
  String check();

  /**
   * @return the event
   */
  String event();

  /**
   * @return whether property can be null
   */
  boolean nullable() default false;
	
  /**
   * @return initial value
   */
  String init() default "N/A";
}

The user of QxWT now only has to define an interface like this

@QxClass(properties = {
  @QxProperty(check = "String", event = "changeName", name = "name", nullable = true),
  @QxProperty(check = "String", event = "changeEmote", name = "emote"),
  @QxProperty(check = "Boolean", event = "changeOnline", name = "online", init = "true")		
})
public interface Person extends IQxObject {

  public String getName();

  public void setName(String name);
	
  public String getEmote();

  public void setEmote(String emote);

  public boolean isOnline();

  public void setOnline(boolean online);

  public void toggleOnline();
}

and use it like this:

Person person = GWT.create(Person.class); 
person.setName("Tom Schindl");
person.setEmote("embarrassed");
person.setOnline(false);

This blog now got much longer than I expected so we are looking into how the generator works internally in one of the next posts about QxWT explaining how this objects can be bound directly to QxWT-controls

3 thoughts on “Background to QxWT and GWT-Generator integration

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s