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