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    package org.apache.tapestry.internal.event.impl;
015    
016    import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
017    import org.apache.hivemind.util.Defense;
018    import org.apache.tapestry.IActionListener;
019    import org.apache.tapestry.IComponent;
020    import org.apache.tapestry.IForm;
021    import org.apache.tapestry.IRequestCycle;
022    import org.apache.tapestry.event.BrowserEvent;
023    import org.apache.tapestry.event.ResetEventListener;
024    import org.apache.tapestry.form.FormSupport;
025    import org.apache.tapestry.internal.event.ComponentEventProperty;
026    import org.apache.tapestry.internal.event.EventBoundListener;
027    import org.apache.tapestry.internal.event.IComponentEventInvoker;
028    import org.apache.tapestry.listener.ListenerInvoker;
029    import org.apache.tapestry.spec.IComponentSpecification;
030    import org.apache.tapestry.spec.IEventListener;
031    
032    import java.util.*;
033    
034    
035    /**
036     * Implementation of {@link IComponentEventInvoker}.
037     */
038    public class ComponentEventInvoker implements IComponentEventInvoker, ResetEventListener
039    {
040        static final ComponentEventProperty[] EMPTY_PROPERTIES = new ComponentEventProperty[0];
041    
042        // Mapped component id path -> List of IEventListeners
043        private Map _components = new ConcurrentHashMap();
044        // Mapped form id path -> List of IEventListeners
045        private Map _formComponents = new ConcurrentHashMap();
046        // Used to invoke actual listener methods
047        private ListenerInvoker _invoker;
048    
049        // Cached set of ComponentEventProperty[] arrays mapped to specific components
050        private Map _propertyCache = new ConcurrentHashMap();
051    
052        /**
053         * {@inheritDoc}
054         */
055        public void invokeListeners(IComponent component, IRequestCycle cycle, BrowserEvent event)
056        {
057            Defense.notNull(component, "component");
058            Defense.notNull(cycle, "cycle");
059            Defense.notNull(event, "event");
060    
061            invokeComponentListeners(component, cycle, event);
062    
063            invokeElementListeners(component, cycle, event);
064        }
065    
066        /**
067         * {@inheritDoc}
068         */
069        public void invokeFormListeners(FormSupport formSupport, final IRequestCycle cycle, final BrowserEvent event)
070        {
071            Defense.notNull(formSupport, "formSupport");
072            Defense.notNull(cycle, "cycle");
073            Defense.notNull(event, "event");
074    
075            IForm form = formSupport.getForm();
076            String formIdPath = form.getExtendedId();
077    
078            String targetId = (String)event.getTarget().get("id");
079            String componentId = event.getComponentId();
080    
081            if (targetId == null || componentId == null)
082                return;
083    
084            List comps = getFormEventListeners(formIdPath);
085            if (comps == null)
086                return;
087    
088            boolean disableFocus = false;
089    
090            for (int i=0; i < comps.size(); i++)
091            {
092                IComponentSpecification spec = (IComponentSpecification)comps.get(i);
093                EventBoundListener[] listeners = spec.getFormEvents(formIdPath, event);
094    
095                IComponent target = null;
096                if (spec.isPageSpecification())
097                {
098                    target = form.getPage();
099                } else {
100                    target = findComponent(form.getPage().getComponents().values(), spec);
101                }
102    
103                for (int e=0; e < listeners.length; e++)
104                {
105                    // ensure ~only~ the method that targeted this event gets called!
106    
107                    if (!listeners[e].getComponentId().endsWith(componentId))
108                        continue;
109    
110                    // clear validation errors but not input if async validation is
111                    // disabled
112    
113                    if (!listeners[e].isValidateForm())
114                    {
115                        form.getDelegate().clearErrors();
116                    }
117    
118                    // handle disabling focus
119                    if (!disableFocus && !listeners[e].shouldFocusForm())
120                        disableFocus = true;
121    
122                    // defer execution until after form is done rewinding
123    
124                    form.addDeferredRunnable(
125                      new FormRunnable(target.getListeners().getListener(listeners[e].getMethodName()),
126                                       target,
127                                       cycle));
128                }
129            }
130    
131            // Form uses cycle attributes to test whether or not to focus .
132            // The attribute existing at all is enough to bypass focusing.
133            
134            if (disableFocus)
135            {
136                cycle.disableFocus();
137            }
138        }
139    
140        void invokeComponentListeners(IComponent component, IRequestCycle cycle, BrowserEvent event)
141        {
142            String idPath = component.getExtendedId();
143            List listeners = getEventListeners(idPath);
144            if (listeners == null)
145                return;
146    
147            for (int i = 0; i < listeners.size(); i++)
148            {
149                IComponentSpecification listener = (IComponentSpecification)listeners.get(i);
150    
151                IComponent target;
152                ComponentEventProperty props;
153    
154                if (listener.isPageSpecification())
155                {
156                    target = component.getPage();
157                    props = listener.getComponentEvents(idPath);
158                } else
159                {
160                    target = findComponent(component.getPage().getComponents().values(), listener);
161                    props = target.getSpecification().getComponentEvents(idPath);
162                }
163                
164                if (props == null)
165                    continue;
166    
167                List clisteners = props.getEventListeners(event.getName());
168                for (int e=0; e < clisteners.size(); e++)
169                {
170                    EventBoundListener eventListener = (EventBoundListener)clisteners.get(e);
171    
172                    _invoker.invokeListener(target.getListeners().getListener(eventListener.getMethodName()), target, cycle);
173                }
174    
175            }
176        }
177    
178        void invokeElementListeners(IComponent component, IRequestCycle cycle, BrowserEvent event)
179        {
180            String targetId = (String)event.getTarget().get("id");
181            if (targetId == null)
182                return;
183    
184            ComponentEventProperty prop = component.getSpecification().getElementEvents(targetId);
185            if (prop == null)
186                return;
187    
188            List listeners = prop.getEventListeners(event.getName());
189    
190            for (int i=0; i < listeners.size(); i++)
191            {
192                EventBoundListener listener = (EventBoundListener)listeners.get(i);
193    
194                _invoker.invokeListener(component.getListeners().getListener(listener.getMethodName()), component, cycle);
195            }
196        }
197    
198        IComponent findComponent(Collection comps, IComponentSpecification spec)
199        {
200            IComponent ret = null;
201    
202            Iterator it = comps.iterator();
203    
204            while (it.hasNext())
205            {
206                IComponent comp = (IComponent)it.next();
207    
208                if (comp.getSpecification().equals(spec))
209                {
210                    ret = comp;
211                    break;
212                }
213    
214                ret = findComponent(comp.getComponents().values(), spec);
215                if (ret != null)
216                    break;
217            }
218    
219            return ret;
220        }
221    
222        /** Local runnable for deferred form connections. */
223        class FormRunnable implements Runnable {
224    
225            private IActionListener _listener;
226            private IComponent _component;
227            private IRequestCycle _cycle;
228    
229            public FormRunnable(IActionListener listener, IComponent comp, IRequestCycle cycle)
230            {
231                _listener = listener;
232                _component = comp;
233                _cycle = cycle;
234            }
235    
236            public void run()
237            {
238                _invoker.invokeListener(_listener, _component, _cycle);
239            }
240        }
241    
242        /**
243         * {@inheritDoc}
244         */
245        public void addEventListener(String componentId, IComponentSpecification listener)
246        {
247            List listeners = (List)_components.get(componentId);
248    
249            if (listeners == null)
250            {
251                listeners = new ArrayList();
252                _components.put(componentId, listeners);
253            }
254    
255            if (!listeners.contains(listener))
256            {
257                listeners.add(listener);
258            }
259    
260            _propertyCache.remove(componentId);
261        }
262    
263        /**
264         * {@inheritDoc}
265         */
266        public List getEventListeners(String componentId)
267        {
268            if (componentId == null)
269                return null;
270    
271            return (List)_components.get(componentId);
272        }
273    
274        public ComponentEventProperty[] getEventPropertyListeners(String componentIdPath)
275        {
276            if (componentIdPath == null)
277                return EMPTY_PROPERTIES;
278    
279            ComponentEventProperty[] ret = (ComponentEventProperty[])_propertyCache.get(componentIdPath);
280            if (ret != null)
281                return ret;
282    
283            List listeners = getEventListeners(componentIdPath);
284            if (listeners == null || listeners.size() < 1)
285                return EMPTY_PROPERTIES;
286    
287            List props = new ArrayList();
288            for (int i=0; i < listeners.size(); i++)
289            {
290                IEventListener listener = (IEventListener)listeners.get(i);
291    
292                props.add(listener.getComponentEvents(componentIdPath));
293            }
294    
295            ret = (ComponentEventProperty[])props.toArray(new ComponentEventProperty[props.size()]);
296    
297            _propertyCache.put(componentIdPath, ret);
298    
299            return ret;
300        }
301    
302        /**
303         * {@inheritDoc}
304         */
305        public void addFormEventListener(String formId, IComponentSpecification listener)
306        {
307            List listeners = (List)_formComponents.get(formId);
308    
309            if (listeners == null)
310            {
311                listeners = new ArrayList();
312                _formComponents.put(formId, listeners);
313            }
314    
315            if (!listeners.contains(listener))
316            {
317                listeners.add(listener);
318            }
319        }
320    
321        /**
322         * {@inheritDoc}
323         */
324        public List getFormEventListeners(String formId)
325        {
326            if (formId == null)
327                return null;
328    
329            return (List)_formComponents.get(formId);
330        }
331    
332        /**
333         * {@inheritDoc}
334         */
335        public void resetEventDidOccur()
336        {
337            _components.clear();
338            _formComponents.clear();
339            _propertyCache.clear();
340        }
341    
342        /** Injected. */
343        public void setInvoker(ListenerInvoker invoker)
344        {
345            _invoker = invoker;
346        }
347    }