blob: 673f488ce5d209aca5789069f8623c9f0bdde74b [file] [log] [blame]
/*
* Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.sample.json.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
import java.util.Set;
/**
* Class that acts as a client to a JSON service. Currently, this client just
* requests a text which contains a JSON encoding of a search result set from
* yahoo. We use a text file to demonstrate how the pieces work without tripping
* on cross-site scripting issues.
*
* If you would like to make this a more dynamic example, you can associate a
* servlet with this example and simply have it hit the yahoo service and return
* the results.
*/
public class JSON implements EntryPoint {
/**
* Class for handling the response text associated with a request for a JSON
* object.
*/
private class JSONResponseTextHandler implements RequestCallback {
public void onError(Request request, Throwable exception) {
displayRequestError(exception.toString());
resetSearchButtonCaption();
}
public void onResponseReceived(Request request, Response response) {
String responseText = response.getText();
try {
JSONValue jsonValue = JSONParser.parseStrict(responseText);
displayJSONObject(jsonValue);
} catch (JSONException e) {
displayParseError(responseText);
}
resetSearchButtonCaption();
}
}
/*
* Class for handling the fetch button's click event.
*/
private class SearchButtonHandler implements ClickHandler {
public void onClick(ClickEvent event) {
jsonTree.setVisible(false);
doFetchURL();
}
}
/*
* Default URL to use to fetch JSON objects. Note that the contents of this
* JSON result were as a result of requesting the following URL:
*
* http://api.search.yahoo.com/ImageSearchService/V1/imageSearch?appid=YahooDemo
* &query=potato&results=2&output=json
*/
private static final String DEFAULT_SEARCH_URL = GWT.getModuleBaseURL()
+ "search-results.js";
/*
* Text displayed on the fetch button when we are in a default state.
*/
private static final String SEARCH_BUTTON_DEFAULT_TEXT = "Search";
/*
* Text displayed on the fetch button when we are waiting for a JSON reply.
*/
private static final String SEARCH_BUTTON_WAITING_TEXT = "Waiting for JSON Response...";
private Tree jsonTree = new Tree();
/*
* RequestBuilder used to issue HTTP GET requests.
*/
private final RequestBuilder requestBuilder = new RequestBuilder(
RequestBuilder.GET, DEFAULT_SEARCH_URL);
private Button searchButton = new Button();
/**
* Entry point for this simple application. Currently, we build the
* application's form and wait for events.
*/
public void onModuleLoad() {
initializeMainForm();
}
/*
* Add the object presented by the JSONValue as a children to the requested
* TreeItem.
*/
private void addChildren(TreeItem treeItem, JSONValue jsonValue) {
JSONArray jsonArray;
JSONObject jsonObject;
JSONString jsonString;
if ((jsonArray = jsonValue.isArray()) != null) {
for (int i = 0; i < jsonArray.size(); ++i) {
TreeItem child = treeItem.addItem(getChildText("["
+ Integer.toString(i) + "]"));
addChildren(child, jsonArray.get(i));
}
} else if ((jsonObject = jsonValue.isObject()) != null) {
Set<String> keys = jsonObject.keySet();
for (String key : keys) {
TreeItem child = treeItem.addItem(getChildText(key));
addChildren(child, jsonObject.get(key));
}
} else if ((jsonString = jsonValue.isString()) != null) {
// Use stringValue instead of toString() because we don't want escaping
treeItem.addItem(SafeHtmlUtils.fromString(jsonString.stringValue()));
} else {
// JSONBoolean, JSONNumber, and JSONNull work well with toString().
treeItem.addItem(getChildText(jsonValue.toString()));
}
}
private void displayError(String errorType, String errorMessage) {
jsonTree.removeItems();
jsonTree.setVisible(true);
TreeItem treeItem = jsonTree.addItem(SafeHtmlUtils.fromString(errorType));
treeItem.addItem(SafeHtmlUtils.fromString(errorMessage));
treeItem.setStyleName("JSON-JSONResponseObject");
treeItem.setState(true);
}
/*
* Update the treeview of a JSON object.
*/
private void displayJSONObject(JSONValue jsonValue) {
jsonTree.removeItems();
jsonTree.setVisible(true);
TreeItem treeItem = jsonTree.addItem(SafeHtmlUtils.fromString("JSON Response"));
addChildren(treeItem, jsonValue);
treeItem.setStyleName("JSON-JSONResponseObject");
treeItem.setState(true);
}
private void displayParseError(String responseText) {
displayError("Failed to parse JSON response", responseText);
}
private void displayRequestError(String message) {
displayError("Request failed.", message);
}
private void displaySendError(String message) {
displayError("Failed to send the request.", message);
}
/*
* Fetch the requested URL.
*/
private void doFetchURL() {
searchButton.setText(SEARCH_BUTTON_WAITING_TEXT);
try {
requestBuilder.sendRequest(null, new JSONResponseTextHandler());
} catch (RequestException ex) {
displaySendError(ex.toString());
resetSearchButtonCaption();
}
}
/*
* Causes the text of child elements to wrap.
*/
private SafeHtml getChildText(String text) {
return new SafeHtmlBuilder()
.appendHtmlConstant("<span style='white-space:normal'>")
.appendEscaped(text)
.appendHtmlConstant("</span>")
.toSafeHtml();
}
/**
* Initialize the main form's layout and content.
*/
private void initializeMainForm() {
searchButton.setStyleName("JSON-SearchButton");
searchButton.setText(SEARCH_BUTTON_DEFAULT_TEXT);
searchButton.addClickHandler(new SearchButtonHandler());
// Avoids showing an "empty" cell
jsonTree.setVisible(false);
// Find out where the host page wants the button.
//
RootPanel searchButtonSlot = RootPanel.get("search");
if (searchButtonSlot == null) {
Window.alert("Please define a container element whose id is 'search'");
return;
}
// Find out where the host page wants the tree view.
//
RootPanel treeViewSlot = RootPanel.get("tree");
if (treeViewSlot == null) {
Window.alert("Please define a container element whose id is 'tree'");
return;
}
// Add both widgets.
//
searchButtonSlot.add(searchButton);
treeViewSlot.add(jsonTree);
}
private void resetSearchButtonCaption() {
searchButton.setText(SEARCH_BUTTON_DEFAULT_TEXT);
}
}