| /* |
| * 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.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 { |
| /** |
| * 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.parse(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); |
| } |
| } |