1 /* EventProcessor.java
2
3 {{IS_NOTE
4 Purpose:
5
6 Description:
7
8 History:
9 Tue May 8 14:10:54 2007, Created by tomyeh
10 }}IS_NOTE
11
12 Copyright (C) 2007 Potix Corporation. All Rights Reserved.
13
14 {{IS_RIGHT
15 This program is distributed under GPL Version 2.0 in the hope that
16 it will be useful, but WITHOUT ANY WARRANTY.
17 }}IS_RIGHT
18 */
19 package org.zkoss.zk.ui.impl;
20
21 import java.util.Set;
22 import java.util.HashSet;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.lang.reflect.Method;
26
27 import org.zkoss.util.logging.Log;
28
29 import org.zkoss.zk.ui.Execution;
30 import org.zkoss.zk.ui.Desktop;
31 import org.zkoss.zk.ui.Page;
32 import org.zkoss.zk.ui.Component;
33 import org.zkoss.zk.ui.event.Event;
34 import org.zkoss.zk.ui.event.EventListener;
35 import org.zkoss.zk.ui.event.Express;
36 import org.zkoss.zk.ui.sys.SessionsCtrl;
37 import org.zkoss.zk.ui.sys.ExecutionCtrl;
38 import org.zkoss.zk.ui.sys.ExecutionsCtrl;
39 import org.zkoss.zk.ui.sys.DesktopCtrl;
40 import org.zkoss.zk.ui.sys.ComponentCtrl;
41 import org.zkoss.zk.ui.sys.ComponentsCtrl;
42 import org.zkoss.zk.ui.sys.EventProcessingThread;
43 import org.zkoss.zk.ui.metainfo.ZScript;
44 import org.zkoss.zk.scripting.Namespace;
45 import org.zkoss.zk.scripting.Namespaces;
46
47 /**
48 * A utility class that simplify the implementation of
49 * {@link org.zkoss.zk.ui.sys.EventProcessingThread}.
50 *
51 * @author tomyeh
52 */
53 public class EventProcessor {
54 // private static final Log log = Log.lookup(EventProcessor.class);
55
56 /** The desktop that the component belongs to. */
57 private final Desktop _desktop;
58 /** Part of the command: component to handle the event. */
59 private final Component _comp;
60 /** Part of the command: event to process. */
61 private Event _event;
62 /** Whether it is in processing an event.
63 * It is used only the event processing thread is disabled.
64 */
65 private static ThreadLocal _inEvt;
66
67 /** Returns whether the current thread is an event listener.
68 */
69 public static final boolean inEventListener() {
70 return (Thread.currentThread() instanceof EventProcessingThread)
71 || (_inEvt != null && _inEvt.get() != null); //used if event thread is disabled
72 }
73 /** Sets whether the current thread is an event listener.
74 * It needs to be called only if the event processing thread is
75 * disabled.
76 *
77 * <p>It is used only internally.
78 */
79 /*package*/ static final void inEventListener(boolean in) {
80 if (in) {
81 if (_inEvt == null)
82 _inEvt = new ThreadLocal();
83 _inEvt.set(Boolean.TRUE);
84 } else {
85 if (_inEvt != null)
86 _inEvt.set(null);
87 }
88 }
89
90 /**
91 * @param comp the component. Its desktop must be either null
92 * or the same as desktop.
93 */
94 public EventProcessor(Desktop desktop, Component comp, Event event) {
95 if (desktop == null || comp == null || event == null)
96 throw new IllegalArgumentException("null");
97
98 final Desktop dt = comp.getDesktop();
99 if (dt != null && desktop != dt)
100 throw new IllegalStateException("Process events for another desktop? "+comp);
101
102 _desktop = desktop;
103 _comp = comp;
104 _event = event;
105 }
106
107 /** Returns the desktop.
108 */
109 public final Desktop getDesktop() {
110 return _desktop;
111 }
112 /** Returns the event.
113 */
114 public final Event getEvent() {
115 return _event;
116 }
117 /** Returns the component.
118 */
119 public final Component getComponent() {
120 return _comp;
121 }
122
123 /** Process the event.
124 * Note: it doesn't invoke EventThreadInit and EventThreadCleanup.
125 *
126 * <p>This method is to implement
127 * {@link org.zkoss.zk.ui.sys.EventProcessingThread}.
128 * See also {@link org.zkoss.zk.ui.util.Configuration#isEventThreadEnabled}.
129 */
130 public void process() throws Exception {
131 //Bug 1506712: event listeners might be zscript, so we have to
132 //keep built-in variables as long as possible
133 final HashMap backup = new HashMap();
134 final Namespace ns = Namespaces.beforeInterpret(backup, _comp, true);
135 //we have to push since process0 might invoke methods from zscript class
136 try {
137 Namespaces.backupVariable(backup, ns, "event");
138 ns.setVariable("event", _event, true);
139
140 _event = ((DesktopCtrl)_desktop).beforeProcessEvent(_event);
141 if (_event != null) {
142 ns.setVariable("event", _event, true); //_event might change
143 process0(ns);
144 ((DesktopCtrl)_desktop).afterProcessEvent(_event);
145 }
146 } finally {
147 Namespaces.afterInterpret(backup, ns, true);
148 }
149 }
150 private void process0(Namespace ns) throws Exception {
151 final Page page = getPage();
152 final String evtnm = _event.getName();
153
154 final Set listenerCalled = new HashSet();
155 boolean retry = false;
156 for (Iterator it = _comp.getListenerIterator(evtnm);;) {
157 final EventListener el = nextListener(it);
158 if (el == null) {
159 break; //done
160
161 } else if (el == RETRY) {
162 retry = true;
163 it = _comp.getListenerIterator(evtnm);
164
165 } else if ((el instanceof Express)
166 && (!retry || !listenerCalled.contains(el))) {
167 listenerCalled.add(el);
168
169 el.onEvent(_event);
170 if (!_event.isPropagatable())
171 return; //done
172 }
173 }
174
175 final ZScript zscript = ((ComponentCtrl)_comp).getEventHandler(evtnm);
176 if (zscript != null) {
177 page.interpret(
178 zscript.getLanguage(), zscript.getContent(page, _comp), ns);
179 if (!_event.isPropagatable())
180 return; //done
181 }
182
183 retry = false;
184 for (Iterator it = _comp.getListenerIterator(evtnm);;) {
185 final EventListener el = nextListener(it);
186 if (el == null) {
187 break; //done
188
189 } else if (el == RETRY) {
190 retry = true;
191 it = _comp.getListenerIterator(evtnm);
192
193 } else if (!(el instanceof Express)
194 && (!retry || !listenerCalled.contains(el))) {
195 listenerCalled.add(el);
196
197 el.onEvent(_event);
198 if (!_event.isPropagatable())
199 return; //done
200 }
201 }
202
203 final Method mtd =
204 ComponentsCtrl.getEventMethod(_comp.getClass(), evtnm);
205 if (mtd != null) {
206 // if (log.finerable()) log.finer("Method for event="+evtnm+" comp="+_comp+" method="+mtd);
207
208 if (mtd.getParameterTypes().length == 0)
209 mtd.invoke(_comp, null);
210 else
211 mtd.invoke(_comp, new Object[] {_event});
212 if (!_event.isPropagatable())
213 return; //done
214 }
215
216 retry = false;
217 listenerCalled.clear();
218 for (Iterator it = page.getListenerIterator(evtnm);;) {
219 final EventListener el = nextListener(it);
220 if (el == null) {
221 break; //done
222
223 } else if (el == RETRY) {
224 retry = true;
225 it = page.getListenerIterator(evtnm);
226
227 } else if (!retry || !listenerCalled.contains(el)) {
228 listenerCalled.add(el);
229
230 el.onEvent(_event);
231 if (!_event.isPropagatable())
232 return; //done
233 }
234 }
235 }
236 private static EventListener nextListener(Iterator it) {
237 try {
238 return it.hasNext() ? (EventListener)it.next(): null;
239 } catch (java.util.ConcurrentModificationException ex) {
240 return RETRY;
241 }
242 }
243 /** Represents {@link #nextListener} encounter co-modification error. */
244 private static final EventListener RETRY = new EventListener() {
245 public void onEvent(Event event) {
246 }
247 };
248
249 /** Setup this processor before processing the event by calling
250 * {@link #process}.
251 *
252 * <p>Note: it doesn't invoke {@link ExecutionCtrl#onActivate}
253 */
254 public void setup() {
255 SessionsCtrl.setCurrent(_desktop.getSession());
256 final Execution exec = _desktop.getExecution();
257 ExecutionsCtrl.setCurrent(exec);
258 ((ExecutionCtrl)exec).setCurrentPage(getPage());
259 }
260 /** Cleanup this process after processing the event by calling
261 * {@link #process}.
262 *
263 * <p>Note: Don't call this method if the event process executes
264 * in the same thread.
265 */
266 public void cleanup() {
267 ExecutionsCtrl.setCurrent(null);
268 SessionsCtrl.setCurrent(null);
269 }
270
271 private Page getPage() {
272 final Page page = _comp.getPage();
273 if (page != null)
274 return page;
275
276 final Iterator it = _desktop.getPages().iterator();
277 return it.hasNext() ? (Page)it.next(): null;
278 }
279
280 //Object//
281 public String toString() {
282 return "[comp: "+_comp+", event: "+_event+']';
283 }
284 }