Chronoscope and GWT Exporter

258 views
Skip to first unread message

Ray Cromwell

unread,
May 30, 2007, 12:27:40 AM5/30/07
to Google Web Toolkit

Hi all,
Given the occasion of Google Developer Day, I thought I'd try to get
some feedback on one of the neat GWT applications we are developing:
Chronoscope.

Chronoscope is a charting platform, currently in alpha, for GWT
focused on time series charts, and providing a "Google Maps"-like
experience of soaring around the dataset, as well as providing maps-
style API for mashups, like adding overlays, pushpin markers, etc. You
can see an indepth preview at http://www.timepedia.org, bugs and all.
(It only works on Firefox at the moment. It also works in Webkit/
Hosted Mode, but not in Safari 2.0.4 without rendering errors. Camino
has some issues too.)

What's interesting is how we arrived at GWT. Our requirements for
Chronoscope demand that it run on the server as well as the client and
mobile devices.

Originally, Chronoscope was done with JFreeGraph (heavily modified)
and a parallel hand-crafted Javascript version, but maintaining a
separate Java and Javascript code base turned out to be intractable.
GWT allowed us to unify Java Graphics2D rendering, client-side
Javascript, and mobile platforms into a single code base, and now
porting to another rendering platform (e.g. Flash, Silverlight, etc)
takes only a few extra classes. I think this is a real success story
for GWT, atleast from our point of view -- it was invaluable.

The second thing we are releasing today is GWT Exporter. We wanted
Chronoscope to be scriptable from ordinary Javascript, so we put alot
of effort into generating bridge methods between ordinary JS class
method idioms and JSNI methods.

For example, stuff like $wnd.MyClass.prototype.foo = function()
{ this.instance.@jsniReference::foo(..)(..); }

As our platform grew, the manual bridging code became a pain to
maintain, so I decided to see what it would take to get GWT to do the
work automatically. I was pleasantly surprised that it wasn't that
hard to achieve by creating a custom generator (albeit, better
documentation would be nice) After a day of hacking, I came up with
the following solution:

Given a class like:

public class MyClass {
public static final int FIELD_1=1;
public static final int FIELD_2=20;

private int origin;
public MyClass(int origin) {
this.origin=origin;
}

public int add(int amt) {
return origin+amt;
}
}


one can export a Javascript API that looks like this:

var myclass = new mypackage.MyClass(10);
alert(myclass.add(mypackage.MyClass.FIELD_2));

by doing the following annotations:


/**
* this line overrides the default package which may be too long/deep
* @gwt.exportPackage mypackage
*/
public class MyClass implements Exportable {
/**
* @gwt.export
*/
public static final int FIELD_1=1;

/**
* @gwt.export
*/
public static final int FIELD_2=20;

private int origin;
public MyClass(int origin) {
this.origin=origin;
}

/**
* @gwt.exportConstructor
*/
public static MyClass createConstructor(int origin) {
return new MyClass(origin);
}

/**
* @gwt.export
*/
public int add(int amt) {
return origin+amt;
}
}

and then in your entry point method:

Exporter exporter = (Exporter)GWT.create(MyClass.class);
exporter.export(); // this line will create a bunch of bridge method/
fields with $wnd scope

The @gwt.export annotation signals to the generator that a bridge
method should be created. @gwt.exportConstructor tells it that the
static factory method is to be used for the JS constructor to obtain
an instance.

I will be distributing the open-source code on http://code.google.com/p/gwt-exporter/
probably in the next day or so.

For those interested in chatting about GWT charting and vector
graphics, look for the guys in the black "Timepedia" t-shirts at the
San Jose/Mountain View Google Developer Day.
-Ray

Bruce Johnson

unread,
Jun 7, 2007, 4:19:20 PM6/7/07
to Scott Blum, Google-Web-Tool...@googlegroups.com, Google-We...@googlegroups.com
[+Google-Web-T...@googlegroups.com since this is a useful technique for using ImageBundles in general]

On 6/7/07, Scott Blum < sco...@google.com> wrote:
And I would add to that, 4 or 5 uses cases which cannot be solved by composition (which was done for ImageBundle) or for which such composition would tend to be ugly/unwieldy.

Right. Scott is talking about combining multiple independent ImageBundle-derived interfaces, such as

public interface Bundle1 extends ImageBundle {
  AbstractImagePrototype image1();
}

public interface Bundle2 extends ImageBundle {
  AbstractImagePrototype image2();
}

in your source code by creating a synthetic derived interface that extends all of them as a way to explicitly indicate that they should use the same composited image file. For example,

public interface MyApp extends Bundle1, Bundle2 { }

You ever only instantiate MyApp:

void foo(Bundle1 doesNotKnowAboutBundle2) { ... }

void bar(Bundle2 doesNotKnowAboutBundle1) { ... }

void doStuff() {
  MyApp myApp = (MyApp)GWT.create(MyApp.class);
  foo(myApp);
  bar(myApp);
}

If this sort of composition pattern addresses those use cases more-or-less completely, that's another good reason it wouldn't be strictly necessary.

I can still imagine real-world scenarios where you can't design everything to fit together so elegantly, though, so I don't want to dismiss the idea either. It's a good one to continue kicking around.

Reply all
Reply to author
Forward
0 new messages