001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx;
021 
022 import js.ArrayLike;
023 import js.Id;
024 import js.Initializer;
025 import js.Js;
026 import js.ObjectLike;
027 import js.JsApplet;
028 import js.Static;
029 import js.Var;
030 import js.Vars;
031 import jsx.Configurable;
032 import jsx.core.ArrayLikes;
033 import jsx.core.ObjectLikes;
034 import jsx.core.Variables;
035 
036 /**
037  * <p>An abstract base class for event sources that fire high level events.</p>
038  * <p>This class provides a simple but efficient high level event model. An event is an 
039  * object of the {@link Source.Event} class. An event source is an object of the {@link Source} 
040  * class. An event can be executed immediately from an event source with a call to the 
041  * {@link Source#exec(Event)} method. It can also be raised from a source for general 
042  * dispatching with a call to the {@link Source#fire(Event)} method, or for a specific 
043  * dispatcher with a call to the {@link Source#fire(Event, TaskManager)} method. An event 
044  * dispatcher is actually a task manager of the {@link TaskManager} class. A fired event 
045  * is, after converted to an event task, submitted to the task manager for scheduling to 
046  * get handled by the listeners registered with the type of the events.</p>
047  * <p>An event listener together with the type of events it handles must be registered with 
048  * an event source object for an event task manager to dispatch events of the type to the 
049  * listener for handling when the source object fires the events of that type. With one 
050  * event type, an arbitrary number of listeners can be registered. The listeners registered 
051  * to an event source with the same event type handle a dispatched event in the order 
052  * registration.</p>
053  * <p>Note that, event sources are {@link Configurable}.</p>
054  * 
055  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
056  */
057 public abstract class Source extends Configurable
058 {
059     /**
060      * <p>A typical constructor forcing constructors of subclasses to pass initializing data.
061      * <p>This constructor simply invokes the typical constructor of the superclass passing a 
062      * copy of the specified initializing object, or if the argument one is undefined, a newly 
063      * created {@link ObjectLike} object as the argument.</p>
064      * @param ini The initializing object that can also be created with an object literal. 
065      * @since 1.0
066      */
067     protected Source(ObjectLike ini) {
068         super(Js.be(ini) ? Js.apply(
069                 new Initializer().var(),
070                 ini
071         ) : new Initializer().var());
072     }
073 
074     private final ObjectLike eventListeners = Js.object();
075 
076     /**
077      * <p>Gets all listeners of the specified event type and registered with the specified 
078      * event source.</p>
079      * @param src The event source to get listeners from.
080      * @param et The event type to get listeners of.
081      * @return An array of the event listeners of the type and registered with the source.
082      * @since 1.0
083      */
084     @SuppressWarnings("unchecked")
085     public static final <T> ArrayLike<T> getListeners(Source src, Class<? extends Event<T>> et) {
086         String type = et.getName();
087         ObjectLike eventListeners = src.eventListeners;
088         synchronized(eventListeners) {
089             ArrayLike<T> typed = (ArrayLike<T>)eventListeners.var(type);
090             return Js.be(typed) ? ArrayLikes.slice(typed, 0) : new Vars<T>().var();
091         }
092     }
093 
094     /**
095      * <p>Registers an event listener, together with the event type it is allowed to handle, 
096      * to this event source.</p>
097      * <p>Note that, this method is not declared final so that subclasses can override it 
098      * to provide more flexible wrapping support.</p>
099      * @param et The event type this source allows the listener to handle.
100      * @param on The event listener this source allows to handle the type of event.
101      * @since 1.0
102      */
103     public final <T> void addListener(Class<? extends Event<T>> et, T on) {
104         if (Js.be(on)) {
105             String type = et.getName();
106             synchronized(eventListeners) {
107                 ArrayLike<?> listeners = (ArrayLike<?>)eventListeners.var(type);
108                 if (Variables.undefined(listeners)) {
109                     listeners = new Vars<Object>().var();
110                     eventListeners.var(type, listeners);
111                 }
112                 if (ArrayLikes.indexOf(listeners, on) == -1) {
113                     ArrayLikes.push((ArrayLike<?>)eventListeners.var(type), on);
114                 }
115             }
116         }
117     }
118 
119     private static final void remove(ObjectLike eventListeners, String type, Object on) {
120         ArrayLike<?> listeners = (ArrayLike<?>)eventListeners.var(type);
121         if (Js.be(listeners)) {
122             ArrayLikes.remove(listeners, on);
123             if (Js.not(ArrayLikes.length(listeners))) {
124                 ObjectLikes.delete(eventListeners, type);
125             }
126         }
127     }
128 
129     /**
130      * <p>Unregisters an event listener for all event types.</p>
131      * @param on The event listener this source allows to handle events.
132      * @since 1.0
133      */
134     public final void removeListener(Object on) {
135         if (Js.be(on)) {
136             synchronized(eventListeners) {
137                 ArrayLike<String> types = Js.keys(eventListeners);
138                 for (int i = 0, len = ArrayLikes.length(types); i < len; i++) {
139                     String type = types.get(i);
140                     remove(eventListeners, type, on);
141                 }
142             }
143         }
144     }
145 
146     /**
147      * <p>Unregisters an event listener, together with the event type it is allowed to 
148      * handle, from this event source.</p>
149      * <p>Note that, this method is not declared final so that subclasses can override it 
150      * to provide more flexible wrapping support.</p>
151      * @param et The event type this source allows the listener to handle.
152      * @param on The event listener this source allows to handle the type of event.
153      * @since 1.0
154      */
155     public final <T> void removeListener(Class<? extends Event<T>> et, T on) {
156         if (Js.be(on)) {
157             String type = et.getName();
158             synchronized(eventListeners) {
159                 remove(eventListeners, type, on);
160             }
161         }
162     }
163 
164     /**
165      * <p>Stops firing a certain type of events from this event source.</p>
166      * <p>Note that, this method is not declared final so that subclasses can override it 
167      * to provide more flexible wrapping support.</p>
168      * @param et The type of events this source fires, which is to be disabled.
169      * @since 1.0
170      */
171     public final <T> void removeListeners(Class<? extends Event<T>> et) {
172         synchronized(eventListeners) {
173             ObjectLikes.delete(eventListeners, et.getName());
174         }
175     }
176 
177     /**
178      * <p>Returns the wrapped source object if this one is a wrapper source.</p>
179      * <p>The high level event model supports the wrapping technology widely used in this 
180      * library. The wrapped object of an event source must also be an event source. The 
181      * event queuing and dispatching mechanism in this class tries to dispatch an event 
182      * fired from an event source also to its wrapped source by calling this method, if 
183      * this source does wrap another valid source.</p>
184      * <p>A concrete method overriding this one in a subclass may return <tt>null</tt> for 
185      * it does not wrap, the wrapped source object if it wraps one, or the current instance 
186      * itself if it is a wrapped source but does not wrap others.</p>
187      * @return The wrapped source object.
188      * @since 1.0
189      */
190     protected abstract Source unwrap();
191     /**
192      * <p>Returns the parent source object graphically containing the current one.</p>
193      * <p>The wrapped object of an event source must also be an event source. The event 
194      * queuing and dispatching mechanism of this class tries to dispatch an event fired 
195      * from an event source also to its containing source by calling this method, if this 
196      * source is graphically contained by another valid source and the event wants to 
197      * bubble, that is, its {@link Event#BUBBLE} configurable property is <tt>true</tt>.</p>
198      * <p>A concrete method overriding this one in a subclass may return <tt>null</tt> when 
199      * it is neither graphical nor contained, or the parent source object if it has one.</p>
200      * @return The source object graphically containing this source object.
201      * @since 1.0
202      */
203     protected abstract Source getParent();
204 
205     private static final <T> int execute(Event<T> evt) {
206         Source src = ini(evt).var(Event.SOURCE);
207         int found = 0;
208         do {
209             found += execute(src, evt);
210             Source e = src.unwrap();
211             if (Js.be(e) && Js.neq(src, e)) {
212                 found += execute(e, evt);
213             }
214             src = src.getParent();
215         } while (Js.be(ini(evt).var(Event.BUBBLE)) && Js.be(src));
216         return found;
217     }
218 
219     @SuppressWarnings("unchecked")
220     private static final <T> int execute(Source src, Event<T> evt) {
221         ArrayLike<?> listeners = getListeners(
222                 src,
223                 (Class<? extends Event<T>>)evt.getClass()
224         );
225         int len = ArrayLikes.length(listeners);
226         for (int i = 0; i < len; i++) {
227             evt.execute((T)listeners.get(i));
228         }
229         return len;
230     }
231 
232     private static boolean setSource(Event<?> evt, Source src) {
233         Source s = ini(evt).var(Event.SOURCE);
234         if (Js.eq(src, s)) {
235             return true;
236         } else if (Js.not(s)) {
237             ini(evt).var(Event.SOURCE, src);
238             return true;
239         } else {
240             return false;
241         }
242     }
243 
244     /**
245      * <p>Executes an event from the current source object immediately if the source 
246      * object fires that type of events.</p>
247      * <p>An event becomes fired when it is successfully executed with this method.</p>
248      * <p>The method does nothing if the event has been fired, or the event source does not 
249      * fire that type of events, that is, the source does not have any listeners registered 
250      * to it for the type of the event.</p>
251      * <p>Note that, the caller of this method will not return until the event task has been 
252      * finished running, and  in JS Simulation mode the event is run in the same thread as 
253      * the caller of this method does.</p>
254      * @param evt The event to execute.
255      * @return The count of listeners which are executed successfully from the event 
256      * source. The method returns <tt>0</tt> if the event has been fired, or if the 
257      * event source does not fire that type of events, that is, the source does not have 
258      * any listeners registered to it with the type of the event.
259      * @since 1.0
260      */
261     public final <T> int exec(Event<T> evt) {
262         if (setSource(evt, this)) {
263             return execute(evt);
264         }
265         return 0;
266     }
267 
268     private final static Var<TaskManager> DISPATCHER = new Static<TaskManager>(
269             new Var<TaskManager>() {
270                 @Override
271                 public TaskManager var() {
272                     return new TaskManager();
273                 }
274             }
275     );
276 
277     /**
278      * <p>Raises the specified event from the current event source, to be queued for 
279      * general event dispatching.</p>
280      * <p>The method does nothing if the event has been fired, or the event source does not 
281      * fire that type of events, that is, the source does not have any listeners registered 
282      * to it for the type of the event.</p>
283      * <p>An event dispatcher is actually a task manager. The fired event is, after 
284      * converted to an event task, submitted to the task manager for dispatching.</p>
285      * <p>The general event dispatcher processes events globally in default. Submitting 
286      * events to the general event dispatcher makes sure all events are dispatched in the 
287      * sequence that they are fired. Unless you know exactly what would be happening, use 
288      * {@link #fire(Event)} instead of {@link #fire(Event, TaskManager)}.</p>
289      * <p>Note that, the caller of this method will return immediately after raising the 
290      * event without having to wait for the event task to run, and in JS Simulation mode 
291      * the event can be run in a different thread than the caller of this method does.</p>
292      * @param evt The event being fired from this source.
293      * @since 1.0
294      * @see #fire(Event, TaskManager)
295      * @see #exec(Event)
296      */
297     public final <T> void fire(Event<T> evt) {
298         fire(evt, DISPATCHER.var());
299     }
300 
301     /**
302      * <p>Raises the specified event from the current event source, to be queued for 
303      * specific event dispatching.</p>
304      * <p>The method does nothing if the event has been fired or the event source does not 
305      * fire that type of events, that is, the source does not have any listeners registered 
306      * with the type of the event.</p>
307      * <p>An event dispatcher is actually a task manager. The fired event is, after 
308      * converted to an event task, submitted to the task manager for dispatching.</p>
309      * <p>The general event dispatcher processes events globally in default. Submitting 
310      * events to the general event dispatcher makes sure all events are dispatched in the 
311      * sequence that they are fired. Unless you know exactly what would be happening, use 
312      * {@link #fire(Event)} instead of {@link #fire(Event, TaskManager)}.</p>
313      * <p>Note that, the caller of this method will return immediately after raising the 
314      * event without having to wait for the event task to run, and in JS Simulation mode 
315      * the event can be run in a different thread as the caller of this method does.</p>
316      * @param evt The event being fired from this source.
317      * @param dispatcher The event dispatcher to dispatch the fired event. 
318      * @since 1.0
319      * @see #fire(Event)
320      * @see #exec(Event)
321      */
322     public final <T> void fire(Event<T> evt, TaskManager dispatcher) {
323         if (setSource(evt, this)) {
324             evt = update(evt, dispatcher);
325             dispatcher.submit(
326                     new EventTask<T>(evt)
327             );
328         }
329     }
330 
331     private final static <T> Event<T> update(Event<T> evt, TaskManager dispatcher) {
332         if (Js.be(ini(evt).var(Event.UPDATE))) {
333             EventTask<?> et = (EventTask<?>)dispatcher.pop();
334             if (Js.be(et)) {
335                 Event<T> e = et.event.update(evt);
336                 if (Js.be(e)) {
337                     evt = e;
338                 } else {
339                     dispatcher.submit(et);
340                 }
341             }
342         }
343         return evt;
344     }
345 
346     private static final class EventTask<T> extends Task
347     {
348         private final Event<T> event;
349 
350         private EventTask(Event<T> event) {
351             this.event = event;
352         }
353 
354         @Override
355         public void run() {
356             synchronized(JsApplet.class) {
357                 execute(event);
358             }
359         }
360     }
361 
362     /**
363      * <p>An abstract base class for high level events.</p>
364      * <p>This class is designed generic with the type parameter being the type of its 
365      * corresponding listener type. A subclass must be concrete and final if it is to be an 
366      * event type. As an event type, the subclass must also declare the type parameter of 
367      * this class as the type of its own listener.</p>
368      * <p>An event listener is typically of an interface with a declared method to react on 
369      * the event it handles so that the class of the event can implement the {@link Event#execute(Object)} 
370      * method by simply calling that declared method passing the event itself as the argument. 
371      * A subclass of this class defines events uniquely typed by the subclass itself and 
372      * designates a listener type corresponding to that type of events.</p>
373      * <p>For an event source to fire an event, the listener of the event must have been 
374      * registered onto the source object with a call to the {@link Source#addListener(Class, Object)} 
375      * method which asks both the type of the event and the listener object for arguments.</p>
376      * <p>Note that, high level events are {@link Configurable}.</p>
377      * 
378      * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
379      */
380     public static abstract class Event<T> extends Configurable
381     {
382         /**
383          * <p>A typical constructor forcing constructors of subclasses to pass 
384          * initializing data.</p>
385          * <p>Note that, this constructor does not change the configurable properties 
386          * {@link Event#BUBBLE} and {@link Event#UPDATE} of this event object.</p>
387          * @since 1.0
388          */
389         protected Event(ObjectLike ini) {
390             super(ini);
391         }
392 
393         /**
394          * <p>A default constructor creating the initializing object by itself.</p>
395          * <p>Note that, this constructor creates an initializing object for this 
396          * configurable object but does not change the configurable properties 
397          * {@link Event#BUBBLE} and {@link Event#UPDATE} of this event object.</p>
398          * @since 1.0
399          */
400         protected Event() {
401             this(new Initializer().var());
402         }
403 
404         /**
405          * <p>A global identifier for a configurable property of an {@link Event} object.</p>
406          * <p>The identified configurable property of an {@link Event} object 
407          * refers to a boolean value specifying whether the event bubbles while being 
408          * dispatched.</p>
409          * <p>Note that, an event does not bubble unless specified otherwise.</p>
410          * @since 1.0
411          */
412         public final static Id<Boolean> BUBBLE = new Id<Boolean>();
413         /**
414          * <p>A global identifier for a configurable property of an {@link Event} object.</p>
415          * <p>The identified configurable property of an {@link Event} object 
416          * refers to a boolean value specifying whether the event updates, while being 
417          * dispatched, the one previously fired if it is still pending.</p>
418          * <p>Note that, an event does not update unless specified otherwise.</p>
419          * @since 1.0
420          */
421         public final static Id<Boolean> UPDATE = new Id<Boolean>();
422 
423         /**
424          * <p>A global identifier for a configurable property of an {@link Event} object.</p>
425          * <p>The identified configurable property of an {@link Event} object 
426          * is a reference to the source object of the event.</p>
427          * <p>The property is undefined before the event is fired on a source. Fire a 
428          * fired event is meaningless.</p>
429          * @since 1.0
430          * @see Source#exec(Event)
431          * @see Source#fire(Event)
432          * @see Source#fire(Event, TaskManager)
433          */
434         protected final static Id<Source> SOURCE = new Id<Source>();
435 
436         /**
437          * <p>Gets the source object of an event.</p>
438          * <p>This method simply returns the configurable property {@link #SOURCE} of the 
439          * specified event.</p>
440          * @param evt The event to get source.
441          * @return The source of the specified event. This method returns <tt>null</tt> 
442          * if the event has not been fired. Firing a fired event is meaningless.
443          * @since 1.0
444          * @see Source#exec(Event)
445          * @see Source#fire(Event)
446          * @see Source#fire(Event, TaskManager)
447          */
448         public static final Source source(Event<?> evt) {
449             return ini(evt).var(SOURCE);
450         }
451 
452         /**
453          * <p>Determines how to update a pending event with this one.</p>
454          * <p>This method simply returns this event if the argument event has the same 
455          * event type and event source; Otherwise, it returns <tt>null</tt>. Subclasses 
456          * may override this method to provide different criteria.</p>
457          * @param evt The event to update.
458          * @return The updated event. This method returns <tt>null</tt> if the update 
459          * criteria does not meet.
460          * @since 1.0
461          * @see Source#exec(Event)
462          * @see Source#fire(Event)
463          * @see Source#fire(Event, TaskManager)
464          */
465         protected <S> Event<S> update(Event<S> evt) {
466             return evt.getClass() == getClass() &&
467                 source(this) == source(evt) ? evt : null;
468         }
469 
470         /**
471          * <p>Dispatches this event to the specified event listener to handle, by invoking the 
472          * handler method of the listener interface passing the event itself as the argument.</p>
473          * <p>Concrete subclasses must implement this method making the listener handle 
474          * this event.</p>
475          * @param on The event listener.
476          * @since 1.0
477          */
478         protected abstract void execute(T on);
479     }
480 }