Screenshot
If I had to populate a Gwt-Ext store with data in arbitrary format,
- I could parse data on the client and use a MemoryProxy + ArrayReader or
- I could have the server do the parsing and send JSON data to the client which could then be handled by a JsonReader for populating the store.
This thread at Gwt-Ext.com made me wonder if a good third option would be to have a reader that can be used directly with the store and proxy mechanism and that allows one to write parsing logic in Java on the client. I have attempted a CustomReader that does just that. You can use it with a proxy that fetches remote data.
CustomReader registers an Ext user extension of the same name. Ill refer to them as JavaCustomReader and JSCustomReader to avoid confusion. JavaCustomReader has a static method for registering the user extension.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| ...
static {
init();
}
...
private static native void init()/*-{
$wnd.Ext.namespace("GwtExt");
$wnd.GwtExt.CustomReader = function(meta, recordType, readerInstance) {
meta = meta || {};
this.readerInstance = readerInstance;
$wnd.GwtExt.CustomReader.superclass.constructor.call(this);
};
$wnd.Ext.extend($wnd.GwtExt.CustomReader, $wnd.Ext.data.DataReader, {
read : function(response){
return this.readerInstance.@com.maharana.gwtextcustomreader.client.CustomReader::read(Lcom/google/gwt/core/client/JavaScriptObject;)(response);
},
readRecords : function(o){
alert('Custom reader does not work with local data.');
}
});
}-*/; |
JSCustomReader is passed one additional parameter when it is instantiated. This parameter is a reference to the JavaCustomReader that created it.
1
2
3
| protected native JavaScriptObject create(JavaScriptObject config, JavaScriptObject recordDef)/*-{
return new $wnd.GwtExt.CustomReader(config, recordDef, this);
}-*/; |
JSCustomReader’s read() method is called by a proxy which has finished loading remote data. The proxy expects a return object with the success status, total record count and actual records as its fields. JSCustomReader invokes JavaCustomReader.read() which looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public JavaScriptObject read(JavaScriptObject response) {
ReaderResult r = handleRemoteResponse(new RemoteResponse(response));
JavaScriptObject o = JavaScriptObjectHelper.createObject();
JavaScriptObjectHelper.setAttribute(o, "success", r.success);
JavaScriptObject[] jsRecords = new JavaScriptObject[r.records.length];
for(int i =0; i<r.records.length;i++)
jsRecords[i] = r.records[i].getJsObj();
JavaScriptObjectHelper.setAttribute(o, "records", JavaScriptObjectHelper.convertToJavaScriptArray(jsRecords));
JavaScriptObjectHelper.setAttribute(o, "totalRecords", r.records.length);
return o;
}
// subclass and put your parsing logic here
public abstract ReaderResult handleRemoteResponse(RemoteResponse remoteResponse); |
In turn, it handles the to/from JavaScriptObject stuff and calls handleRemoteResponse(). Users just need to provide their parsing code in this method in derived classes. RemoteResponse and ReaderResult are convenience classes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| class RemoteResponse {
JavaScriptObject response;
public RemoteResponse(JavaScriptObject response) {
this.response = response;
}
public String getResponseText() {
return JavaScriptObjectHelper.getAttribute(response, "responseText");
}
public String getResponseXML() {
return JavaScriptObjectHelper.getAttribute(response, "responseXML");
}
}
class ReaderResult {
public final boolean success;
public final Record[] records;
public ReaderResult(boolean success, Record[] records) {
this.success = success;
this.records = records;
}
} |
This completes CustomReader. As an example, I made a simple CSV parsing reader as mentioned in that thread at Gwt-Ext.com. This reader gets stock quotes from Yahoo finance as a CSV file and creates records out of them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| RecordDef recordDef = new RecordDef(
new FieldDef[]{
new StringFieldDef("name"),
new StringFieldDef("lasttrdate"),
new StringFieldDef("lasttrval")
}
);
CustomReader c = new CustomReader(recordDef,1){
public ReaderResult handleRemoteResponse(RemoteResponse remoteResponse) {
String csvalue = remoteResponse.getResponseText();
String[] lines = csvalue.split("\n");
Record[] records = new Record[lines.length];
for(int i=0; i<lines.length; ++i)
{
String[] columns = lines[i]. replace('\"', ' ').split(",");
records[i] = this.recordDef.createRecord(columns);
}
return new ReaderResult(true, records);
}
}; |
For getting the file from Yahoo, I made a utility Servlet Redirect.java which fetches the contents of a url and passes it to the client.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String queryString = request.getQueryString();
String address = queryString.split("url=")[1];
URL url = new URL(address);
PrintStream out = new PrintStream(response.getOutputStream());
String inputLine = "";
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
while((inputLine = br.readLine()) != null)
{
// System.out.println(inputLine);
out.println(inputLine);
}
out.close();
} |
It is invoked by an HttpProxy on the client as follows:
1
2
3
4
5
6
| ...
String url = "http://download.finance.yahoo.com/d/quotes.csv?s=INFY+XOM+BBDb.TO+JNJ+MSFT+GOOG+YHOO&f=nd1l1";
DataProxy proxy = new HttpProxy("redirect?url=" + url);
Store store = new Store(proxy, c); // CustomReader c from above
... |
Once we have the utility servlet and CustomReader as part of the project, I think it will be relatively easier to populate stores with any arbitrary format data. Feel free to drop your comments / suggestions. I would love to hear them.
Download Eclipse project from Gwt-Ext.com or Rapidshare
Reference: GwtProxy by michal.bergmann