Subversion-Projekte lars-tiefland.prado

Revision

Blame | Letzte Änderung | Log anzeigen | RSS feed

<?php
/**
 * TListControl class file
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.pradosoft.com/
 * @copyright Copyright &copy; 2005-2008 PradoSoft
 * @license http://www.pradosoft.com/license/
 * @version $Id: TListControl.php 2591 2008-12-19 20:31:18Z carlgmathisen $
 * @package System.Web.UI.WebControls
 */

/**
 * Includes the supporting classes
 */
Prado::using('System.Web.UI.WebControls.TDataBoundControl');
Prado::using('System.Web.UI.WebControls.TListItem');
Prado::using('System.Collections.TAttributeCollection');
Prado::using('System.Util.TDataFieldAccessor');


/**
 * TListControl class
 *
 * TListControl is a base class for list controls, such as {@link TListBox},
 * {@link TDropDownList}, {@link TCheckBoxList}, etc.
 * It manages the items and their status in a list control.
 * It also implements how the items can be populated from template and
 * data source.
 *
 * The property {@link getItems} returns a list of the items in the control.
 * To specify or determine which item is selected, use the
 * {@link getSelectedIndex SelectedIndex} property that indicates the zero-based
 * index of the selected item in the item list. You may also use
 * {@link getSelectedItem SelectedItem} and {@link getSelectedValue SelectedValue}
 * to get the selected item and its value. For multiple selection lists
 * (such as {@link TCheckBoxList} and {@link TListBox}), property
 * {@link getSelectedIndices SelectedIndices} is useful.
 *
 * TListControl implements {@link setAutoPostBack AutoPostBack} which allows
 * a list control to postback the page if the selections of the list items are changed.
 * The {@link setCausesValidation CausesValidation} and {@link setValidationGroup ValidationGroup}
 * properties may be used to specify that validation be performed when auto postback occurs.
 *
 * There are three ways to populate the items in a list control: from template,
 * using {@link setDataSource DataSource} and using {@link setDataSourceID DataSourceID}.
 * The latter two are covered in {@link TDataBoundControl}. To specify items via
 * template, using the following template syntax:
 * <code>
 * <com:TListControl>
 *   <com:TListItem Value="xxx" Text="yyy" >
 *   <com:TListItem Value="xxx" Text="yyy" Selected="true" >
 *   <com:TListItem Value="xxx" Text="yyy" >
 * </com:TListControl>
 * </code>
 *
 * When {@link setDataSource DataSource} or {@link setDataSourceID DataSourceID}
 * is used to populate list items, the {@link setDataTextField DataTextField} and
 * {@link setDataValueField DataValueField} properties are used to specify which
 * columns of the data will be used to populate the text and value of the items.
 * For example, if a data source is as follows,
 * <code>
 * $dataSource=array(
 *    array('name'=>'John', 'age'=>31),
 *    array('name'=>'Cary', 'age'=>28),
 *    array('name'=>'Rose', 'age'=>35),
 * );
 * </code>
 * setting {@link setDataTextField DataTextField} and {@link setDataValueField DataValueField}
 * to 'name' and 'age' will make the first item's text be 'John', value be 31,
 * the second item's text be 'Cary', value be 28, and so on.
 * The {@link setDataTextFormatString DataTextFormatString} property may be further
 * used to format how the item should be displayed. See {@link formatDataValue()}
 * for an explanation of the format string.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TListControl.php 2591 2008-12-19 20:31:18Z carlgmathisen $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
abstract class TListControl extends TDataBoundControl implements IDataRenderer
{
        /**
         * @var TListItemCollection item list
         */
        private $_items=null;
        /**
         * @var boolean whether items are restored from viewstate
         */
        private $_stateLoaded=false;
        /**
         * @var mixed the following selection variables are used
         * to keep selections when Items are not available
         */
        private $_cachedSelectedIndex=-1;
        private $_cachedSelectedValue=null;
        private $_cachedSelectedIndices=null;
        private $_cachedSelectedValues=null;

        /**
         * @return string tag name of the list control
         */
        protected function getTagName()
        {
                return 'select';
        }

        /**
         * @return boolean whether to render javascript.
         */
        public function getEnableClientScript()
        {
                return $this->getViewState('EnableClientScript',true);
        }

        /**
         * @param boolean whether to render javascript.
         */
        public function setEnableClientScript($value)
        {
                $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true);
        }

        /**
         * Adds attributes to renderer.
         * @param THtmlWriter the renderer
         */
        protected function addAttributesToRender($writer)
        {
                $page=$this->getPage();
                $page->ensureRenderInForm($this);
                if($this->getIsMultiSelect())
                        $writer->addAttribute('multiple','multiple');
                if($this->getEnabled(true))
                {
                        if($this->getAutoPostBack()
                                && $this->getEnableClientScript()
                                && $page->getClientSupportsJavaScript())
                        {
                                $this->renderClientControlScript($writer);
                        }
                }
                else if($this->getEnabled())
                        $writer->addAttribute('disabled','disabled');
                parent::addAttributesToRender($writer);
        }

        /**
         * Renders the javascript for list control.
         */
        protected function renderClientControlScript($writer)
        {
                $writer->addAttribute('id',$this->getClientID());
                $this->getPage()->getClientScript()->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions());
        }

        /**
         * Gets the name of the javascript class responsible for performing postback for this control.
         * Derived classes may override this method and return customized js class names.
         * @return string the javascript class name
         */
        protected function getClientClassName()
        {
                return 'Prado.WebUI.TListControl';
        }

        /**
         * @return array postback options for JS postback code
         */
        protected function getPostBackOptions()
        {
                $options['ID'] = $this->getClientID();
                $options['CausesValidation'] = $this->getCausesValidation();
                $options['ValidationGroup'] = $this->getValidationGroup();
                $options['EventTarget'] = $this->getUniqueID();
                return $options;
        }

        /**
         * Adds object parsed from template to the control.
         * This method adds only {@link TListItem} objects into the {@link getItems Items} collection.
         * All other objects are ignored.
         * @param mixed object parsed from template
         */
        public function addParsedObject($object)
        {
                // Do not add items from template if items are loaded from viewstate
                if(!$this->_stateLoaded && ($object instanceof TListItem))
                {
                        $index=$this->getItems()->add($object);
                        if(($this->_cachedSelectedValue!==null && $this->_cachedSelectedValue===$object->getValue()) || ($this->_cachedSelectedIndex===$index))
                        {
                                $object->setSelected(true);
                                $this->_cachedSelectedValue=null;
                                $this->_cachedSelectedIndex=-1;
                        }
                }
        }

        /**
         * Performs databinding to populate list items from data source.
         * This method is invoked by dataBind().
         * You may override this function to provide your own way of data population.
         * @param Traversable the data
         */
        protected function performDataBinding($data)
        {
                $items=$this->getItems();
                if(!$this->getAppendDataBoundItems())
                        $items->clear();
                $textField=$this->getDataTextField();
                if($textField==='')
                        $textField=0;
                $valueField=$this->getDataValueField();
                if($valueField==='')
                        $valueField=1;
                $textFormat=$this->getDataTextFormatString();
                $groupField=$this->getDataGroupField();
                foreach($data as $key=>$object)
                {
                        $item=$items->createListItem();
                        if(is_array($object) || is_object($object))
                        {
                                $text=TDataFieldAccessor::getDataFieldValue($object,$textField);
                                $value=TDataFieldAccessor::getDataFieldValue($object,$valueField);
                                $item->setValue($value);
                                if($groupField!=='')
                                        $item->setAttribute('Group',TDataFieldAccessor::getDataFieldValue($object,$groupField));
                        }
                        else
                        {
                                $text=$object;
                                $item->setValue("$key");
                        }
                        $item->setText($this->formatDataValue($textFormat,$text));
                }
                // SelectedValue or SelectedIndex may be set before databinding
                // so we make them be effective now
                if($this->_cachedSelectedValue!==null)
                {
                        $this->setSelectedValue($this->_cachedSelectedValue);
                        $this->resetCachedSelections();
                }
                else if($this->_cachedSelectedIndex!==-1)
                {
                        $this->setSelectedIndex($this->_cachedSelectedIndex);
                        $this->resetCachedSelections();
                }
                else if($this->_cachedSelectedValues!==null)
                {
                        $this->setSelectedValues($this->_cachedSelectedValues);
                        $this->resetCachedSelections();
                }
                else if($this->_cachedSelectedIndices!==null)
                {
                        $this->setSelectedIndices($this->_cachedSelectedIndices);
                        $this->resetCachedSelections();
                }
        }

        private function resetCachedSelections()
        {
                $this->_cachedSelectedValue=null;
                $this->_cachedSelectedIndex=-1;
                $this->_cachedSelectedValues=null;
                $this->_cachedSelectedIndices=null;
        }

        /**
         * Creates a collection object to hold list items.
         * This method may be overriden to create a customized collection.
         * @return TListItemCollection the collection object
         */
        protected function createListItemCollection()
        {
                return new TListItemCollection;
        }

        /**
         * Saves items into viewstate.
         * This method is invoked right before control state is to be saved.
         */
        public function saveState()
        {
                parent::saveState();
                if($this->_items)
                        $this->setViewState('Items',$this->_items->saveState(),null);
                else
                        $this->clearViewState('Items');
        }

        /**
         * Loads items from viewstate.
         * This method is invoked right after control state is loaded.
         */
        public function loadState()
        {
                parent::loadState();
                $this->_stateLoaded=true;
                if(!$this->getIsDataBound())
                {
                        $this->_items=$this->createListItemCollection();
                        $this->_items->loadState($this->getViewState('Items',null));
                }
                $this->clearViewState('Items');
        }

        /**
         * @return boolean whether this is a multiselect control. Defaults to false.
         */
        protected function getIsMultiSelect()
        {
                return false;
        }

        /**
         * @return boolean whether performing databind should append items or clear the existing ones. Defaults to false.
         */
        public function getAppendDataBoundItems()
        {
                return $this->getViewState('AppendDataBoundItems',false);
        }

        /**
         * @param boolean whether performing databind should append items or clear the existing ones.
         */
        public function setAppendDataBoundItems($value)
        {
                $this->setViewState('AppendDataBoundItems',TPropertyValue::ensureBoolean($value),false);
        }

        /**
         * @return boolean a value indicating whether an automatic postback to the server
     * will occur whenever the user makes change to the list control and then tabs out of it.
     * Defaults to false.
         */
        public function getAutoPostBack()
        {
                return $this->getViewState('AutoPostBack',false);
        }

        /**
         * Sets the value indicating if postback automatically.
         * An automatic postback to the server will occur whenever the user
         * makes change to the list control and then tabs out of it.
         * @param boolean the value indicating if postback automatically
         */
        public function setAutoPostBack($value)
        {
                $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false);
        }

        /**
         * @return boolean whether postback event trigger by this list control will cause input validation, default is true.
         */
        public function getCausesValidation()
        {
                return $this->getViewState('CausesValidation',true);
        }

        /**
         * @param boolean whether postback event trigger by this list control will cause input validation.
         */
        public function setCausesValidation($value)
        {
                $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true);
        }

        /**
         * @return string the field of the data source that provides the text content of the list items.
         */
        public function getDataTextField()
        {
                return $this->getViewState('DataTextField','');
        }

        /**
         * @param string the field of the data source that provides the text content of the list items.
         */
        public function setDataTextField($value)
        {
                $this->setViewState('DataTextField',$value,'');
        }

        /**
         * @return string the formatting string used to control how data bound to the list control is displayed.
         */
        public function getDataTextFormatString()
        {
                return $this->getViewState('DataTextFormatString','');
        }

        /**
         * Sets data text format string.
         * The format string is used in {@link TDataValueFormatter::format()} to format the Text property value
         * of each item in the list control.
         * @param string the formatting string used to control how data bound to the list control is displayed.
         * @see TDataValueFormatter::format()
         */
        public function setDataTextFormatString($value)
        {
                $this->setViewState('DataTextFormatString',$value,'');
        }

        /**
         * @return string the field of the data source that provides the value of each list item.
         */
        public function getDataValueField()
        {
                return $this->getViewState('DataValueField','');
        }

        /**
         * @param string the field of the data source that provides the value of each list item.
         */
        public function setDataValueField($value)
        {
                $this->setViewState('DataValueField',$value,'');
        }

        /**
         * @return string the field of the data source that provides the label of the list item groups
         */
        public function getDataGroupField()
        {
                return $this->getViewState('DataGroupField','');
        }

        /**
         * @param string the field of the data source that provides the label of the list item groups
         */
        public function setDataGroupField($value)
        {
                $this->setViewState('DataGroupField',$value,'');
        }

        /**
         * @return integer the number of items in the list control
         */
        public function getItemCount()
        {
                return $this->_items?$this->_items->getCount():0;
        }

        /**
         * @return boolean whether the list control contains any items.
         */
        public function getHasItems()
        {
                return ($this->_items && $this->_items->getCount()>0);
        }

        /**
         * @return TListItemCollection the item collection
         */
        public function getItems()
        {
                if(!$this->_items)
                        $this->_items=$this->createListItemCollection();
                return $this->_items;
        }

        /**
         * @return integer the index (zero-based) of the item being selected, -1 if no item is selected.
         */
        public function getSelectedIndex()
        {
                if($this->_items)
                {
                        $n=$this->_items->getCount();
                        for($i=0;$i<$n;++$i)
                                if($this->_items->itemAt($i)->getSelected())
                                        return $i;
                }
                return -1;
        }

        /**
         * @param integer the index (zero-based) of the item to be selected
         */
        public function setSelectedIndex($index)
        {
                if(($index=TPropertyValue::ensureInteger($index))<0)
                        $index=-1;
                if($this->_items)
                {
                        $this->clearSelection();
                        if($index>=0 && $index<$this->_items->getCount())
                                $this->_items->itemAt($index)->setSelected(true);
                }
                $this->_cachedSelectedIndex=$index;
                if($this->getAdapter() instanceof IListControlAdapter)
                        $this->getAdapter()->setSelectedIndex($index);
        }

        /**
         * @return array list of index of items that are selected
         */
        public function getSelectedIndices()
        {
                $selections=array();
                if($this->_items)
                {
                        $n=$this->_items->getCount();
                        for($i=0;$i<$n;++$i)
                                if($this->_items->itemAt($i)->getSelected())
                                        $selections[]=$i;
                }
                return $selections;
        }

        /**
         * @param array list of index of items to be selected
         */
        public function setSelectedIndices($indices)
        {
                if($this->getIsMultiSelect())
                {
                        if($this->_items)
                        {
                                $this->clearSelection();
                                $n=$this->_items->getCount();
                                foreach($indices as $index)
                                {
                                        if($index>=0 && $index<$n)
                                                $this->_items->itemAt($index)->setSelected(true);
                                }
                        }
                        $this->_cachedSelectedIndices=$indices;
                }
                else
                        throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this));

                if($this->getAdapter() instanceof IListControlAdapter)
                        $this->getAdapter()->setSelectedIndices($indices);
        }

        /**
         * @return TListItem|null the selected item with the lowest cardinal index, null if no item is selected.
         */
        public function getSelectedItem()
        {
                if(($index=$this->getSelectedIndex())>=0)
                        return $this->_items->itemAt($index);
                else
                        return null;
        }

        /**
         * Returns the value of the selected item with the lowest cardinal index.
         * This method is required by {@link IDataRenderer}.
         * It is the same as {@link getSelectedValue()}.
         * @return string the value of the selected item with the lowest cardinal index, empty if no selection.
         * @see getSelectedValue
         * @since 3.1.0
         */
        public function getData()
        {
                return $this->getSelectedValue();
        }

        /**
         * Selects an item by the specified value.
         * This method is required by {@link IDataRenderer}.
         * It is the same as {@link setSelectedValue()}.
         * @param string the value of the item to be selected.
         * @see setSelectedValue
         * @since 3.1.0
         */
        public function setData($value)
        {
                $this->setSelectedValue($value);
        }

        /**
         * @return string the value of the selected item with the lowest cardinal index, empty if no selection
         */
        public function getSelectedValue()
        {
                $index=$this->getSelectedIndex();
                return $index>=0?$this->getItems()->itemAt($index)->getValue():'';
        }

        /**
         * Sets selection by item value.
         * Existing selections will be cleared if the item value is found in the item collection.
         * Note, if the value is null, existing selections will also be cleared.
         * @param string the value of the item to be selected.
         */
        public function setSelectedValue($value)
    {
            if($this->_items)
            {
                    if($value===null)
                        $this->clearSelection();
                    else if(($item=$this->_items->findItemByValue($value))!==null)
                {
                        $this->clearSelection();
                        $item->setSelected(true);
                }
                else
                                $this->clearSelection();
        }
        $this->_cachedSelectedValue=$value;
                if($this->getAdapter() instanceof IListControlAdapter)
                        $this->getAdapter()->setSelectedValue($value);
    }


        /**
         * @return array list of the selected item values (strings)
         */
        public function getSelectedValues()
        {
                $values=array();
                if($this->_items)
                {
                        foreach($this->_items as $item)
                        {
                                if($item->getSelected())
                                        $values[]=$item->getValue();
                        }
                }
                return $values;
        }

        /**
         * @param array list of the selected item values
         */
        public function setSelectedValues($values)
        {
                if($this->getIsMultiSelect())
                {
                        if($this->_items)
                        {
                                $this->clearSelection();
                                $lookup=array();
                                foreach($this->_items as $item)
                                        $lookup[$item->getValue()]=$item;
                                foreach($values as $value)
                                {
                                        if(isset($lookup["$value"]))
                                                $lookup["$value"]->setSelected(true);
                                }
                        }
                        $this->_cachedSelectedValues=$values;
                }
                else
                        throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this));

                if($this->getAdapter() instanceof IListControlAdapter)
                        $this->getAdapter()->setSelectedValues($values);
        }

    /**
     * @return string selected value
     */
    public function getText()
    {
            return $this->getSelectedValue();
    }

    /**
     * @param string value to be selected
     */
    public function setText($value)
    {
            $this->setSelectedValue($value);
    }

    /**
     * Clears all existing selections.
     */
    public function clearSelection()
    {
            if($this->_items)
            {
                    foreach($this->_items as $item)
                        $item->setSelected(false);
            }

                if($this->getAdapter() instanceof IListControlAdapter)
                        $this->getAdapter()->clearSelection();
    }

        /**
         * @return string the group of validators which the list control causes validation upon postback
         */
        public function getValidationGroup()
        {
                return $this->getViewState('ValidationGroup','');
        }

        /**
         * @param string the group of validators which the list control causes validation upon postback
         */
        public function setValidationGroup($value)
        {
                $this->setViewState('ValidationGroup',$value,'');
        }

        /**
         * @return string the prompt text which is to be displayed as the first list item.
         * @since 3.1.1
         */
        public function getPromptText()
        {
                return $this->getViewState('PromptText','');
        }

        /**
         * @param string the prompt text which is to be displayed as the first list item.
         * @since 3.1.1
         */
        public function setPromptText($value)
        {
                $this->setViewState('PromptText',$value,'');
        }

        /**
         * @return string the prompt selection value.
         * @see getPromptText
         * @since 3.1.1
         */
        public function getPromptValue()
        {
                return $this->getViewState('PromptValue','');
        }

        /**
         * @param string the prompt selection value. If empty, {@link getPromptText PromptText} will be used as the value.
         * @see setPromptText
         * @since 3.1.1
         */
        public function setPromptValue($value)
        {
                $this->setViewState('PromptValue',(string)$value,'');
        }

        /**
         * Raises OnSelectedIndexChanged event when selection is changed.
         * This method is invoked when the list control has its selection changed
         * by end-users.
         * @param TEventParameter event parameter
         */
        public function onSelectedIndexChanged($param)
        {
                $this->raiseEvent('OnSelectedIndexChanged',$this,$param);
                $this->onTextChanged($param);
        }

        /**
         * Raises OnTextChanged event when selection is changed.
         * This method is invoked when the list control has its selection changed
         * by end-users.
         * @param TEventParameter event parameter
         */
        public function onTextChanged($param)
        {
                $this->raiseEvent('OnTextChanged',$this,$param);
        }

        /**
         * Renders the prompt text, if any.
         * @param THtmlWriter writer
         * @since 3.1.1
         */
        protected function renderPrompt($writer)
        {
                $text=$this->getPromptText();
                $value=$this->getPromptValue();
                if($value==='')
                        $value=$text;
                if($value!=='')
                {
                        $writer->addAttribute('value',$value);
                        $writer->renderBeginTag('option');
                        $writer->write(THttpUtility::htmlEncode($text));
                        $writer->renderEndTag();
                        $writer->writeLine();
                }
        }

        /**
         * Renders body content of the list control.
         * This method renders items contained in the list control as the body content.
         * @param THtmlWriter writer
         */
        public function renderContents($writer)
        {
                $this->renderPrompt($writer);
                
                if($this->_items)
                {
                        $writer->writeLine();
                        $previousGroup=null;
                        foreach($this->_items as $item)
                        {
                                if($item->getEnabled())
                                {
                                        if($item->getHasAttributes())
                                        {
                                                $group=$item->getAttributes()->remove('Group');
                                                if($group!==$previousGroup)
                                                {
                                                        if($previousGroup!==null)
                                                        {
                                                                $writer->renderEndTag();
                                                                $writer->writeLine();
                                                                $previousGroup=null;
                                                        }
                                                        if($group!==null)
                                                        {
                                                                $writer->addAttribute('label',$group);
                                                                $writer->renderBeginTag('optgroup');
                                                                $writer->writeLine();
                                                                $previousGroup=$group;
                                                        }
                                                }
                                                foreach($item->getAttributes() as $name=>$value)
                                                        $writer->addAttribute($name,$value);
                                        }
                                        else if($previousGroup!==null)
                                        {
                                                $writer->renderEndTag();
                                                $writer->writeLine();
                                                $previousGroup=null;
                                        }
                                        if($item->getSelected())
                                                $writer->addAttribute('selected','selected');
                                        $writer->addAttribute('value',$item->getValue());
                                        $writer->renderBeginTag('option');
                                        $writer->write(THttpUtility::htmlEncode($item->getText()));
                                        $writer->renderEndTag();
                                        $writer->writeLine();
                                }
                        }
                        if($previousGroup!==null)
                        {
                                $writer->renderEndTag();
                                $writer->writeLine();
                        }
                }
        }

        /**
         * Formats the text value according to a format string.
         * If the format string is empty, the original value is converted into
         * a string and returned.
         * If the format string starts with '#', the string is treated as a PHP expression
         * within which the token '{0}' is translated with the data value to be formated.
         * Otherwise, the format string and the data value are passed
         * as the first and second parameters in {@link sprintf}.
         * @param string format string
         * @param mixed the data to be formatted
         * @return string the formatted result
         */
        protected function formatDataValue($formatString,$value)
        {
                if($formatString==='')
                        return TPropertyValue::ensureString($value);
                else if($formatString[0]==='#')
                {
                        $expression=strtr(substr($formatString,1),array('{0}'=>'$value'));
                        try
                        {
                                if(eval("\$result=$expression;")===false)
                                        throw new Exception('');
                                return $result;
                        }
                        catch(Exception $e)
                        {
                                throw new TInvalidDataValueException('listcontrol_expression_invalid',get_class($this),$expression,$e->getMessage());
                        }
                }
                else
                        return sprintf($formatString,$value);
        }
}

/**
 * TListItemCollection class.
 *
 * TListItemCollection maintains a list of {@link TListItem} for {@link TListControl}.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TListControl.php 2591 2008-12-19 20:31:18Z carlgmathisen $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TListItemCollection extends TList
{
        /**
         * Creates a list item object.
         * This method may be overriden to provide a customized list item object.
         * @param integer index where the newly created item is to be inserted at.
         * If -1, the item will be appended to the end.
         * @return TListItem list item object
         */
        public function createListItem($index=-1)
        {
                $item=$this->createNewListItem();
                if($index<0)
                        $this->add($item);
                else
                        $this->insertAt($index,$item);
                return $item;
        }

        /**
         * @return TListItem new item.
         */
        protected function createNewListItem($text=null)
        {
                $item =  new TListItem;
                if(!is_null($text))
                        $item->setText($text);
                return $item;
        }

        /**
         * Inserts an item into the collection.
         * @param integer the location where the item will be inserted.
         * The current item at the place and the following ones will be moved backward.
         * @param TListItem the item to be inserted.
         * @throws TInvalidDataTypeException if the item being inserted is neither a string nor TListItem
         */
        public function insertAt($index,$item)
        {
                if(is_string($item))
                        $item = $this->createNewListItem($item);
                if(!($item instanceof TListItem))
                        throw new TInvalidDataTypeException('listitemcollection_item_invalid',get_class($this));
                parent::insertAt($index,$item);
        }

        /**
         * Finds the lowest cardinal index of the item whose value is the one being looked for.
         * @param string the value to be looked for
         * @param boolean whether to look for disabled items also
         * @return integer the index of the item found, -1 if not found.
         */
        public function findIndexByValue($value,$includeDisabled=true)
        {
                $value=TPropertyValue::ensureString($value);
                $index=0;
                foreach($this as $item)
                {
                        if($item->getValue()===$value && ($includeDisabled || $item->getEnabled()))
                                return $index;
                        $index++;
                }
                return -1;
        }

        /**
         * Finds the lowest cardinal index of the item whose text is the one being looked for.
         * @param string the text to be looked for
         * @param boolean whether to look for disabled items also
         * @return integer the index of the item found, -1 if not found.
         */
        public function findIndexByText($text,$includeDisabled=true)
        {
                $text=TPropertyValue::ensureString($text);
                $index=0;
                foreach($this as $item)
                {
                        if($item->getText()===$text && ($includeDisabled || $item->getEnabled()))
                                return $index;
                        $index++;
                }
                return -1;
        }

        /**
         * Finds the item whose value is the one being looked for.
         * @param string the value to be looked for
         * @param boolean whether to look for disabled items also
         * @return TListItem the item found, null if not found.
         */
        public function findItemByValue($value,$includeDisabled=true)
        {
                if(($index=$this->findIndexByValue($value,$includeDisabled))>=0)
                        return $this->itemAt($index);
                else
                        return null;
        }

        /**
         * Finds the item whose text is the one being looked for.
         * @param string the text to be looked for
         * @param boolean whether to look for disabled items also
         * @return TListItem the item found, null if not found.
         */
        public function findItemByText($text,$includeDisabled=true)
        {
                if(($index=$this->findIndexByText($text,$includeDisabled))>=0)
                        return $this->itemAt($index);
                else
                        return null;
        }

        /**
         * Loads state into every item in the collection.
         * This method should only be used by framework and control developers.
         * @param array|null state to be loaded.
         */
        public function loadState($state)
        {
                $this->clear();
                if($state!==null)
                        $this->copyFrom($state);
        }

        /**
         * Saves state of items.
         * This method should only be used by framework and control developers.
         * @return array|null the saved state
         */
        public function saveState()
        {
                return ($this->getCount()>0) ? $this->toArray() : null;
        }
}

/**
 * IListControlAdapter interface
 *
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 * @version $Revision: $  Sun Jun 25 04:53:43 EST 2006 $
 * @package System.Web.UI.ActiveControls
 * @since 3.0
 */
interface IListControlAdapter
{
        /**
         * Selects an item based on zero-base index on the client side.
         * @param integer the index (zero-based) of the item to be selected
         */
        public function setSelectedIndex($index);
        /**
         * Selects a list of item based on zero-base indices on the client side.
         * @param array list of index of items to be selected
         */
        public function setSelectedIndices($indices);

        /**
         * Sets selection by item value on the client side.
         * @param string the value of the item to be selected.
         */
        public function setSelectedValue($value);

        /**
         * Sets selection by a list of item values on the client side.
         * @param array list of the selected item values
         */
        public function setSelectedValues($values);

    /**
     * Clears all existing selections on the client side.
     */
    public function clearSelection();
}


?>