001    package org.apache.tapestry.pageload;
002    
003    import org.apache.hivemind.ApplicationRuntimeException;
004    import org.apache.hivemind.PoolManageable;
005    import org.apache.tapestry.IComponent;
006    import org.apache.tapestry.IForm;
007    import org.apache.tapestry.IPage;
008    import org.apache.tapestry.IRender;
009    import org.apache.tapestry.form.IFormComponent;
010    import org.apache.tapestry.internal.Component;
011    import org.apache.tapestry.internal.event.ComponentEventProperty;
012    import org.apache.tapestry.internal.event.EventBoundListener;
013    import org.apache.tapestry.internal.event.IComponentEventInvoker;
014    import org.apache.tapestry.spec.IComponentSpecification;
015    
016    import java.util.*;
017    
018    /**
019     * Handles connecting up components and forms targeted with the EventListener annotation.
020     */
021    public class EventConnectionVisitor implements IComponentVisitor, PoolManageable {
022    
023        IComponentEventInvoker _invoker;
024    
025        IPage _currentPage = null;
026        List _forms = new ArrayList();
027    
028        public void visitComponent(IComponent component)
029        {
030            checkComponentPage(component);
031    
032            Map events = component.getSpecification().getComponentEvents();
033            Set keySet = events.keySet();
034            String[] compIds = (String[]) keySet.toArray(new String[keySet.size()]);
035            
036            for (int i=0; i < compIds.length; i++)
037            {
038                String compId = compIds[i];
039                ComponentEventProperty property = (ComponentEventProperty) events.get(compId);
040    
041                // find the targeted component
042    
043                IComponent comp = findComponent(compId, component.getPage());
044    
045                if (comp == null)
046                    continue;
047    
048                if (Component.class.isInstance(comp))
049                    ((Component)comp).setHasEvents(true);
050                
051                // wire up with idPath
052    
053                String idPath = comp.getExtendedId();
054                
055                component.getSpecification().rewireComponentId(compId, idPath);
056                
057                _invoker.addEventListener(idPath, component.getSpecification());
058                wireFormEvents(comp, component.getSpecification());
059            }
060            
061            // find form element targets for re-mapping with proper idpath && IEventInvoker connection
062    
063            events = component.getSpecification().getElementEvents();
064            Iterator it = events.keySet().iterator();
065    
066            // for efficiency later in ComponentEventConnectionWorker
067    
068            if (events.size() > 0 && Component.class.isInstance(component)) {
069                ((Component)component).setHasEvents(true);
070            }
071    
072            while (it.hasNext())
073            {
074                String elementId = (String) it.next();
075                ComponentEventProperty property = (ComponentEventProperty) events.get(elementId);
076    
077                Iterator bindingIt  = property.getFormEvents().iterator();
078                while (bindingIt.hasNext())
079                {
080                    String key = (String) bindingIt.next();
081                    List listeners = property.getFormEventListeners(key);
082    
083                    for (int i=0; i < listeners.size(); i++) {
084                        
085                        EventBoundListener listener = (EventBoundListener) listeners.get(i);
086                        wireElementFormEvents(listener, component, component.getSpecification());
087                    }
088                }
089            }
090        }
091    
092        void wireElementFormEvents(EventBoundListener listener, IComponent component, IComponentSpecification spec)
093        {
094            if (listener.getFormId() == null)
095                return;
096    
097            if (_forms.size() < 1)
098                discoverPageForms(component.getPage());
099    
100            IForm form = null;
101            for (int i=0; i < _forms.size(); i++)
102            {
103                IForm f = (IForm) _forms.get(i);
104                if (listener.getFormId().equals(f.getExtendedId()) || listener.getFormId().equals(f.getId()))
105                {
106                    form = f;
107                    break;
108                }
109            }
110    
111            // couldn't find the form they specified
112    
113            if (form == null)
114                throw new ApplicationRuntimeException(PageloadMessages.componentNotFound(listener.getFormId()),
115                                                      component, component.getLocation(), null);
116    
117            String idPath = form.getExtendedId();
118            
119            listener.setFormId(idPath);
120            _invoker.addFormEventListener(idPath, spec);
121        }
122    
123        void wireFormEvents(IComponent component, IComponentSpecification listener)
124        {
125            if (!IFormComponent.class.isInstance(component))
126                return;
127    
128            IFormComponent fcomp = (IFormComponent) component;
129    
130            if (_forms.size() < 1)
131                discoverPageForms(fcomp.getPage());
132    
133            IForm form = findComponentForm(fcomp);
134            if (form == null)
135                return;
136    
137            listener.connectAutoSubmitEvents(component, form);
138            _invoker.addFormEventListener(form.getExtendedId(), listener);
139        }
140    
141        IComponent findComponent(String id, IComponent target)
142        {
143            Map components = target.getComponents();
144            if (components == null)
145                return null;
146            
147            IComponent comp = (IComponent) components.get(id);
148            if (comp != null)
149                return comp;
150    
151            Iterator children = components.values().iterator();
152    
153            while (children.hasNext())
154            {
155                IComponent child = (IComponent) children.next();
156    
157                comp = findComponent(id, child);
158                if (comp != null)
159                    return comp;
160            }
161    
162            return null;
163        }
164    
165        void discoverPageForms(IComponent parent)
166        {
167            if (IForm.class.isInstance(parent))
168                _forms.add(parent);
169    
170            Iterator it = parent.getComponents().values().iterator();
171            while (it.hasNext())
172            {
173                IComponent comp = (IComponent)it.next();
174    
175                discoverPageForms(comp);
176            }
177        }
178    
179        IForm findComponentForm(IFormComponent child)
180        {
181            for (int i = 0; i < _forms.size(); i++) {
182    
183                IForm form = (IForm) _forms.get(i);
184    
185                IComponent match = findContainedComponent(child.getExtendedId(), (Component)form);
186                if (match != null)
187                    return form;
188            }
189    
190            return null;
191        }
192    
193        IComponent findContainedComponent(String idPath, Component container)
194        {
195            IComponent comp = (IComponent) container;
196    
197            if (idPath.equals(comp.getExtendedId()))
198                return comp;
199    
200            IRender[] children = container.getContainedRenderers();
201            if (children == null)
202                return null;
203    
204            for (int i=0; i < children.length; i++) {
205    
206                if (children[i] == null)
207                    return null;
208    
209                if (!Component.class.isInstance(children[i]))
210                    continue;
211    
212                IComponent found = findContainedComponent(idPath, (Component)children[i]);
213                if (found != null)
214                    return found;
215            }
216            
217            return null;
218        }
219    
220        void checkComponentPage(IComponent component)
221        {
222            if (_currentPage == null) {
223    
224                _currentPage = component.getPage();
225                _forms.clear();
226            } else if (component.getPage() != _currentPage) {
227    
228                _currentPage = component.getPage();
229                _forms.clear();
230            }
231        }
232    
233        public void activateService()
234        {
235            _currentPage = null;
236            _forms.clear();
237        }
238    
239        public void passivateService()
240        {
241            _currentPage = null;
242            _forms.clear();
243        }
244    
245        // injected
246        public void setEventInvoker(IComponentEventInvoker invoker)
247        {
248            _invoker = invoker;
249        }
250    }