001    // Copyright May 20, 2006 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    package org.apache.tapestry.internal.event;
015    
016    import org.apache.tapestry.event.BrowserEvent;
017    
018    import java.util.*;
019    
020    
021    /**
022     * Represents a configured listener/event(s) binding for a 
023     * a component and the events that may be optionally listened
024     * for on the client browser.
025     */
026    public class ComponentEventProperty implements Cloneable
027    {
028        private Map _eventMap = new HashMap();
029        private Map _formEventMap = new HashMap();
030        
031        private String _componentId;
032    
033        /**
034         * Creates a new component event property mapped to the specified component id.
035         *
036         * @param componentId
037         *          The component which is the target of all mappings in this property.
038         */
039        public ComponentEventProperty(String componentId)
040        {
041            _componentId = componentId;
042        }
043    
044        /**
045         * Used in cloning only currently.
046         *
047         * @param componentId
048         *          The component this property is bound to.
049         * @param events
050         *          The list of event mappings.
051         * @param formEvents
052         *          The list of form event mappings.
053         */
054        public ComponentEventProperty(String componentId, Map events, Map formEvents)
055        {
056            _componentId = componentId;
057            _eventMap = events;
058            _formEventMap = formEvents;
059        }
060    
061        /**
062         * Adds a listener bound to the specified client side
063         * events.
064         * @param events
065         * @param methodName
066         * @param async
067         */
068        public void addListener(String[] events, String methodName,
069                String formId, boolean validateForm, boolean async, boolean focus)
070        {
071            addListener(events, methodName, formId, validateForm, async, focus, false);
072        }
073    
074        /**
075         * Adds a listener bound to the specified client side
076         * events.
077         * 
078         * @param events The javascript events to bind to.
079         * @param methodName The method to invoke when triggered.
080         * @param formId Optional form to bind event to.
081         * @param validateForm Whether or not form client side validation should be performed.
082         * @param async  Whether or not the request should be asynchronous.
083         * @param focus Whether or not the form should recieve focus events. (if any forms are involved)
084         * @param autoSubmit Whether or not {@link org.apache.tapestry.form.IFormComponent}s should have their forms autowired for submission.
085         */
086        public void addListener(String[] events, String methodName, 
087                String formId, boolean validateForm, boolean async, boolean focus, boolean autoSubmit)
088        {
089            for (int i=0; i < events.length; i++) {
090                if (formId != null && formId.length() > 0)
091                    addFormEventListener(events[i], methodName, formId, validateForm, async, focus);
092                else
093                    addEventListener(events[i], methodName, autoSubmit);
094            }
095        }
096        
097        /**
098         * Adds a form listener to the specified client side event.
099         * @param event
100         * @param methodName
101         * @param formId 
102         * @param validateForm
103         */
104        public void addFormEventListener(String event, String methodName,
105                String formId, boolean validateForm, boolean async, boolean focus)
106        {
107            EventBoundListener listener = new EventBoundListener(methodName, formId, validateForm, _componentId, async, focus);
108            
109            List listeners = getFormEventListeners(event);
110            if (!listeners.contains(listener))
111                listeners.add(listener);
112        }
113        
114        /**
115         * Adds a listener to the specified client side event.
116         * @param event
117         * @param methodName
118         */
119        public void addEventListener(String event, String methodName, boolean autoSubmit)
120        {
121            EventBoundListener listener = new EventBoundListener(methodName, _componentId);
122            
123            List listeners = getEventListeners(event);
124            if (!listeners.contains(listener))
125                listeners.add(listener);
126        }
127    
128        public void connectAutoSubmitEvents(String formIdPath)
129        {
130            Iterator it = getEvents().iterator();
131            List removeKeys = new ArrayList();
132            
133            while (it.hasNext()) {
134                String key = (String)it.next();
135    
136                List listeners = (List) _eventMap.get(key);
137                Iterator lit = listeners.iterator();
138                while (lit.hasNext()) {
139                    
140                    EventBoundListener listener = (EventBoundListener) lit.next();
141    
142                    listener.setFormId(formIdPath);
143                    lit.remove();
144                    
145                    List formListeners = getFormEventListeners(key);
146                    if (!formListeners.contains(listener))
147                        formListeners.add(listener);
148                }
149                
150                // remove mapping if empty
151                
152                if (listeners.size() == 0) {
153                    removeKeys.add(key);
154                }
155            }
156    
157            for (int i=0; i < removeKeys.size(); i++) {
158                
159                _eventMap.remove(removeKeys.get(i));
160            }
161    
162            it = getFormEvents().iterator();
163            
164            while (it.hasNext())
165            {
166                String key = (String) it.next();
167                List listeners = (List) _formEventMap.get(key);
168                Iterator lit = listeners.iterator();
169    
170                while(lit.hasNext())
171                {
172                    EventBoundListener listener = (EventBoundListener) lit.next();
173                    listener.setFormId(formIdPath);
174                }
175            }
176        }
177    
178        /**
179         * Replaces all instances of the existing component id mapped for this property with the new
180         * {@link org.apache.tapestry.IComponent#getIdPath()} version.
181         *
182         * @param idPath The component id path.
183         */
184        public void rewireComponentId(String idPath)
185        {
186            _componentId = idPath;
187    
188            Iterator it = getEvents().iterator();
189            while (it.hasNext())
190            {
191                String key = (String) it.next();
192    
193                List listeners = (List)_eventMap.get(key);
194    
195                for (int i=0; i < listeners.size(); i++) {
196    
197                    EventBoundListener listener = (EventBoundListener) listeners.get(i);
198                    listener.setComponentId(idPath);
199                }
200            }
201    
202            it = getFormEvents().iterator();
203            while (it.hasNext())
204            {
205                String key = (String) it.next();
206    
207                List listeners = (List)_formEventMap.get(key);
208    
209                for (int i=0; i < listeners.size(); i++) {
210    
211                    EventBoundListener listener = (EventBoundListener) listeners.get(i);
212                    listener.setComponentId(idPath);
213                }
214            }
215        }
216    
217        /**
218         * @return the componentId
219         */
220        public String getComponentId()
221        {
222            return _componentId;
223        }
224    
225        /**
226         * Gets the current list of listeners for a specific event,
227         * creates a new instance if one doesn't exist already.
228         * 
229         * @param event
230         * 
231         * @return The current set of listeners bound to the specified event.
232         */
233        public List getEventListeners(String event)
234        {
235            List listeners = (List)_eventMap.get(event);
236            if (listeners == null) {
237                listeners = new ArrayList();
238                _eventMap.put(event, listeners);
239            }
240            
241            return listeners;
242        }
243        
244        /**
245         * Gets the current list of listeners for a specific event,
246         * creates a new instance if one doesn't exist already.
247         * 
248         * @param event
249         * 
250         * @return The current set of listeners that will submit a form bound to the
251         *          specified event.
252         */
253        public List getFormEventListeners(String event)
254        {
255            List listeners = (List)_formEventMap.get(event);
256            if (listeners == null) {
257                listeners = new ArrayList();
258                _formEventMap.put(event, listeners);
259            }
260            
261            return listeners;
262        }
263        
264        /**
265         * The set of all non form based events.
266         * @return The unique set of events.
267         */
268        public Set getEvents()
269        {
270            return _eventMap.keySet();
271        }
272        
273        /**
274         * The set of all form based listener events.
275         * 
276         * @return All mapped form event keys.
277         */
278        public Set getFormEvents()
279        {
280            return _formEventMap.keySet();
281        }
282        
283        /**
284         * Creates a list of listeners bound to a particular form
285         * and client side browser event. 
286         * 
287         * @param formId
288         *          The form to find listeners for.
289         * @param event
290         *          The browser event that generated the request.
291         * @param append 
292         *          The optional list to add the listeners to.
293         * @return The list of listeners to invoke for the form and event passed in,
294         *          will be empty if none found.
295         */
296        public List getFormEventListeners(String formId, BrowserEvent event, List append)
297        {   
298            List ret = (append == null) ? new ArrayList() : append;
299            
300            List listeners = (List)_formEventMap.get(event.getName());
301            if (listeners == null) 
302                return ret;
303            
304            for (int i=0; i < listeners.size(); i++) {
305                EventBoundListener listener = (EventBoundListener)listeners.get(i);
306                if (listener.getFormId().equals(formId))
307                    ret.add(listener);
308            }
309            
310            return ret;
311        }
312    
313        void cloneEvents(Map source, Map target)
314                throws CloneNotSupportedException
315        {
316            Iterator it = source.keySet().iterator();
317            while (it.hasNext())
318            {
319                String event = (String) it.next();
320                List listeners = (List)source.get(event);
321    
322                List newListeners = new ArrayList();
323                for (int i=0; i < listeners.size(); i++) {
324                    EventBoundListener listener = (EventBoundListener) listeners.get(i);
325                    newListeners.add(listener.clone());
326                }
327    
328                target.put(event, newListeners);
329            }
330        }
331    
332        public Object clone()
333        throws CloneNotSupportedException
334        {
335            Map events = new HashMap();
336            Map formEvents = new HashMap();
337    
338            cloneEvents(_eventMap, events);
339            cloneEvents(_formEventMap, formEvents);
340    
341            return new ComponentEventProperty(_componentId, events, formEvents);
342        }
343    }