<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you 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.
-->

<document id="web-queries">
    <properties>
        <title>Web Queries</title>
    </properties>

    <body>
        <p>
            "Web queries" are Pivot's native means of communicating with remote data services. They
            are designed primarily to facilitate interaction with JSON-based REST services, but
            they are sufficiently generic to support communication with any type of HTTP-based
            service, using any data format.
        </p>

        <p>
            A web query is represented by an instance of <tt>org.apache.pivot.web.Query</tt>,
            which extends the <tt>Task</tt> class discussed in the previous section. <tt>Query</tt>
            is an abstract class with four concrete subclasses, each of which corresponds to once
            of the four primary HTTP verbs:
        </p>

        <ul>
            <li><p><b>GetQuery</b> - executes an HTTP GET request</p></li>
            <li><p><b>PostQuery</b> - executes an HTTP POST request</p></li>
            <li><p><b>PutQuery</b> - executes an HTTP PUT request</p></li>
            <li><p><b>DeleteQuery</b> - executes an HTTP DELETE request</p></li>
        </ul>

        <p>
            Query string parameters are specified via the query's parameter dictionary, and request
            headers can be specified via the request header dictionary. After the query has been
            executed, response headers are also available via the response header dictionary.
        </p>

        <p>
            All query types have an associated serializer that is used to serialize or deserialize
            the request or response data. <tt>GetQuery</tt> returns an <tt>Object</tt> representing
            the data that was returned by the server, <tt>PostQuery</tt> and <tt>PutQuery</tt>
            define a <tt>value</tt> property that is sent to the server when the query is executed,
            and, by definition, <tt>DeleteQuery</tt> neither accepts nor returns a value.
        </p>

        <p>
            Because they are instances of <tt>Task</tt>, web queries can be (and usually are)
            executed asynchronously, such that the UI doesn't get bogged down waiting for the
            server to respond. The following application demonstrates the use of a
            <tt>GetQuery</tt> to read data from a Yahoo! Pipe. The pipe in this example searches
            for "pizza" within 5 miles of Cambridge, Massachusetts (note that the applet is signed
            to allow it to communicate with the Yahoo web service):
        </p>

        <application class="org.apache.pivot.tutorials.webqueries.WebQueries"
            width="240" height="320">
            <libraries signed="true">
                <library>core</library>
                <library>web</library>
                <library>wtk</library>
                <library>wtk-terra</library>
                <library>tutorials</library>
            </libraries>
        </application>

        <p>The BXML for the application's user interface is as follows:</p>

        <source type="xml" location="org/apache/pivot/tutorials/webqueries/web_queries.bxml">
            <![CDATA[
            <Window title="Web Queries" maximized="true"
                xmlns:bxml="http://pivot.apache.org/bxml"
                xmlns:webqueries="org.apache.pivot.tutorials.webqueries"
                xmlns="org.apache.pivot.wtk">
                <Border styles="{color:10}">
                    <StackPane>
                        <ScrollPane horizontalScrollBarPolicy="fill">
                            <ListView bxml:id="listView">
                                <itemRenderer>
                                    <webqueries:ResultItemRenderer/>
                                </itemRenderer>
                            </ListView>
                        </ScrollPane>

                        <Label bxml:id="loadingLabel" text="Loading..."
                            styles="{horizontalAlignment:'center', verticalAlignment:'center'}"/>
                    </StackPane>
                </Border>
            </Window>
            ]]>
        </source>

        <p>
            Note that the <tt>ListView</tt> defines a custom item renderer; this renderer is used
            to present the name, address, and phone number of the matching items returned by the
            query.
        </p>

        <p>The Java source for the application is as follows:</p>

        <source type="java" location="org/apache/pivot/tutorials/webqueries/WebQueries.java">
            <![CDATA[
            package org.apache.pivot.tutorials.webqueries;

            import org.apache.pivot.beans.BXMLSerializer;
            import org.apache.pivot.collections.List;
            import org.apache.pivot.collections.Map;
            import org.apache.pivot.json.JSON;
            import org.apache.pivot.util.concurrent.Task;
            import org.apache.pivot.util.concurrent.TaskListener;
            import org.apache.pivot.web.GetQuery;
            import org.apache.pivot.wtk.Application;
            import org.apache.pivot.wtk.DesktopApplicationContext;
            import org.apache.pivot.wtk.Display;
            import org.apache.pivot.wtk.Label;
            import org.apache.pivot.wtk.ListView;
            import org.apache.pivot.wtk.TaskAdapter;
            import org.apache.pivot.wtk.Window;

            public class WebQueries implements Application {
                private Window window = null;

                private ListView listView = null;
                private Label loadingLabel = null;

                @Override
                public void startup(Display display, Map<String, String> properties) throws Exception {
                    BXMLSerializer bxmlSerializer = new BXMLSerializer();
                    window = (Window)bxmlSerializer.readObject(WebQueries.class, "web_queries.bxml");

                    listView = (ListView)bxmlSerializer.getNamespace().get("listView");
                    loadingLabel = (Label)bxmlSerializer.getNamespace().get("loadingLabel");

                    // Execute the query:
                    // http://pipes.yahoo.com/pipes/pipe.run?_id=43115761f2da5af5341ae2e56a93d646&_render=json
                    GetQuery getQuery = new GetQuery("pipes.yahoo.com", "/pipes/pipe.run");
                    getQuery.getParameters().put("_id", "43115761f2da5af5341ae2e56a93d646");
                    getQuery.getParameters().put("_render", "json");

                    getQuery.execute(new TaskAdapter<Object>(new TaskListener<Object>() {
                        @Override
                        public void taskExecuted(Task<Object> task) {
                            List<?> items = (List<?>)JSON.get(task.getResult(), "value.items");
                            if (items.getLength() > 0) {
                                listView.setListData(items);
                                loadingLabel.setVisible(false);
                            } else {
                                loadingLabel.setText("No results.");
                            }
                        }

                        @Override
                        public void executeFailed(Task<Object> task) {
                            loadingLabel.setText(task.getFault().getMessage());
                        }
                    }));

                    window.open(display);
                }

                @Override
                public boolean shutdown(boolean optional) {
                    if (window != null) {
                        window.close();
                    }

                    return false;
                }

                @Override
                public void suspend() {
                }

                @Override
                public void resume() {
                }

                public static void main(String[] args) {
                    DesktopApplicationContext.main(WebQueries.class, args);
                }
            }
            ]]>
        </source>

        <p>
            In <tt>startup()</tt>, the application creates the query object and sets the
            appropriate query string parameters to identify the pipe and ensure that the pipe's
            output is returned as JSON data. It then executes the query, wrapping the task listener
            in a task adapter to ensure that the callback is executed on the UI thread. When the
            data is obtained, the "items" list is extracted from it and set as the data of the
            list view component.
        </p>

        <p>
            The application uses a custom list item renderer to present the result data. The
            renderer is defined as follows (it extends <tt>BoxPane</tt> and uses labels to present
            the name, address, and phone numbers of the result items):
        </p>

        <source type="java" location="org/apache/pivot/tutorials/webqueries/ResultItemRenderer.java">
            <![CDATA[
            package org.apache.pivot.tutorials.webqueries;

            import java.awt.Color;
            import java.awt.Font;

            import org.apache.pivot.collections.Map;
            import org.apache.pivot.json.JSON;
            import org.apache.pivot.wtk.BoxPane;
            import org.apache.pivot.wtk.Insets;
            import org.apache.pivot.wtk.Label;
            import org.apache.pivot.wtk.ListView;
            import org.apache.pivot.wtk.Orientation;

            public class ResultItemRenderer extends BoxPane implements ListView.ItemRenderer {
                private Label titleLabel = new Label();
                private Label addressLabel = new Label();
                private Label phoneLabel = new Label();

                public ResultItemRenderer() {
                    super(Orientation.VERTICAL);

                    add(titleLabel);
                    add(addressLabel);
                    add(phoneLabel);

                    getStyles().put("padding", new Insets(3, 2, 3, 2));
                    getStyles().put("spacing", 2);
                }

                @Override
                public void setSize(int width, int height) {
                    super.setSize(width, height);
                    validate();
                }

                @Override
                public String toString(Object item) {
                    return JSON.get(item, "title");
                }

                @Override
                public void render(Object item, int index, ListView listView, boolean selected,
                    boolean checked, boolean highlighted, boolean disabled) {
                    if (item != null) {
                        titleLabel.setText((String)JSON.get(item, "title"));
                        phoneLabel.setText((String)JSON.get(item, "Phone"));

                        Map<String, ?> location = JSON.get(item, "['y:location']");
                        if (location == null) {
                            addressLabel.setText("");
                        } else {
                            String street = JSON.get(location, "street");
                            String city = JSON.get(location, "city");
                            String state = JSON.get(location, "state");
                            addressLabel.setText(street + ", " + city + " " + state);
                        }
                    }

                    Font font = (Font)listView.getStyles().get("font");
                    titleLabel.getStyles().put("font", font.deriveFont(font.getStyle() | Font.BOLD));
                    phoneLabel.getStyles().put("font", font);
                    addressLabel.getStyles().put("font", font);

                    Color color;
                    if (listView.isEnabled() && !disabled) {
                        if (selected) {
                            if (listView.isFocused()) {
                                color = (Color)listView.getStyles().get("selectionColor");
                            } else {
                                color = (Color)listView.getStyles().get("inactiveSelectionColor");
                            }
                        } else {
                            color = (Color)listView.getStyles().get("color");
                        }
                    } else {
                        color = (Color)listView.getStyles().get("disabledColor");
                    }

                    titleLabel.getStyles().put("color", color);
                    phoneLabel.getStyles().put("color", color);
                    addressLabel.getStyles().put("color", color);
                }
            }
            ]]>
        </source>
    </body>
</document>
