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