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