Thursday, April 3, 2008

GWT: The Road to 1.5, Language features and GQuery

Hi folks,
I had to delay the second part of my article on Linkers because the APIs have been changing radically lately and have only just settled down in the trunk. However, to pass the time, I decided to demonstrate some of the huge benefits of 1.5 by implementing a type-safe JQuery clone in GWT.

That's right, for all those folks who don't want to deal with GWT Widgets, but just want to query, wrap, and manipulate plain old DOM elements in GWT, this article is for you. I'm talking to you, you self-hating Java programmer with an inner Javascript Ninja begging to be let out.

Yes this is working code


First, let's just dispense with the details and show you a working example, yes, this is working code:

public void onModuleLoad() {
$("div").css("color", "red").click(new Function() {
public void f(Element e) {
Window.alert("Hello");
$(e).as(Effects).fadeOut();
}
});
}

What does this do? It makes every DIV element red and clickable, and if you click one, it pops up an alert message, and fades the DIV out.

The API is mostly faithful to jQuery's public API, with the exception of the lack of succinct closures (Hello, Neal Gafter!), but that's ok, we can introduce enough prebuilt composable functors to eliminate alot of the need for inline anonymous functions for common operations.

Small, Fast, Efficient Code, and Other Benefits


Since GWT prunes any code not being used, this example produced a obfuscated + gzipped script size of only 5459. If I change the click handler to simply $(e).setInnerHTML("Boom"), the compiled + gzipped script size is 1400 bytes.

There's a few small differences to take note which are either a benefit, or a hinderance depending on who you ask:
1. Type-Safety, IDE code completion, debuggability, etc
2. Lack of prototype modifications forced slighty change for GQuery plugin usage (see Effects above)
3. Selectors can be compiled at compile time, more on that later.

How 1.5 Enables GQuery


Now, let's talk about the features in GWT 1.5 that make GQuery nice and efficient to implement.

1) Joel Webber of the GWT Team just submitted a patch to GWT 1.5 that implements every DOM2 Core+HTML class as a subclass of the new JavaScriptObject. This not only made the coding of GQuery very easy, but it produces nicely optimized JavaScript.

2) Static Imports. The $ function would not nearly be as nice without static imports.

3) Generics and Covariant return. The Plugin mechanism utilizes this to fake prototype based overrides of the GQuery object.

How to write GQuery plugins? Damn simple


Java doesn't feature metaclasses or prototypes, so there is no way to add methods to an existing instance or class without subclassing, but in order to preserve jQuery style syntax, we can use covariant return, type capture, and other features of JDK 1.5, to fake it.

There are two ways this occurs, first, the $() method can take a final parameter which is a Plugin class literal, or, you can call the as() method on any GQuery and "cast" it to a plugin interface. Here's the implementation of the as() method:

/**
* Convert to Plugin interface provided by Class literal.
*/
public <T extends GQuery> T as(Class<T> plugin) {
return (T) plugins.get(plugin).init(this);
}


The type of the plugin, which is a subclass of GQuery is captured by the class literal parameter, the Class literal is mapped to a Plugin class factory which creates instances of the plugin, and finally, the current GQuery is passed in, so that the Plugin can access the set of matched elements.

Here is an example plugin implementation:

public class Effects extends GQuery {

static {
GQuery.registerPlugin(Effects.class, new EffectsPlugin());
}
public static final Class<Effects> Effects = Effects.class;

public Effects(Element element) {
super(element);
}

public Effects(Element[] elements) {
super(elements);
}

public Effects(NodeList list) {
super(list);
}

public Effects fadeOut() {
Animation a = new Animation() {

public void onCancel() {
}

public void onComplete() {
for(Element e : elements) {
e.getStyle().setProperty("opacity", "1.0");
}
}

public void onStart() {
}

public void onUpdate(double progress) {
for(Element e : elements) {
e.getStyle().setProperty("opacity", String.valueOf(1.0-progress));
}
}
};
a.run(2000);
return this;
}

public Effects fadeIn() {
Animation a = new Animation() {

public void onCancel() {
}

public void onComplete() {
}

public void onStart() {
}

public void onUpdate(double progress) {
for(Element e : elements) {
e.getStyle().setProperty("opacity", String.valueOf(progress));
}
}
};
a.run(2000);
return this;
}

public static class EffectsPlugin implements Plugin<Effects> {
public Effects init(GQuery gq) {
return new Effects(gq.get());
}
}
}


That's it. Plugins are nothing more than subclasses of GQuery which have an extra registry step and factory.

I'll put up the source code on http://code.google.com/p/gwtquery after GWT 1.5 milestone 2 is released, which is needed to run this.

You forgot to talk about compile-time selectors


So, yeah, as Generators are a frequent topic of this blog, you really think I wouldn't find a way to sneak them in? GQuery supports two mechanisms for selector evaluation. It can compile and evaluate selectors at runtime (currently just using XPath for prototyping, which isn't available everywhere), or, you can create an interface containing selectors you know about at runtime, and have them precompiled and ready to execute.

As an example:

public interface MySelectors extends GQuery.Selectors {
@Selector("div")
public GQuery allDivs();

@Selector("div.foo")
public GQuery allFooDivsBelow(Element context);

}

Then to use:

MySelectors s = (MySelectors)GWT.create(MySelectors.class);
s.allFooDivsBelow(context).css("backgroundColor", "yellow");

A generator performs a browser-specific compile-time transformation of the selector in the annotation into the most optimal form that the browser supports (XPath, pure-JS, DOM traversal, document.getElementsByClass, etc) Not only does this produce smaller per-browser code, it produces faster code as well.

One last thing, the $$ function


jQuery contains a lot of methods which take essentially property/value pairs in a Javascript object literal. Emulating this with Java syntax would be too tedious (Scala frontend to GWT, please!), however, to ease this use case, I added a global $$ function for manipulation JavaScriptObject/JSON objects easily.

For example, if you want to create a JS object literal, you write:

Properties literal = $$("{ foo: 'bar', baz: 'bam'}");

You can then use this Properties literal in GQuery functions ala jQuery, or, you can access them with JS-like syntax

$("div").attr($$("{ foo: 1, bar: 2 }"); // set every DIV to have a foo attribute = 1, and bar attribute = 2

// And;
$$("{foo: 1, bar : 2}").get("foo") == 1;
$$("{foo: 1, bar: { baz: 3} }").get("bar.baz") == 3;

-Ray

13 comments:

Ray Ryan said...

Wow. Wow! Oh, and +1 on the scala front end notion.

Andrés Testi said...

Nice post! Yes, we need Scala to GWT.

Chii said...

that is amazing! i m definitely keeping an eye out for this.

Ray Cromwell said...

Hehe, so far, it looks like more people are interested in a Scala front end. :)

James Strachan said...

Great stuff! Is GwtQuery gonna show up in svn any time soon? :)

Unknown said...

That really nice!
Cannot wait to play with this one...

Cameron said...

I'm excited about GwtQuery, any updates on when the source will be available ?

Anonymous said...

小弟第貼影片區小弟第貼影片區小弟第貼影片區小弟第貼影片區小弟第貼影片區小弟第貼影片區亞洲成人免費線上影片亞洲成人免費線上影片亞洲成人免費線上影片亞洲成人免費線上影片亞洲成人免費線上影片亞洲成人免費線上影片亞洲成人免費線上影片亞洲成人免費線上影片亞洲成人免費線上影片亞洲成人免費線上影片xx18net影片交流區xx18net影片交流區xx18net影片交流區xx18net影片交流區xx18net影片交流區xx18net影片交流區xx18net影片交流區xx18net影片交流區xx18net影片交流區xx18net影片交流區線上影片 貼影片區線上影片 貼影片區線上影片 貼影片區線上影片 貼影片區線上影片 貼影片區線上影片 貼影片區線上影片 貼影片區線上影片 貼影片區線上影片 貼影片區線上影片 貼影片區777美女dvd影片777美女dvd影片777美女dvd影片777美女dvd影片777美女dvd影片777美女dvd影片777美女dvd影片777美女dvd影片777美女dvd影片777美女dvd影片一葉情貼影片區一葉情貼影片區一葉情貼影片區一葉情貼影片區一葉情貼影片區一葉情貼影片區一葉情貼影片區一葉情貼影片區一葉情貼影片區一葉情貼影片區383movie成人影城383movie成人影城383movie成人影城383movie成人影城383movie成人影城383movie成人影城383movie成人影城383movie成人影城383movie成人影城383movie成人影城綺麗影城綺麗影城綺麗影城綺麗影城綺麗影城綺麗影城綺麗影城綺麗影城綺麗影城綺麗影城免費成人影城免費成人影城免費成人影城免費成人影城免費成人影城免費成人影城免費成人影城免費成人影城免費成人影城免費成人影城嘟嘟影城嘟嘟影城嘟嘟影城嘟嘟影城嘟嘟影城嘟嘟影城嘟嘟影城嘟嘟影城嘟嘟影城嘟嘟影城383成人影城383成人影城383成人影城383成人影城383成人影城383成人影城383成人影城383成人影城383成人影城383成人影城18成人影城0204movie18成人影城0204movie18成人影城0204movie18成人影城0204movie18成人影城0204movie18成人影城0204movie18成人影城0204movie18成人影城0204movie18成人影城0204movie18成人影城0204movie亞洲禁果影城亞洲禁果影城亞洲禁果影城亞洲禁果影城亞洲禁果影城亞洲禁果影城亞洲禁果影城亞洲禁果影城亞洲禁果影城亞洲禁果影城夜未眠成人影城夜未眠成人影城夜未眠成人影城夜未眠成人影城夜未眠成人影城夜未眠成人影城夜未眠成人影城夜未眠成人影城夜未眠成人影城夜未眠成人影城a38影城a38影城a38影城a38影城a38影城a38影城a38影城a38影城a38影城a38影城絕色影城絕色影城aa片免費看微風論壇080哈啦聊天室6k聊天室成人聊天室上班族捷克論壇大眾論壇plus論壇080視訊聊天室520視訊聊天室尋夢園上班族聊天室成人聊天室上班族 a片a片影片免費情色影片免費a片觀看小弟第貼影片區免費av影片免費h影片試看 H漫 - 卡通美女短片小魔女貼影片免費影片觀賞無碼a片網美女pc交友相簿美女交友-哈啦聊天室中文a片線上試看免費電影下載區免費試看a短片免費卡通aa片觀看女優影片無碼直播免費性感a片試看日本AV女優影音娛樂網日本av女優無碼 dvd辣妹視訊 - 免費聊天室美女交友視訊聊天室 080免費視訊聊天室尋夢園聊天室080苗栗人聊天室a片下載

Anonymous said...

(法新社倫敦四日電) 英國情色大亨芮孟的公司昨天成人光碟說,av芮孟日前去世,享成人影片壽八十二歲;這位身價上億的房地產開發商,曾av經在倫敦推av出第一場脫衣舞表演。
a片下載

成人孟的財a片產估計達六部落格億五千萬英鎊(台幣將近四百億),由於他名下sex色情業大多分布在日本av倫敦夜生活區蘇活區,因此擁有「蘇AV片活之王」的稱av女優號。

情色視訊
他的公司「保羅芮成人電影孟集團」旗下發行多種情情色色雜誌a片,包括「Razzle」、「男性世a片下載界」以及「Maavyf成人網站air」。情色電影


芮孟本名傑福瑞.安東尼.奎恩,父親為搬運承包商。芮孟十a片五歲離開學校,矢色情a片色情要在情色av女優演事業a片留名,起先表演av女優讀心術,後來成為巡迴歌舞雜耍表成人電影演的製作人。


色情影片多評論家認為,他把情色成人表演帶進主流社會,成人網站一九五九年主持破天荒的情色電影脫衣舞表演,後來avdvd更靠著在蘇活區與倫敦西區開發房色情地產成人影片賺得大筆財富。


有人形容芮部落格孟是成人網站英國的海夫成人影片部落格,地位等同美國的「花花公子」創辦人海夫納。

Anonymous said...

汽車旅館
消費券優惠
motel
消費券
薇閣
住宿券
廣交會
廣州飯店
廣州
广州
广交会
广州酒店
Canton Fair
Guangzhou Hotel
Guangzhou
広州
広州の交易会
広州のホテル

Anonymous said...

成人網站,av女優,成人網站,a片,成人影片,h漫,成人電影,成人電影,色情,成人影片,免費A片,色情,成人影片,色情,免費A片,微風成人,情色,成人網站,av女優,成人網站,a片,成人影片,h漫,色情,成人電影,色情,成人電影,色情,成人影片,免費A片,成人影片,免費A片,情色,微風成人,成人網站,av女優,成人網站,a片,成人影片,h漫,成人電影,成人電影,色情,成人影片,免費A片,色情,成人影片,色情,免費A片,


微風成人,情色,成人網站,av女優,成人網站,a片,成人影片,h漫,色情,成人電影,色情,成人電影,色情,成人影片,免費A片,成人影片,免費A片,情色,微風成人,情趣用品,情趣用品,情趣用品,情趣用品,打卡鐘,跳蛋,持久液,成人網站,成人網站,成人網站,成人網站,色情網站,色情網站,色情網站,色情網站,av女優,av女優,av女優,av女優,色情,色情,色情,色情,h漫,h漫,h漫,h漫,sex,sex,sex,sex,成人影片,成人影片,成人影片,成人影片,成人電影,成人電影,成人電影,成人電影,av女優,a片,a片,a片,a片,成人網站,



成人網站,成人網站,成人網站,成人影片,成人影片,成人影片,成人影片,av女優,av女優,av女優,av女優,色情,色情,色情,色情,h漫,h漫,h漫,h漫,sex,sex,sex,sex,情色,情色,情色,情色,黃金回收,黃金回收,黃金回收,黃金回收,借錢,借錢,借錢,借錢,植牙,植牙,植牙,牙醫,牙醫,牙醫,a片,a片,a片,a片,情趣用品,情趣用品,情趣用品,情趣用品,成人網站,成人網站,成人網站,成人網站,成人影片,成人影片,

成人影片
,成人影片,av女優,av女優,av女優,av女優,色情,色情,色情,色情,h漫,h漫,h漫,h漫,sex,sex,sex,sex,情色,情色,情色,情色,黃金,黃金,黃金,黃金,黃金價格,黃金價格,黃金價格,黃金價格,黃金買賣,黃金買賣,黃金買賣,黃金買賣,當舖,當舖,當舖,當舖,鑽石價格,鑽石價格,鑽石價格,鑽石價格,鑽石回收,鑽石回收,鑽石回收,鑽石回收,鑽石買賣,鑽石買賣,鑽石買賣,鑽石買賣,黃金存摺,黃金存摺,黃金存摺,



黃金存摺,辣妹視訊,辣妹視訊,辣妹視訊,辣妹視訊,080視訊聊天室,080視訊聊天室,080視訊聊天室,080視訊聊天室,美女交友,美女交友,美女交友,美女交友,情色視訊,情色視訊,情色視訊,情色視訊,哈啦聊天室,哈啦聊天室,哈啦聊天室,哈啦聊天室,ut聊天室,ut聊天室,ut聊天室,ut聊天室,聊天室,聊天室,聊天室,打卡鐘,火鍋吃到飽,創業加盟,賺錢,吃到飽麻辣鍋

Jeremy said...

Nice work Ray, very impressive. Not ready to covert from jQuery, but very impressed.

Eric said...

How do I include the jar file in my eclipse project?

I'm just new to Java and GWT.
I started learning yesterday :)