<?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="stock-tracker.data-binding">
    <properties>
        <title>Data Binding</title>
    </properties>

    <body>
        <p>
            Data binding is the process of automatically mapping values between a set of user
            interface elements and an internal data representation; for example, from a order entry
            form to a collection of database fields or vice versa. Data binding can help simplify
            development by eliminating some or all of the tedious boilerplate code that often goes
            along with this type of programming.
        </p>

        <p>
            In Pivot, data binding is controlled by the <tt>load()</tt> and <tt>store()</tt>
            methods of the <tt>Component</tt> class:
        </p>

        <p>
            <tt>public void load(Object context) {...}</tt><br/>
            <tt>public void store(Object context) {...}</tt><br/>
        </p>

        <p>
            The <tt>Object</tt> value passed to these methods provides the "bind context". It
            may be either an implementation of the <tt>org.apache.pivot.collections.Dictionary</tt>
            interface or a Java bean value that can be wrapped in an instance of
            <tt>org.apache.pivot.beans.BeanAdapter</tt>, which implements <tt>Dictionary</tt>.
        </p>

        <p>
            The bind context is essentially a collection of name/value pairs representing the data
            to which the components will be bound. Each bindable property of a component can be
            assigned a "bind key" that associates the property with a value in the context. Data is
            imported from the context into the property during a load, and exported from the
            property to the context during a store. Bind contexts may be flat or hierarchical;
            JSON path syntax can be used to retrieve nested values (e.g. "foo.bar.baz" or
            "foo['bar'].baz").
        </p>

        <p>
            Many Pivot components support data binding including text inputs, checkboxes and radio
            buttons, list views, and table views, among others. Some components support binding to
            multiple properties; for example, a caller can bind to both the list data as well as the
            selection state of a <tt>ListView</tt> component.
        </p>

        <h3>Data Binding in Stock Tracker</h3>

        <p>
            The Stock Tracker demo uses data binding to populate the fields in the quote detail
            view. The bind context is actually the row data retrieved from the web query for the
            selected stock. This is why the application requests more data than it seems to need
            from the GET query: the extra fields are used to fill in the data in the detail form.
        </p>

        <p>
            The bound components, in this case, are labels - Stock Tracker maps values from the
            retrieved quote data to the text property of each. The name of the key to use for each
            label is specified via the "textKey" property:
        </p>

        <source type="xml">
            <![CDATA[
            <Form styles="{padding:0, fill:true, showFlagIcons:false, showFlagHighlight:false,
                leftAlignLabels:true}">
                <Form.Section>
                    <bxml:define>
                        <stocktracker:ValueMapping bxml:id="valueMapping"/>
                        <stocktracker:ChangeMapping bxml:id="changeMapping"/>
                        <stocktracker:VolumeMapping bxml:id="volumeMapping"/>
                    </bxml:define>

                    <Label bxml:id="valueLabel" Form.label="%value"
                        textKey="value" textBindMapping="$valueMapping"
                        styles="{horizontalAlignment:'right'}"/>
                    <Label bxml:id="changeLabel" Form.label="%change"
                        textKey="change" textBindMapping="$changeMapping"
                        styles="{horizontalAlignment:'right'}"/>
                    <Label bxml:id="openingValueLabel" Form.label="%openingValue"
                        textKey="openingValue" textBindMapping="$valueMapping"
                        styles="{horizontalAlignment:'right'}"/>
                    <Label bxml:id="highValueLabel" Form.label="%highValue"
                        textKey="highValue" textBindMapping="$valueMapping"
                        styles="{horizontalAlignment:'right'}"/>
                    <Label bxml:id="lowValueLabel" Form.label="%lowValue"
                        textKey="lowValue" textBindMapping="$valueMapping"
                        styles="{horizontalAlignment:'right'}"/>
                    <Label bxml:id="volumeLabel" Form.label="%volume"
                        textKey="volume" textBindMapping="$volumeMapping"
                        styles="{horizontalAlignment:'right'}"/>
                </Form.Section>
            </Form>
            ]]>
        </source>

        <p>
            The actual binding occurs when the selection changes in the table view; the selection
            change handler calls the <tt>refreshDetail()</tt> method in response to a selection
            change event:
        </p>

        <source type="java">
            <![CDATA[
            @SuppressWarnings("unchecked")
            private void refreshDetail() {
                StockQuote stockQuote = null;

                int firstSelectedIndex = stocksTableView.getFirstSelectedIndex();
                if (firstSelectedIndex != -1) {
                    int lastSelectedIndex = stocksTableView.getLastSelectedIndex();

                    if (firstSelectedIndex == lastSelectedIndex) {
                        List<StockQuote> tableData = (List<StockQuote>)stocksTableView.getTableData();
                        stockQuote = tableData.get(firstSelectedIndex);
                    } else {
                        stockQuote = new StockQuote();
                    }
                } else {
                    stockQuote = new StockQuote();
                }

                detailPane.load(new BeanAdapter(stockQuote));
            }
            ]]>
        </source>

        <p>
            Note that the <tt>load()</tt> method is actually called on the detail pane itself
            rather than on the parent container of the detail labels (an instance of <tt>Form</tt>).
            This is because the application also needs to bind to the label that contains the
            company name, which is not a child of the <tt>Form</tt>:
        </p>

        <source type="xml">
            <![CDATA[
            <Label textKey="companyName" styles="{font:{size:12, bold:true}}"/>
            ]]>
        </source>

        <h3>Bind Mappings</h3>

        <p>
            Also note the use of the "textBindMapping" attributes. Bind mappings allow a caller
            to modify the data during the bind process. Incoming data can be converted before it
            is assigned to a property, and outgoing data can be converted before it is stored in
            the bind context. For example, the <tt>ValueMapping</tt> class formats incoming data
            as a currency value in U.S. dollars:
        </p>

        <source type="java">
            <![CDATA[
            public class ValueMapping implements Label.TextBindMapping {
                private static final DecimalFormat FORMAT = new DecimalFormat("$0.00");

                @Override
                public String toString(Object value) {
                    return Float.isNaN((Float)value) ? null : FORMAT.format(value);
                }

                @Override
                public Object valueOf(String text) {
                    throw new UnsupportedOperationException();
                }
            }
            ]]>
        </source>

        <p>
            The <tt>toString() method is called to perform the translation during a <tt>load()</tt>
            operation</tt>. The <tt>valueOf()</tt> method would be called during <tt>store()</tt>,
            but throws <tt>UnsupportedOperationException</tt> because <tt>store()</tt> is never
            called by the Stock Tracker application.
        </p>
    </body>
</document>
