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.form;
016    
017    import java.util.Iterator;
018    
019    import org.apache.hivemind.ApplicationRuntimeException;
020    import org.apache.tapestry.IActionListener;
021    import org.apache.tapestry.IForm;
022    import org.apache.tapestry.IMarkupWriter;
023    import org.apache.tapestry.IRequestCycle;
024    import org.apache.tapestry.Tapestry;
025    import org.apache.tapestry.coerce.ValueConverter;
026    import org.apache.tapestry.listener.ListenerInvoker;
027    import org.apache.tapestry.services.DataSqueezer;
028    
029    /**
030     * A specialized component used to edit a list of items within a form; it is similar to a
031     * {@link org.apache.tapestry.components.Foreach}but leverages hidden inputs within the
032     * &lt;form&gt; to store the items in the list. [ <a
033     * href="../../../../../ComponentReference/ListEdit.html">Component Reference </a>]
034     * 
035     * @author Howard Lewis Ship
036     * @since 1.0.2
037     */
038    
039    public abstract class ListEdit extends AbstractFormComponent
040    {
041        /**
042         * @see org.apache.tapestry.form.AbstractFormComponent#renderFormComponent(org.apache.tapestry.IMarkupWriter,
043         *      org.apache.tapestry.IRequestCycle)
044         */
045        protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle)
046        {
047            this.render(writer, cycle, getSource());
048        }
049    
050        /**
051         * @see org.apache.tapestry.form.AbstractFormComponent#rewindFormComponent(org.apache.tapestry.IMarkupWriter,
052         *      org.apache.tapestry.IRequestCycle)
053         */
054        protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle)
055        {
056            String[] values = cycle.getParameters(getName());
057    
058            this.render(writer, cycle, (Iterator) getValueConverter().coerceValue(
059                    values,
060                    Iterator.class));
061        }
062    
063        protected void render(IMarkupWriter writer, IRequestCycle cycle, Iterator i)
064        {
065            // If the source (when rendering), or the submitted values (on submit)
066            // are null, then skip the remainder (nothing to update, nothing to
067            // render).
068    
069            if (i == null)
070                return;
071    
072            int index = 0;
073    
074            String element = getElement();
075    
076            boolean indexBound = isParameterBound("index");
077    
078            while (i.hasNext())
079            {
080                Object value = null;
081    
082                if (indexBound)
083                    setIndex(index++);
084    
085                if (cycle.isRewinding())
086                    value = convertValue((String) i.next());
087                else
088                {
089                    value = i.next();
090                    writeValue(getForm(), getName(), value);
091                }
092    
093                setValue(value);
094    
095                getListenerInvoker().invokeListener(getListener(), this, cycle);
096    
097                if (element != null)
098                {
099                    writer.begin(element);
100                    renderInformalParameters(writer, cycle);
101                }
102    
103                renderBody(writer, cycle);
104    
105                if (element != null)
106                    writer.end();
107            }
108        }
109    
110        private void writeValue(IForm form, String name, Object value)
111        {
112            String externalValue;
113    
114            try
115            {
116                externalValue = getDataSqueezer().squeeze(value);
117            }
118            catch (Exception ex)
119            {
120                throw new ApplicationRuntimeException(Tapestry.format(
121                        "ListEdit.unable-to-convert-value",
122                        value), this, null, ex);
123            }
124    
125            form.addHiddenValue(name, externalValue);
126        }
127    
128        private Object convertValue(String value)
129        {
130            try
131            {
132                return getDataSqueezer().unsqueeze(value);
133            }
134            catch (Exception ex)
135            {
136                throw new ApplicationRuntimeException(Tapestry.format(
137                        "ListEdit.unable-to-convert-string",
138                        value), this, null, ex);
139            }
140        }
141    
142        public abstract String getElement();
143    
144        /** @since 2.2 * */
145    
146        public abstract IActionListener getListener();
147    
148        /** @since 3.0 * */
149    
150        public boolean isDisabled()
151        {
152            return false;
153        }
154    
155        /** @since 4.0 */
156    
157        public abstract Iterator getSource();
158    
159        /** @since 4.0 */
160    
161        public abstract void setValue(Object value);
162    
163        /** @since 4.0 */
164    
165        public abstract void setIndex(int index);
166    
167        /** @since 4.0 */
168    
169        public abstract DataSqueezer getDataSqueezer();
170    
171        /** @since 4.0 */
172    
173        public abstract ValueConverter getValueConverter();
174    
175        /**
176         * Injected.
177         * 
178         * @since 4.0
179         */
180    
181        public abstract ListenerInvoker getListenerInvoker();
182    
183        /**
184         * Returns false; ListEdit components can't take focus.
185         * 
186         * @since 4.0
187         */
188        protected boolean getCanTakeFocus()
189        {
190            return false;
191        }
192    
193        public String getClientId()
194        {
195            // TODO Auto-generated method stub
196            return null;
197        }
198    
199        public String getDisplayName()
200        {
201            // TODO Auto-generated method stub
202            return null;
203        }
204    
205    }