/*
 * 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.
 *
 * $Header:$
 */
package org.apache.beehive.netui.tags.databinding.repeater;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.*;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;

import org.apache.beehive.netui.script.common.DataAccessProviderStack;
import org.apache.beehive.netui.script.common.IDataAccessProvider;
import org.apache.beehive.netui.tags.AbstractClassicTag;
import org.apache.beehive.netui.tags.ExpressionHandling;
import org.apache.beehive.netui.tags.databinding.repeater.pad.PadContext;
import org.apache.beehive.netui.util.Bundle;
import org.apache.beehive.netui.util.exception.LocalizedUnsupportedOperationException;
import org.apache.beehive.netui.util.internal.InternalStringBuilder;
import org.apache.beehive.netui.util.iterator.IteratorFactory;
import org.apache.beehive.netui.util.logging.Logger;

/**
 * The &lt;netui-data:repeater> tag is a markup-generic tag that repeats over a data set.
 * The repeater tag set is used to render data from a data set into a page.  The repeater
 * itself does not render any markup. Instead, the markup from its contained tags is
 * rendered to create the content generated by this tag.  The tags in the repeater tag
 * set are as follows:
 * <table border="1" cellspacing="0" cellpadding="5" width="75%">
 * <tr><td><b>Tag</b></td><td><b>Description</b></td></tr>
 * <tr><td>{@link RepeaterHeader}</td><td>Renders once in the {@link #HEADER} state.</td></tr>
 * <tr><td>{@link RepeaterItem}</td><td>Renders once in the {@link #ITEM} state.</td></tr>
 * <tr><td>{@link RepeaterFooter}</td><td>Renders once in the {@link #FOOTER} state.</td></tr>
 * <tr><td>{@link org.apache.beehive.netui.tags.databinding.repeater.pad.Pad}</td><td>Used to convert irregular data sets into regular data sets through padding or truncating the output.</td></tr>
 * </table>
 * <p>The repeater can render in two modes; the first mode is a simple mode where the body of
 * the repeater is rendered once for each item in the data set.  In this case, none of the
 * other tags above are present in the repeater body.  For example, the following will
 * render an unordered HTML list of items that are list items which contain the lastName, firstName
 * of the current "customer" in the data set.</p>
 * <pre>
 * &lt;ul&gt;
 * &lt;netui-data:repeater dataSource="pageInput.customers"&gt;
 *     &lt;li&gt;&lt;netui:span value="${container.item.lastName}, ${container.item.firstName}"/&gt;&lt;/li&gt;
 * &lt;/netui-data:repeater&gt;
 * &lt;/ul&gt;
 * </pre>
 * <p>The second mode is a more structured mode
 * of rendering where the tags above are used to delineate iteration boundaries on the body of
 * a &lt;netui-data:repeater> tag.  In this case, if one of the above tags is present,
 * any content directly in the body of the repeater is not rendered; rather, the content
 * inside the structured tags of the repeater is rendered.</p>
 * <p>For example, the following will render the same output as the example
 * shown above, but it uses the structured tags for rendering the
 * <code>pageFlow.customers</code> expression:</p>
 * <pre>
 * &lt;netui-data:repeater dataSource="pageInput.customers"&gt;
 *     &lt;netui-data:repeaterHeader&gt;
 *         &lt;ul&gt;
 *     &lt;/netui-data:repeaterHeader&gt;
 *     &lt;netui-data:repeaterItem&gt;
 *         &lt;li&gt;&lt;netui:span value="${container.item.lastName}, ${container.item.firstName}"/&gt;&lt;/li&gt;
 *     &lt;/netui-data:repeaterItem&gt;
 *     &lt;netui-data:repeaterFooter&gt;
 *         &lt;/ul&gt;
 *     &lt;/netui-data:repeaterFooter&gt;
 * &lt;/netui-data:repeater&gt;
 * </pre>
 *
 * @jsptagref.tagdescription <p>Iterates over a data set to render it as HTML.
 * The HTML is specified either directly within the the body of
 * &lt;netui-data:repeater> tag or within an associated set of "helper" tags.
 * The "helper" tags are listed below.
 * <p/>
 * <blockquote>
 * <table border="1" cellspacing="0" cellpadding="5" width="90%">
 * <tr><td><b>Tag</b></td><td><b>Description</b></td></tr>
 * <tr><td>{@link RepeaterHeader}</td><td>Renders once at the start of the iteration.</td></tr>
 * <tr><td>{@link RepeaterItem}</td><td>Renders once for each iteration.</td></tr>
 * <tr><td>{@link RepeaterFooter}</td><td>Renders once at the end of the iteration.</td></tr>
 * <tr><td>{@link org.apache.beehive.netui.tags.databinding.repeater.pad.Pad}</td><td>Used to convert
 * irregular data sets into regular data sets through padding
 * or truncating the output.</td></tr>
 * </table>
 * </blockquote>
 * <p/>
 * <p>The &lt;netui-data:repeater> tag can render in two modes; the first mode is a simple mode where the body of
 * the &lt;netui-data:repeater> tag is rendered once for each item in the data set.  In this case, none of the
 * other tags above are present in the repeater body.  For example, the following will
 * render the items in the "customers" data set as an unordered HTML list.</p>
 * <p/>
 * <pre>     &lt;ul&gt;
 *         &lt;netui-data:repeater dataSource="pageInput.customers"&gt;
 *             &lt;li&gt;&lt;netui:span value="${container.item.lastName}, ${container.item.firstName}"/&gt;&lt;/li&gt;
 *         &lt;/netui-data:repeater&gt;
 *     &lt;/ul&gt;</pre>
 * <p/>
 * <p>The second mode is a more structured mode
 * of rendering where the "helper" tags
 * are used to define the rendering of the data set.  In this case, if one of the helper tags
 * is present,
 * any HTML markup directly in the body of the &lt;netui-data:repeater> tag is not rendered; rather, the HTML markup
 * inside the helper tags is rendered.</p>
 * <p>For example, the following will render the same output as the example
 * shown above, but it uses the "helper" tags for rendering the
 * HTML markup:</p>
 * <p/>
 * <pre>
 *     &lt;netui-data:repeater dataSource="pageInput.customers"&gt;
 *         &lt;netui-data:repeaterHeader&gt;
 *             &lt;ul&gt;
 *         &lt;/netui-data:repeaterHeader&gt;
 *         &lt;netui-data:repeaterItem&gt;
 *             &lt;li&gt;&lt;netui:span value="${container.item.lastName}, ${container.item.firstName}"/&gt;&lt;/li&gt;
 *         &lt;/netui-data:repeaterItem&gt;
 *         &lt;netui-data:repeaterFooter&gt;
 *             &lt;/ul&gt;
 *         &lt;/netui-data:repeaterFooter&gt;
 *     &lt;/netui-data:repeater&gt;</pre>
 * @example The following sample renders the data set as an HTML table.  The table has two columns, "index" and "name",
 * and each iteration over the data set is rendered as a row of the table.
 * <p/>
 * <pre>    &lt;netui-data:repeater dataSource="pageInput.myDataSet">
 *        &lt;netui-data:repeaterHeader>
 *            &lt;table border="1">
 *                &lt;tr>
 *                    &lt;td>&lt;b>index&lt;/b>&lt;/td>
 *                    &lt;td>&lt;b>name&lt;/b>&lt;/td>
 *                &lt;/tr>
 *        &lt;/netui-data:repeaterHeader>
 *        &lt;netui-data:repeaterItem>
 *            &lt;tr>
 *                &lt;td>
 *                    &lt;netui:span value="${container.index}" />
 *                &lt;/td>
 *                &lt;td>
 *                    &lt;netui: value="${container.item}" />
 *                &lt;/td>
 *            &lt;/tr>
 *        &lt;/netui-data:repeaterItem>
 *        &lt;netui-data:repeaterFooter>
 *            &lt;/table>
 *        &lt;/netui-data:repeaterFooter>
 *    &lt;/netui-data:repeater></pre>
 * @netui:tag name="repeater" description="A markup-generic tag that repeats over a data set, and renders the data onto the page."
 */
public class Repeater
        extends AbstractClassicTag
        implements IDataAccessProvider, TryCatchFinally {

    private static final Logger LOGGER = Logger.getInstance(Repeater.class);

    /**
     * A Repeater rendering state that signals the beginning of repeater rendering.
     */
    public static final int INIT = 0;

    /**
     * A Repeater rendering state that signals the rendering of the HEADER.
     * The body renders in the HEADER state once.
     */
    public static final int HEADER = 1;

    /**
     * A Repeater rendering state that signals the rendering of the ITEM.
     * The body renders in the ITEM state once for each item in the
     * data set.
     */
    public static final int ITEM = 2;

    /**
     * A Repeater rendering state that signals the rendering of the FOOTER.
     * The body renders in the FOOTER state once.
     */
    public static final int FOOTER = 3;

    /**
     * A Repeater rendering state that signals the end of repeater rendering.
     */
    public static final int END = 4;

    private boolean _ignoreNulls = false;
    private boolean _haveKids = false;
    private boolean _containerInPageContext = false;
    private int _currentIndex = -1;
    private int _renderedItems = 0;
    private int _renderState = INIT;
    private Object _defaultText = null;
    private Object _currentItem = null;
    private String _dataSource = null;
    private Iterator _iterator = null;
    private PadContext _padContext = null;
    private InternalStringBuilder _contentBuffer = null;

    /**
     * Get the name of this tag.  This is used to identify the type of this tag
     * for reporting tag errors.
     *
     * @return a constant String representing the name of this tag.
     */
    public String getTagName() {
        return "Repeater";
    }

    /**
     * Set a boolean that describes whether the repeater should ignore null
     * items encountered while iterating over a data set.
     *
     * @param ignoreNulls whether or not to ignore nulls
     * @jsptagref.attributedescription
     * Boolean. If set to true, any null iteration items in the data set will be ignored.
     * @jsptagref.attributesyntaxvalue <i>boolean_ignoreNulls</i>
     * @netui:attribute required="false"
     */
    public void setIgnoreNulls(boolean ignoreNulls) {
        _ignoreNulls = ignoreNulls;
    }

    /**
     * @param padContext
     */
    public void setPadContext(PadContext padContext) {
        if(_padContext == null)
            _padContext = padContext;

        LOGGER.debug("Repeater has a padContext with text: " + _padContext);

        return;
    }

    /**
     * Set the text that will be rendered if the dataSource expression
     * references a null object and the defaultText attribute is non-null.
     *
     * @param defaultText the default text
     * @jsptagref.attributedescription
     * The text to render if the <code>dataSource</code> attribute references a null data set.
     * @jsptagref.attributesyntaxvalue <i>string_defaultText</i>
     * @netui:attribute required="false" rtexprvalue="true"
     */
    public void setDefaultText(Object defaultText) {
        _defaultText = defaultText;
    }

    /**
     * Get the index of the current iteration through the body of this tag.  This
     * data can be accessed using the expression <code>container.index</code>
     * on an attribute of a databindable NetUI tag that is contained within the
     * repeating body of this tag.  This expression is only valid when the dataset
     * is being rendered.
     *
     * @return the integer index of the current data item in the data set
     * @see org.apache.beehive.netui.script.common.IDataAccessProvider
     */
    public int getCurrentIndex() {
        return _currentIndex;
    }

    /**
     * Get the item that is currently being rendered by this repeating tag.
     * This can be accessed using the expression <code>expression.item</code>
     * on an attribute of a databindable NetUI tag that is contained within
     * the repeating body of this tag.  The expression is only valid when the dataset
     * is being rendered.
     *
     * @return the current item in the data set
     * @see org.apache.beehive.netui.script.common.IDataAccessProvider
     */
    public Object getCurrentItem() {
        return _currentItem;
    }

    /**
     * Get the metadata for the current item.  This method is not supported by
     * this tag.
     *
     * @throws UnsupportedOperationException this tag does not support this method from the IDataAccessProvider interface
     * @see org.apache.beehive.netui.script.common.IDataAccessProvider
     */
    public Object getCurrentMetadata() {
        LocalizedUnsupportedOperationException uoe =
                new LocalizedUnsupportedOperationException("The " + getTagName() + "does not export metadata for its iterated items.");
        uoe.setLocalizedMessage(Bundle.getErrorString("Tags_DataAccessProvider_metadataUnsupported", new Object[]{getTagName()}));
        throw uoe;
    }

    /**
     * Get the parent IDataAccessProvider for this tag.  If this tag is contained within
     * a IDataAccessProvider, the containing IDataAccessProvider is available through the
     * expression <code>container.container</code>.  Any valid properties of the
     * parent IDataAccessProvider can be accessed through this expression.  This method
     * will return null if there is no parent IDataAccessProvider
     *
     * @return a containing IDataAccessProvider if one exists, null otherwise.
     * @see org.apache.beehive.netui.script.common.IDataAccessProvider
     */
    public IDataAccessProvider getProviderParent() {
        IDataAccessProvider dap = (IDataAccessProvider)SimpleTagSupport.findAncestorWithClass(this, IDataAccessProvider.class);
        return dap;
    }

    /**
     * Get the current render state for the repeater.  This tag is used by child tags
     * to access the current location in the repeater's rendering lifecycle.
     *
     * @return an integer that represents the current state of the grid; this is one
     *         of {@link #INIT}, {@link #HEADER}, {@link #ITEM},{@link #FOOTER}, or {@link #END}.
     */
    public int getRenderState() {
        return _renderState;
    }

    /**
     * Add content to the content that is being buffered by this tag.  All content
     * written by the body of this tag is added to this buffer.  The buffer is rendered
     * at the end of the tag's lifecycle if no fatal errors have occurred during this
     * tag's lifecycle.
     *
     * @param content content that this tag should render.
     */
    public void addContent(String content) {
        if(_contentBuffer == null) {
            int size = (content != null ? (5 * content.length()) : 1024);
            _contentBuffer = new InternalStringBuilder(size);
        }

        _contentBuffer.append(content);
    }

    /**
     * Method used by tags in the repeater tag set to register the presence of a contained
     * tag.  When registered, the repeater will change the way in which it renders to
     * either use structured or non-structured rendering.
     * @param repeaterComponent {@link RepeaterComponent} to register with the Repeater parent
     */
    public void registerChildTag(RepeaterComponent repeaterComponent) {
        _haveKids = true;
    }

    /**
     * Start rendering the repeater.
     * @return {@link #SKIP_BODY} if an error occurs; {@link #EVAL_BODY_BUFFERED} otherwise
     * @throws JspException if an error occurs that can not be reported in the page
     */
    public int doStartTag()
            throws JspException {
        Object source = evaluateDataSource();

        // report any errors that may have occured 
        if(hasErrors())
            return SKIP_BODY;

        _renderState = INIT;

        boolean empty = false;
        if(source != null) {
            _iterator = IteratorFactory.createIterator(source);
            if(_iterator == null) {
                LOGGER.warn(Bundle.getString("Tags_Repeater_nullIterator"));
                _iterator = Collections.EMPTY_LIST.iterator();
            }

            if(_iterator.hasNext()) {
                _currentIndex = 0;
                _currentItem = _iterator.next();

                if(_ignoreNulls && _currentItem == null) {
                    /*
                      doStartTag doesn't know if the repeater is structured or unstructured
                      thus, if ignoreNulls is true, it's going to make an attempt to go
                      through the body with a non-null item.  if there are no non-null
                      items in the data structure, the doAfterBody method will handle
                      this correctly, but a data structure with a null first item
                      will render the same as a data structure with a null second item
                     */
                    advanceToNonNullItem();

                    /*
                       this null check needs to re-run here because the advanceToNonNullItem method
                       side-effects the _currentItem.
                     */
                    if(_currentItem == null)
                        empty = true;
                }
            }
            /* there is no data set of there are zero items in the iterator */
            else empty = true;
        }
        /* the dataSource evaluated to null */
        else {
            _iterator = Collections.EMPTY_LIST.iterator();
            empty = true;
        }

        if(empty) {
            /* if the defaultText attribute is non-null, it will be evaluated as an expression and rendered to the page */
            if(_defaultText != null)
                addContent(_defaultText.toString());
            return SKIP_BODY;
        }
        else {
            DataAccessProviderStack.addDataAccessProvider(this, pageContext);
            _containerInPageContext = true;
            return EVAL_BODY_BUFFERED;
        }
    }

    /**
     * <p>
     * Continue rendering the repeater changing the render state or advancing to a new data item
     * as needed.
     * </p>
     * @return {@link #SKIP_BODY} if an error occurs or the data set has been rendered; {@link #EVAL_BODY_AGAIN} otherwise
     */
    public int doAfterBody() {

        if(hasErrors())
            return SKIP_BODY;

        LOGGER.debug("structured repeater: " + _haveKids + " render state: " + renderStateToString(_renderState));

        /*
          structured rendering of the repeater; body content is ignored and real
          content is rendered through cooperating nested tags
         */
        if(_haveKids)
            return renderStructured();
        /*
          unstructured rendering of the repeater; this means that there isn't
          a repeater(Header|Item|Footer) inside the body of the repeater.
         */
        else {
            if(bodyContent != null) {
                addContent(bodyContent.getString());
                bodyContent.clearBody();
            }

            if(_iterator.hasNext()) {
                _currentIndex++;
                _currentItem = _iterator.next();

                if(_ignoreNulls && _currentItem == null) {
                    advanceToNonNullItem();

                    /* ignoring null items and no more non-null items, so skip to doEndTag() */
                    if(_currentItem == null)
                        return SKIP_BODY;
                }

                /* found another item; re-render the repeater's body */
                return EVAL_BODY_AGAIN;
            }
            /* no more items; skip to doEndTag() */
            else return SKIP_BODY;
        }
    }

    /**
     * Complete rendering the repeater.
     * @return {@link #EVAL_PAGE}
     * @throws JspException if an error occurs that can not be reported in the page
     */
    public int doEndTag()
            throws JspException {

        if(hasErrors())
            reportErrors();
        else if(_contentBuffer != null)
            write(_contentBuffer.toString());

        return EVAL_PAGE;
    }

    public void doFinally() {
        localRelease();
    }

    public void doCatch(Throwable t)
            throws Throwable {
        throw t;
    }

    /**
     * Reset all of the fields of this tag.
     */
    protected void localRelease() {
        super.localRelease();
        _currentItem = null;
        _currentIndex = -1;
        _iterator = null;
        _defaultText = null;
        _renderState = INIT;
        _haveKids = false;
        _contentBuffer = null;
        _padContext = null;
        _ignoreNulls = false;
        _renderedItems = 0;
        if(_containerInPageContext) {
            DataAccessProviderStack.removeDataAccessProvider(pageContext);
            _containerInPageContext = false;
        }
    }

    /**
     * Render a the repeater using the full repeater lifecycle.  This
     * method is executed after each pass through the body if there
     * are tags from the repeater tag set in the body of this repeater.
     * This method ensures that the repeater tag runs as a full state
     * machine for these tags.
     *
     * @return EVAL_BODY_AGAIN unless the lifecycle has completed; then return SKIP_BODY
     */
    /* todo: perf -- optimize the number of trips through the body by ignoring the header / footer when necessary */
    private int renderStructured() {
        if(LOGGER.isDebugEnabled() && _padContext != null)
            LOGGER.debug("\ncurrentIndex: " + _currentIndex + "\n" +
                    "checkMaxRepeat: " + _padContext.checkMaxRepeat(_currentIndex) + "\n" +
                    "checkMinRepeat: " + _padContext.checkMinRepeat(_currentIndex) + "\n");

        if(_renderState == INIT) {
            _renderState = HEADER;
            return EVAL_BODY_AGAIN;
        }

        if(_renderState == HEADER) {
            assert _renderedItems == 0;

            /* this would only happen if Pad.maxRepeat == 0 */
            if(_padContext != null && _padContext.checkMaxRepeat(_renderedItems)) {
                _renderState = FOOTER;
                return EVAL_BODY_AGAIN;
            }

            if(_currentItem == null && _ignoreNulls) {
                advanceToNonNullItem();

                /* no non-null item was found; render the footer */
                if(_currentItem == null) {
                    doPadding();
                    // render the header
                    _renderState = FOOTER;
                }
                /* non-null item found; it's not the 0th item; render it */
                else _renderState = ITEM;
            }
            /* 0th item is non-null; render it */
            else _renderState = ITEM;

            return EVAL_BODY_AGAIN;
        }

        if(_renderState == ITEM) {
            _renderedItems++;

            /* check that the maximum number of items to render has *not* been reached */
                if(_iterator.hasNext() && (_padContext == null || (_padContext != null && !_padContext.checkMaxRepeat(_renderedItems)))) {
                _currentIndex++;
                _currentItem = _iterator.next();

                if(_ignoreNulls && _currentItem == null) {
                    advanceToNonNullItem();

                    /* last item */
                    if(_currentItem == null) {
                        doPadding();

                        /* render the header */
                        _renderState = FOOTER;
                        return EVAL_BODY_AGAIN;
                    }
                }

                /* if _ignoreNulls is false, the _currentItem may be null here */
                return EVAL_BODY_AGAIN;
            }
            /*
             have finished rendering items for some reason:
              1) there isn't a next item
              2) reached the maximum number of items to render
              So:
              1) pad if necessary
              2) render the footer
             */
            else {
                doPadding();

                _renderState = FOOTER;
                return EVAL_BODY_AGAIN;
            }
        }

        if(_renderState == FOOTER) {
            _renderState = END;
            return SKIP_BODY;
        }

        return SKIP_BODY;
    }

    /**
     * <p/>
     * Internal utility method.
     * </p>
     * </p>
     * This is called in places where the repeater needs to move to the next
     * non-null item in the data set to render.  This occurs when the
     * current data item is null and the <code>ignoreNulls</code>
     * flag has been set to skip rendering null items in the data set.
     * </p>
     * <p/>
     * This method side-effects to advance the iterator to the next
     * non-null item or the end if there are zero remaining non-null
     * items.
     * </p>
     * <p/>
     * At the end, the <code>currentItem</code> may be null, and the
     * <code>currentIndex</code> will reference either the integer
     * location in the data structure of the non-null data item, or
     * it will reference the end of the data structure.
     * </p>
     */
    private final void advanceToNonNullItem() {
        assert _iterator != null;
        assert _currentItem == null;

        while(_iterator.hasNext() && _currentItem == null) {
            _currentItem = _iterator.next();
            _currentIndex++;
        }
    }

    /**
     * When using the repeater's pad tag, it is possible to require a minimum number of
     * items render in the repeater.  This method pads out the number of items until it
     * reaches the {@link org.apache.beehive.netui.tags.databinding.repeater.pad.PadContext}'s
     * <code>minRepeat</code> property.
     */
    private final void doPadding() {
        if(_padContext != null && !_padContext.checkMinRepeat(_renderedItems)) {
            /*
              since padding is now running, un-set the current item so that the last
              item isn't accessible during any later data binding
             */
            _currentItem = null;

            for(int i = _renderedItems; !_padContext.checkMinRepeat(i); i++) {
                _currentIndex++;
                addContent(_padContext.getPadText());
            }
        }
    }

    /**
     * An internal method that turns the current render state into a string for debugging.
     */
    private static final String renderStateToString(int state) {
        switch(state) {
            case INIT:
                return "INIT";
            case HEADER:
                return "HEADER";
            case ITEM:
                return "ITEM";
            case FOOTER:
                return "FOOTER";
            case END:
                return "END";
            default:
                return "INVALID STATE";
        }
    }

    /**
     * Return an <code>ArrayList</code> which represents a chain of <code>INameInterceptor</code>
     * objects.  This method by default returns <code>null</code> and should be overridden
     * by objects that support naming.
     * @return an <code>ArrayList</code> that will contain <code>INameInterceptor</code> objects.
     */
    protected List getNamingChain() {
        return AbstractClassicTag.DefaultNamingChain;
    }

    /**
     * Sets the tag's data source (can be an expression).
     * @param dataSource - the data source
     * @jsptagref.attributedescription <p>The <code>dataSource</code> attribute determines both
     * (1) the source of populating data for the tag and
     * (2) the object to which the tag submits data.
     *
     * <p>For example, assume that the Controller file (= JPF file) contains
     * a Form Bean with the property foo.  Then the following &lt;netui:textBox> tag will
     * (1) draw populating data from the Form Bean's foo property and (2)
     * submit user defined data to the same property.
     *
     * <p>&nbsp;&nbsp;&nbsp;&nbsp;<code>&lt;netui:textBox dataSource="actionForm.foo" /></code>
     *
     * <p>The <code>dataSource</code> attribute takes either a data binding expression or
     * the name of a Form Bean property.  In the
     * above example, <code>&lt;netui:textBox dataSource="foo" /></code>
     * would have the exactly same behavior.
     *
     * <p>When the tag is used to submit data, the data binding expression must
     * refer to a Form Bean property.
     * In cases where the tag is not used to submit data, but is used for
     * displaying data only, the data
     * binding expression need not refer to a Form Bean property.  For example,
     * assume that myIterativeData is a member variable on
     * the Controller file ( = JPF file).  The following &lt;netui-data:repeater>
     * tag draws its data from myIterativeData.
     *
     * <p>&nbsp;&nbsp;&nbsp;&nbsp;<code>&lt;netui-data:repeater dataSource="pageFlow.myIterativeData"></code>
     * @jsptagref.attributesyntaxvalue <i>expression_datasource</i>
     * @netui:attribute required="true"
     */
    public void setDataSource(String dataSource) {
        _dataSource = dataSource;
    }

    /**
     * Gets the tag's data source (can be an expression).
     * @return the data source
     */
    public String getDataSource() {
        return "{" + _dataSource + "}";
    }

    /**
     * Return the Object that is represented by the specified data source.
     * @return Object
     * @throws JspException
     */
    private Object evaluateDataSource()
            throws JspException {
        ExpressionHandling expr = new ExpressionHandling(this);
        String dataSource = getDataSource();
        String ds = expr.ensureValidExpression(dataSource, "dataSource", "DataSourceError");
        if (ds == null)
            return null;

        /* have a valid expression */
        return expr.evaluateExpression(dataSource, "dataSource", pageContext);
    }
}
