Saturday, September 29, 2007

The marriage of GWT with Eclipse-Technologies

I'm currently on the track to explore GWT and started just this morning with it. The first thing I found missing is something like the Viewers-Concept from JFace.

I don't want to learn new things if the one I'm familiar with would make my life much easier. JFace-Viewers is one of my main working areas in Eclipse-land. So I sat down and took a closer look and after 1 hour of work my GWT-code looks like this:


package at.bestsolution.eclipse.jface.example.client;

import java.util.ArrayList;

import org.gwtwidgets.client.util.SimpleDateParser;

import at.bestsolution.eclipse.jface.client.viewers.TreeViewer;
import at.bestsolution.eclipse.jface.example.client.model.Family;
import at.bestsolution.eclipse.jface.example.client.model.Person;
import at.bestsolution.eclipse.jface.example.client.ui.MyContentProvider;
import at.bestsolution.eclipse.jface.example.client.ui.MyLabelProvider;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Tree;

/**
* Entry point classes define onModuleLoad().
*/
public class JFaceExampleApp implements EntryPoint {

/**
* This is the entry point method.
*/
public void onModuleLoad() {

Tree tree = new Tree();
TreeViewer viewer = new TreeViewer(tree);
viewer.setLabelProvider(new MyLabelProvider());
viewer.setContentProvider(new MyContentProvider());
viewer.setInput(createInput());

RootPanel.get().add(tree);
}

private ArrayList createInput() {
SimpleDateParser parser = new SimpleDateParser("yyyy-MM-dd");
ArrayList list = new ArrayList();

Family fam = new Family("Schindl");
Person p = new Person("Tom",parser.parse("1979-05-01"),fam);
p = new Person("Tina",parser.parse("1980-10-14"),fam);
p = new Person("Laura",parser.parse("1991-08-07"),fam);

list.add(fam);

fam = new Family("Hoppichler");
p = new Person("Andreas",parser.parse("1977-11-06"),fam);
p = new Person("Franzi",parser.parse("1978-11-02"),fam);

list.add(fam);

return list;
}
}


And the respective Viewer-classes like this:


package at.bestsolution.eclipse.jface.example.client.ui;

import java.util.ArrayList;

import at.bestsolution.eclipse.jface.client.viewers.ITreeContentProvider;
import at.bestsolution.eclipse.jface.client.viewers.Viewer;
import at.bestsolution.eclipse.jface.example.client.model.Family;
import at.bestsolution.eclipse.jface.example.client.model.Person;

public class MyContentProvider implements ITreeContentProvider {

public Object[] getChildren(Object parentElement) {
if( parentElement instanceof Family ) {
return ((Family)parentElement).getList().toArray();
}
return new Object[0];
}

public Object getParent(Object element) {
if( element instanceof Person ) {
return ((Person)element).getFamily();
}

return null;
}

public boolean hasChildren(Object element) {
return getChildren(element).length > 0;
}

public Object[] getElements(Object inputElement) {
return ((ArrayList)inputElement).toArray();
}

public void dispose() {

}

public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {

}

}



package at.bestsolution.eclipse.jface.example.client.ui;

import org.gwtwidgets.client.util.SimpleDateFormat;

import at.bestsolution.eclipse.jface.client.viewers.ILabelProvider;
import at.bestsolution.eclipse.jface.example.client.model.Family;
import at.bestsolution.eclipse.jface.example.client.model.Person;

public class MyLabelProvider implements ILabelProvider {
private SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy");

public String getText(Object element) {
if( element instanceof Family ) {
return ((Family)element).getName();
} else if( element instanceof Person ) {
return ((Person)element).getName() + " ("+format.format(((Person)element).getBirthday())+")";
}

return "";
}

}


Now you think this is cool? Because this was to trivial I thought I'll have to go further and after ~ 6h I had ported the the core-parts from the JFace-Databinding-Framework to GWT! So my UI-Binding-Code looks like this now:


// ....
TextBox box = new TextBox();

TextBoxObservableValue targetValue = new TextBoxObservableValue(box,TextBoxObservableValue.Change);
IObservableValue modelValue = PEMFObservablesFactory.observeDetailValue(Realm.getDefault(), value, "id", String.class);

DataBindingContext ctx = new DataBindingContext();
UpdateValueStrategy targetToModel = new UpdateValueStrategy();
targetToModel.setConverter(new IConverter() {

public Object convert(Object fromObject) {
return fromObject.toString();
}

public Object getFromType() {
return null;
}

public Object getToType() {
return null;
}

});

UpdateValueStrategy modelToTarget = new UpdateValueStrategy();
modelToTarget.setConverter(new IConverter() {

public Object convert(Object fromObject) {
return fromObject.toString();
}

public Object getFromType() {
return null;
}

public Object getToType() {
return null;
}

});
ctx.bindValue(targetValue, modelValue, targetToModel, modelToTarget);


The GWT-Compiler translates all into JS-Code and because there's no Reflection-API available in this space I had to invent my own concept which I lend from EMF hence the name PEMF (for PoorEMF). Without question much of the Databinding-Framework is not working but at this stage I don't mind. The good thing is that all those low-level modules use fairly only classes already available from GWT so porting the whole framework is only a matter of time.