001: /* EventProcessor.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Tue May 8 14:10:54 2007, Created by tomyeh
010: }}IS_NOTE
011:
012: Copyright (C) 2007 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.zk.ui.impl;
020:
021: import java.util.Set;
022: import java.util.HashSet;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.lang.reflect.Method;
026:
027: import org.zkoss.util.logging.Log;
028:
029: import org.zkoss.zk.ui.Execution;
030: import org.zkoss.zk.ui.Desktop;
031: import org.zkoss.zk.ui.Page;
032: import org.zkoss.zk.ui.Component;
033: import org.zkoss.zk.ui.event.Event;
034: import org.zkoss.zk.ui.event.EventListener;
035: import org.zkoss.zk.ui.event.Express;
036: import org.zkoss.zk.ui.sys.SessionsCtrl;
037: import org.zkoss.zk.ui.sys.ExecutionCtrl;
038: import org.zkoss.zk.ui.sys.ExecutionsCtrl;
039: import org.zkoss.zk.ui.sys.DesktopCtrl;
040: import org.zkoss.zk.ui.sys.ComponentCtrl;
041: import org.zkoss.zk.ui.sys.ComponentsCtrl;
042: import org.zkoss.zk.ui.sys.EventProcessingThread;
043: import org.zkoss.zk.ui.metainfo.ZScript;
044: import org.zkoss.zk.scripting.Namespace;
045: import org.zkoss.zk.scripting.Namespaces;
046:
047: /**
048: * A utility class that simplify the implementation of
049: * {@link org.zkoss.zk.ui.sys.EventProcessingThread}.
050: *
051: * @author tomyeh
052: */
053: public class EventProcessor {
054: // private static final Log log = Log.lookup(EventProcessor.class);
055:
056: /** The desktop that the component belongs to. */
057: private final Desktop _desktop;
058: /** Part of the command: component to handle the event. */
059: private final Component _comp;
060: /** Part of the command: event to process. */
061: private Event _event;
062: /** Whether it is in processing an event.
063: * It is used only the event processing thread is disabled.
064: */
065: private static ThreadLocal _inEvt;
066:
067: /** Returns whether the current thread is an event listener.
068: */
069: public static final boolean inEventListener() {
070: return (Thread.currentThread() instanceof EventProcessingThread)
071: || (_inEvt != null && _inEvt.get() != null); //used if event thread is disabled
072: }
073:
074: /** Sets whether the current thread is an event listener.
075: * It needs to be called only if the event processing thread is
076: * disabled.
077: *
078: * <p>It is used only internally.
079: */
080: /*package*/static final void inEventListener(boolean in) {
081: if (in) {
082: if (_inEvt == null)
083: _inEvt = new ThreadLocal();
084: _inEvt.set(Boolean.TRUE);
085: } else {
086: if (_inEvt != null)
087: _inEvt.set(null);
088: }
089: }
090:
091: /**
092: * @param comp the component. Its desktop must be either null
093: * or the same as desktop.
094: */
095: public EventProcessor(Desktop desktop, Component comp, Event event) {
096: if (desktop == null || comp == null || event == null)
097: throw new IllegalArgumentException("null");
098:
099: final Desktop dt = comp.getDesktop();
100: if (dt != null && desktop != dt)
101: throw new IllegalStateException(
102: "Process events for another desktop? " + comp);
103:
104: _desktop = desktop;
105: _comp = comp;
106: _event = event;
107: }
108:
109: /** Returns the desktop.
110: */
111: public final Desktop getDesktop() {
112: return _desktop;
113: }
114:
115: /** Returns the event.
116: */
117: public final Event getEvent() {
118: return _event;
119: }
120:
121: /** Returns the component.
122: */
123: public final Component getComponent() {
124: return _comp;
125: }
126:
127: /** Process the event.
128: * Note: it doesn't invoke EventThreadInit and EventThreadCleanup.
129: *
130: * <p>This method is to implement
131: * {@link org.zkoss.zk.ui.sys.EventProcessingThread}.
132: * See also {@link org.zkoss.zk.ui.util.Configuration#isEventThreadEnabled}.
133: */
134: public void process() throws Exception {
135: //Bug 1506712: event listeners might be zscript, so we have to
136: //keep built-in variables as long as possible
137: final HashMap backup = new HashMap();
138: final Namespace ns = Namespaces.beforeInterpret(backup, _comp,
139: true);
140: //we have to push since process0 might invoke methods from zscript class
141: try {
142: Namespaces.backupVariable(backup, ns, "event");
143: ns.setVariable("event", _event, true);
144:
145: _event = ((DesktopCtrl) _desktop)
146: .beforeProcessEvent(_event);
147: if (_event != null) {
148: ns.setVariable("event", _event, true); //_event might change
149: process0(ns);
150: ((DesktopCtrl) _desktop).afterProcessEvent(_event);
151: }
152: } finally {
153: Namespaces.afterInterpret(backup, ns, true);
154: }
155: }
156:
157: private void process0(Namespace ns) throws Exception {
158: final Page page = getPage();
159: final String evtnm = _event.getName();
160:
161: final Set listenerCalled = new HashSet();
162: boolean retry = false;
163: for (Iterator it = _comp.getListenerIterator(evtnm);;) {
164: final EventListener el = nextListener(it);
165: if (el == null) {
166: break; //done
167:
168: } else if (el == RETRY) {
169: retry = true;
170: it = _comp.getListenerIterator(evtnm);
171:
172: } else if ((el instanceof Express)
173: && (!retry || !listenerCalled.contains(el))) {
174: listenerCalled.add(el);
175:
176: el.onEvent(_event);
177: if (!_event.isPropagatable())
178: return; //done
179: }
180: }
181:
182: final ZScript zscript = ((ComponentCtrl) _comp)
183: .getEventHandler(evtnm);
184: if (zscript != null) {
185: page.interpret(zscript.getLanguage(), zscript.getContent(
186: page, _comp), ns);
187: if (!_event.isPropagatable())
188: return; //done
189: }
190:
191: retry = false;
192: for (Iterator it = _comp.getListenerIterator(evtnm);;) {
193: final EventListener el = nextListener(it);
194: if (el == null) {
195: break; //done
196:
197: } else if (el == RETRY) {
198: retry = true;
199: it = _comp.getListenerIterator(evtnm);
200:
201: } else if (!(el instanceof Express)
202: && (!retry || !listenerCalled.contains(el))) {
203: listenerCalled.add(el);
204:
205: el.onEvent(_event);
206: if (!_event.isPropagatable())
207: return; //done
208: }
209: }
210:
211: final Method mtd = ComponentsCtrl.getEventMethod(_comp
212: .getClass(), evtnm);
213: if (mtd != null) {
214: // if (log.finerable()) log.finer("Method for event="+evtnm+" comp="+_comp+" method="+mtd);
215:
216: if (mtd.getParameterTypes().length == 0)
217: mtd.invoke(_comp, null);
218: else
219: mtd.invoke(_comp, new Object[] { _event });
220: if (!_event.isPropagatable())
221: return; //done
222: }
223:
224: retry = false;
225: listenerCalled.clear();
226: for (Iterator it = page.getListenerIterator(evtnm);;) {
227: final EventListener el = nextListener(it);
228: if (el == null) {
229: break; //done
230:
231: } else if (el == RETRY) {
232: retry = true;
233: it = page.getListenerIterator(evtnm);
234:
235: } else if (!retry || !listenerCalled.contains(el)) {
236: listenerCalled.add(el);
237:
238: el.onEvent(_event);
239: if (!_event.isPropagatable())
240: return; //done
241: }
242: }
243: }
244:
245: private static EventListener nextListener(Iterator it) {
246: try {
247: return it.hasNext() ? (EventListener) it.next() : null;
248: } catch (java.util.ConcurrentModificationException ex) {
249: return RETRY;
250: }
251: }
252:
253: /** Represents {@link #nextListener} encounter co-modification error. */
254: private static final EventListener RETRY = new EventListener() {
255: public void onEvent(Event event) {
256: }
257: };
258:
259: /** Setup this processor before processing the event by calling
260: * {@link #process}.
261: *
262: * <p>Note: it doesn't invoke {@link ExecutionCtrl#onActivate}
263: */
264: public void setup() {
265: SessionsCtrl.setCurrent(_desktop.getSession());
266: final Execution exec = _desktop.getExecution();
267: ExecutionsCtrl.setCurrent(exec);
268: ((ExecutionCtrl) exec).setCurrentPage(getPage());
269: }
270:
271: /** Cleanup this process after processing the event by calling
272: * {@link #process}.
273: *
274: * <p>Note: Don't call this method if the event process executes
275: * in the same thread.
276: */
277: public void cleanup() {
278: ExecutionsCtrl.setCurrent(null);
279: SessionsCtrl.setCurrent(null);
280: }
281:
282: private Page getPage() {
283: final Page page = _comp.getPage();
284: if (page != null)
285: return page;
286:
287: final Iterator it = _desktop.getPages().iterator();
288: return it.hasNext() ? (Page) it.next() : null;
289: }
290:
291: //Object//
292: public String toString() {
293: return "[comp: " + _comp + ", event: " + _event + ']';
294: }
295: }
|