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