001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.components;
016    
017    import java.util.Iterator;
018    
019    import org.apache.tapestry.AbstractComponent;
020    import org.apache.tapestry.IBinding;
021    import org.apache.tapestry.IMarkupWriter;
022    import org.apache.tapestry.IRequestCycle;
023    import org.apache.tapestry.Tapestry;
024    import org.apache.tapestry.coerce.ValueConverter;
025    
026    /**
027     * Repeatedly renders its wrapped contents while iterating through a list of values. [ <a
028     * href="../../../../../ComponentReference/Foreach.html">Component Reference </a>]
029     * <p>
030     * While the component is rendering, the property {@link #getValue() value}(accessed as
031     * <code>components.<i>foreach</i>.value</code> is set to each successive value from the source,
032     * and the property {@link #getIndex() index}is set to each successive index into the source
033     * (starting with zero).
034     * 
035     * @author Howard Lewis Ship
036     */
037    
038    public abstract class Foreach extends AbstractComponent
039    {
040        private Object _value;
041    
042        private int _index;
043    
044        /**
045         * Gets the source binding and returns an {@link Iterator}representing the values identified by
046         * the source. Returns an empty {@link Iterator}if the binding, or the binding value, is null.
047         * <p>
048         * Invokes {@link Tapestry#coerceToIterator(Object)}to perform the actual conversion.
049         */
050    
051        protected Iterator getSourceData()
052        {
053            Object source = null;
054            
055            IBinding sourceBinding = getBinding("source");
056            if (sourceBinding != null)
057                    source = sourceBinding.getObject();
058    
059            if (source == null)
060                return null;
061    
062            return (Iterator) getValueConverter().coerceValue(source, Iterator.class);
063        }
064    
065        protected void prepareForRender(IRequestCycle cycle)
066        {
067            _value = null;
068            _index = 0;
069        }
070    
071        protected void cleanupAfterRender(IRequestCycle cycle)
072        {
073            _value = null;
074        }
075    
076        /**
077         * Gets the source binding and iterates through its values. For each, it updates the value
078         * binding and render's its wrapped elements.
079         */
080    
081        protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
082        {
083            Iterator dataSource = getSourceData();
084    
085            // The dataSource was either not convertable, or was empty.
086    
087            if (dataSource == null)
088                return;
089    
090            boolean indexBound = isParameterBound("index");
091            boolean valueBound = isParameterBound("value");
092    
093            String element = getElement();
094    
095            boolean hasNext = dataSource.hasNext();
096    
097            while (hasNext)
098            {
099                _value = dataSource.next();
100                hasNext = dataSource.hasNext();
101    
102                if (indexBound)
103                    setIndexParameter(_index);
104    
105                if (valueBound)
106                    setValueParameter(_value);
107    
108                if (element != null)
109                {
110                    writer.begin(element);
111                    renderInformalParameters(writer, cycle);
112                }
113    
114                renderBody(writer, cycle);
115    
116                if (element != null)
117                    writer.end();
118    
119                _index++;
120            }
121    
122        }
123    
124        /**
125         * Returns the most recent value extracted from the source parameter.
126         * 
127         * @throws org.apache.tapestry.ApplicationRuntimeException
128         *             if the Foreach is not currently rendering.
129         */
130    
131        public Object getValue()
132        {
133            if (!isRendering())
134                throw Tapestry.createRenderOnlyPropertyException(this, "value");
135    
136            return _value;
137        }
138    
139        /**
140         * The index number, within the {@link #getSource() source}, of the the current value.
141         * 
142         * @throws org.apache.tapestry.ApplicationRuntimeException
143         *             if the Foreach is not currently rendering.
144         * @since 2.2
145         */
146    
147        public int getIndex()
148        {
149            if (!isRendering())
150                throw Tapestry.createRenderOnlyPropertyException(this, "index");
151    
152            return _index;
153        }
154        
155        public abstract String getElement();
156    
157        /** @since 4.0 */
158        public abstract void setIndexParameter(int value);
159    
160        /** @since 4.0 */
161        public abstract void setValueParameter(Object value);
162    
163        /** @since 4.0 */
164        public abstract ValueConverter getValueConverter();
165    }