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 }