KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > dom > events > EventSupport


1 /*
2
3    Copyright 2000-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.dom.events;
19
20 import org.apache.batik.dom.util.HashTable;
21 import org.w3c.dom.events.Event JavaDoc;
22 import org.w3c.dom.events.EventException JavaDoc;
23 import org.w3c.dom.events.EventListener JavaDoc;
24
25 /**
26  * The class allows registration and removal of EventListeners on
27  * an NodeEventTarget and dispatch of events to that NodeEventTarget.
28  *
29  * @see NodeEventTarget
30  * @author <a HREF="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
31  * @author <a HREF="mailto:stephane@hillion.org">Stephane Hillion</a>
32  * @version $Id: EventSupport.java,v 1.17 2005/03/27 08:58:32 cam Exp $
33  */

34 public class EventSupport {
35
36     /**
37      * The capturing listeners table.
38      */

39     protected HashTable capturingListeners;
40
41     /**
42      * The bubbling listeners table.
43      */

44     protected HashTable bubblingListeners;
45
46     /**
47      * This method allows the registration of event listeners on the
48      * event target. If an <code>EventListener</code> is added to an
49      * <code>EventTarget</code> which is currently processing an event
50      * the new listener will not be triggered by the current event.
51      * <br> If multiple identical <code>EventListener</code>s are
52      * registered on the same <code>EventTarget</code> with the same
53      * parameters the duplicate instances are discarded. They do not
54      * cause the <code>EventListener</code> to be called twice and
55      * since they are discarded they do not need to be removed with
56      * the <code>removeEventListener</code> method.
57      *
58      * @param type The event type for which the user is registering
59      *
60      * @param listener The <code>listener</code> parameter takes an
61      * interface implemented by the user which contains the methods to
62      * be called when the event occurs.
63      *
64      * @param useCapture If true, <code>useCapture</code> indicates
65      * that the user wishes to initiate capture. After initiating
66      * capture, all events of the specified type will be dispatched to
67      * the registered <code>EventListener</code> before being
68      * dispatched to any <code>EventTargets</code> beneath them in the
69      * tree. Events which are bubbling upward through the tree will
70      * not trigger an <code>EventListener</code> designated to use
71      * capture.
72      */

73     public void addEventListener(String JavaDoc type, EventListener JavaDoc listener,
74                  boolean useCapture) {
75     HashTable listeners;
76     if (useCapture) {
77         if (capturingListeners == null) {
78         capturingListeners = new HashTable();
79         }
80         listeners = capturingListeners;
81     } else {
82         if (bubblingListeners == null) {
83         bubblingListeners = new HashTable();
84         }
85         listeners = bubblingListeners;
86     }
87     EventListenerList list = (EventListenerList) listeners.get(type);
88     if (list == null) {
89         list = new EventListenerList();
90         listeners.put(type, list);
91     }
92     if (!list.contains(listener)) {
93         list.add(listener);
94     }
95     }
96
97     /**
98      * This method allows the removal of event listeners from the
99      * event target. If an <code>EventListener</code> is removed from
100      * an <code>EventTarget</code> while it is processing an event, it
101      * will complete its current actions but will not be triggered
102      * again during any later stages of event flow. <br>If an
103      * <code>EventListener</code> is removed from an
104      * <code>EventTarget</code> which is currently processing an event
105      * the removed listener will still be triggered by the current
106      * event. <br>Calling <code>removeEventListener</code> with
107      * arguments which do not identify any currently registered
108      * <code>EventListener</code> on the <code>EventTarget</code> has
109      * no effect.
110      *
111      * @param type Specifies the event type of the
112      * <code>EventListener</code> being removed.
113      *
114      * @param listener The <code>EventListener</code> parameter
115      * indicates the <code>EventListener </code> to be removed.
116      *
117      * @param useCapture Specifies whether the
118      * <code>EventListener</code> being removed was registered as a
119      * capturing listener or not. If a listener was registered twice,
120      * one with capture and one without, each must be removed
121      * separately. Removal of a capturing listener does not affect a
122      * non-capturing version of the same listener, and vice versa.
123      */

124     public void removeEventListener(String JavaDoc type, EventListener JavaDoc listener,
125                     boolean useCapture) {
126     HashTable listeners;
127     if (useCapture) {
128         listeners = capturingListeners;
129     } else {
130         listeners = bubblingListeners;
131     }
132     if (listeners == null) {
133         return;
134     }
135     EventListenerList list = (EventListenerList)listeners.get(type);
136     if (list != null) {
137         list.remove(listener);
138             if (list.size() == 0) {
139                 listeners.remove(type);
140             }
141     }
142     }
143
144     /**
145      * This method allows the dispatch of events into the
146      * implementations event model. Events dispatched in this manner
147      * will have the same capturing and bubbling behavior as events
148      * dispatched directly by the implementation. The target of the
149      * event is the <code> EventTarget</code> on which
150      * <code>dispatchEvent</code> is called.
151      *
152      * @param target the target node
153      * @param e Specifies the event type, behavior, and contextual
154      * information to be used in processing the event.
155      *
156      * @return The return value of <code>dispatchEvent</code>
157      * indicates whether any of the listeners which handled the event
158      * called <code>preventDefault</code>. If
159      * <code>preventDefault</code> was called the value is false, else
160      * the value is true.
161      *
162      * @exception EventException
163      * UNSPECIFIED_EVENT_TYPE_ERR: Raised if the
164      * <code>Event</code>'s type was not specified by initializing
165      * the event before <code>dispatchEvent</code> was
166      * called. Specification of the <code>Event</code>'s type as
167      * <code>null</code> or an empty string will also trigger this
168      * exception.
169      */

170     public static boolean dispatchEvent(NodeEventTarget target, Event JavaDoc e)
171         throws EventException JavaDoc {
172     AbstractEvent evt = (AbstractEvent) e;
173     if (evt == null) {
174         return false;
175     }
176     String JavaDoc type = evt.getType();
177     if (type == null) {
178         throw createUnspecifiedEventTypeErr("Event type can't be null");
179     }
180     // fix event status
181
evt.setTarget(target);
182     evt.stopPropagation(false);
183     evt.preventDefault(false);
184     // dump the tree hierarchy from top to the target
185
NodeEventTarget [] ancestors = getAncestors(target);
186     // CAPTURING_PHASE : fire event listeners from top to EventTarget
187
evt.setEventPhase(Event.CAPTURING_PHASE);
188     for (int i=0; i < ancestors.length && !evt.getStopPropagation();
189          ++i) {
190         NodeEventTarget node = ancestors[i];
191         evt.setCurrentTarget(node);
192         fireEventListeners(node, evt, true);
193     }
194     // AT_TARGET : fire local event listeners
195
if (!evt.getStopPropagation()) {
196         evt.setEventPhase(Event.AT_TARGET);
197         evt.setCurrentTarget(target);
198         fireEventListeners(target, evt, false);
199     }
200     // BUBBLING_PHASE : fire event listeners from target to top
201
if (evt.getBubbles()) {
202         evt.setEventPhase(Event.BUBBLING_PHASE);
203         for (int i=ancestors.length-1;
204              i >=0 && !evt.getStopPropagation(); --i) {
205         NodeEventTarget node = ancestors[i];
206         evt.setCurrentTarget(node);
207         fireEventListeners(node, evt, false);
208         }
209     }
210     return !evt.getPreventDefault();
211     }
212
213     private static void fireEventListeners(NodeEventTarget node,
214                        Event JavaDoc evt, boolean useCapture) {
215     String JavaDoc type = evt.getType();
216     EventSupport support = node.getEventSupport();
217     // check if the event support has been instantiated
218
if (support == null) {
219         return;
220     }
221     EventListenerList list=support.getEventListeners(type, useCapture);
222     // check if the event listeners list is not empty
223
if (list == null) {
224         return;
225     }
226     // dump event listeners, we get the registered listeners NOW
227
EventListener JavaDoc [] listeners = list.getEventListeners();
228     // check if event listeners with the correct event type exist
229
if (listeners == null) {
230         return;
231     }
232     // fire event listeners
233
for (int i=0; i < listeners.length; ++i) {
234         try {
235         listeners[i].handleEvent(evt);
236             } catch (ThreadDeath JavaDoc td) {
237                 throw td;
238         } catch (Throwable JavaDoc th) {
239                 th.printStackTrace();
240         }
241     }
242     }
243
244     // Returns all ancestors of the specified node
245
private static NodeEventTarget [] getAncestors(NodeEventTarget node) {
246     node = node.getParentNodeEventTarget(); // skip current node
247
int nancestors = 0;
248     for (NodeEventTarget n = node;
249              n != null;
250              n = n.getParentNodeEventTarget(), nancestors++) {}
251     NodeEventTarget [] ancestors = new NodeEventTarget[nancestors];
252     for (int i=nancestors-1;
253              i >= 0;
254              --i, node = node.getParentNodeEventTarget()) {
255         ancestors[i] = node;
256     }
257     return ancestors;
258     }
259
260     /**
261      * Returns a list event listeners depending on the specified event
262      * type and phase.
263      * @param type the event type
264      * @param useCapture
265      */

266     public EventListenerList getEventListeners(String JavaDoc type,
267                            boolean useCapture) {
268     HashTable listeners=(useCapture)?capturingListeners:bubblingListeners;
269     if (listeners == null) {
270         return null;
271     } else {
272         return (EventListenerList) listeners.get(type);
273     }
274     }
275
276     /**
277      * Creates an EventException. Overrides this method if you need to
278      * create your own RangeException subclass.
279      * @param code the exception code
280      * @param message the detail message
281      */

282     private static EventException JavaDoc createEventException(short code, String JavaDoc s) {
283     return new EventException JavaDoc(code, s);
284     }
285
286     private static EventException JavaDoc createUnspecifiedEventTypeErr(String JavaDoc s) {
287     return createEventException(EventException.UNSPECIFIED_EVENT_TYPE_ERR,
288                     s);
289     }
290 }
291
Popular Tags