3
Licensed to the Apache Software Foundation (ASF) under one or more
4
contributor license agreements. See the NOTICE file distributed with
5
this work for additional information regarding copyright ownership.
6
The ASF licenses this file to You under the Apache License, Version 2.0
7
(the "License"); you may not use this file except in compliance with
8
the License. You may obtain a copy of the License at
10
http://www.apache.org/licenses/LICENSE-2.0
12
Unless required by applicable law or agreed to in writing, software
13
distributed under the License is distributed on an "AS IS" BASIS,
14
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
See the License for the specific language governing permissions and
16
limitations under the License.
19
package org.apache.batik.dom.svg12;
21
import java.util.HashSet;
23
import org.apache.batik.dom.AbstractNode;
24
import org.apache.batik.dom.events.AbstractEvent;
25
import org.apache.batik.dom.events.EventListenerList;
26
import org.apache.batik.dom.events.EventSupport;
27
import org.apache.batik.dom.events.NodeEventTarget;
28
import org.apache.batik.dom.util.HashTable;
29
import org.apache.batik.dom.xbl.NodeXBL;
30
import org.apache.batik.dom.xbl.ShadowTreeEvent;
31
import org.apache.batik.util.XMLConstants;
33
import org.w3c.dom.DOMException;
34
import org.w3c.dom.Node;
35
import org.w3c.dom.NodeList;
36
import org.w3c.dom.events.Event;
37
import org.w3c.dom.events.EventException;
38
import org.w3c.dom.events.EventListener;
39
import org.w3c.dom.events.MutationEvent;
42
* An EventSupport class that handles XBL-specific event processing.
44
* @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
45
* @version $Id: XBLEventSupport.java 601207 2007-12-05 04:57:31Z cam $
47
public class XBLEventSupport extends EventSupport {
50
* The unstoppable capturing listeners table.
52
protected HashTable capturingImplementationListeners;
55
* The unstoppable bubbling listeners table.
57
protected HashTable bubblingImplementationListeners;
60
* Map of event types to their aliases.
62
protected static HashTable eventTypeAliases = new HashTable();
64
eventTypeAliases.put("SVGLoad", "load");
65
eventTypeAliases.put("SVGUnoad", "unload");
66
eventTypeAliases.put("SVGAbort", "abort");
67
eventTypeAliases.put("SVGError", "error");
68
eventTypeAliases.put("SVGResize", "resize");
69
eventTypeAliases.put("SVGScroll", "scroll");
70
eventTypeAliases.put("SVGZoom", "zoom");
74
* Creates a new XBLEventSupport object.
76
public XBLEventSupport(AbstractNode n) {
81
* Registers an event listener for the given namespaced event type
82
* in the specified group.
84
public void addEventListenerNS(String namespaceURI,
86
EventListener listener,
89
super.addEventListenerNS
90
(namespaceURI, type, listener, useCapture, group);
91
if (namespaceURI == null
92
|| namespaceURI.equals(XMLConstants.XML_EVENTS_NAMESPACE_URI)) {
93
String alias = (String) eventTypeAliases.get(type);
95
super.addEventListenerNS
96
(namespaceURI, alias, listener, useCapture, group);
102
* Deregisters an event listener.
104
public void removeEventListenerNS(String namespaceURI,
106
EventListener listener,
107
boolean useCapture) {
108
super.removeEventListenerNS(namespaceURI, type, listener, useCapture);
109
if (namespaceURI == null
110
|| namespaceURI.equals(XMLConstants.XML_EVENTS_NAMESPACE_URI)) {
111
String alias = (String) eventTypeAliases.get(type);
113
super.removeEventListenerNS
114
(namespaceURI, alias, listener, useCapture);
120
* Registers an event listener that will not be stopped by the usual
123
public void addImplementationEventListenerNS(String namespaceURI,
125
EventListener listener,
126
boolean useCapture) {
129
if (capturingImplementationListeners == null) {
130
capturingImplementationListeners = new HashTable();
132
listeners = capturingImplementationListeners;
134
if (bubblingImplementationListeners == null) {
135
bubblingImplementationListeners = new HashTable();
137
listeners = bubblingImplementationListeners;
139
EventListenerList list = (EventListenerList) listeners.get(type);
141
list = new EventListenerList();
142
listeners.put(type, list);
144
list.addListener(namespaceURI, null, listener);
148
* Unregisters an implementation event listener.
150
public void removeImplementationEventListenerNS(String namespaceURI,
152
EventListener listener,
153
boolean useCapture) {
154
HashTable listeners = useCapture ? capturingImplementationListeners
155
: bubblingImplementationListeners;
156
if (listeners == null) {
159
EventListenerList list = (EventListenerList) listeners.get(type);
163
list.removeListener(namespaceURI, listener);
164
if (list.size() == 0) {
165
listeners.remove(type);
170
* Moves all of the event listeners from this EventSupport object
171
* to the given EventSupport object.
173
* org.apache.batik.dom.AbstractDocument#renameNode(Node,String,String)}.
175
public void moveEventListeners(EventSupport other) {
176
super.moveEventListeners(other);
177
XBLEventSupport es = (XBLEventSupport) other;
178
es.capturingImplementationListeners = capturingImplementationListeners;
179
es.bubblingImplementationListeners = bubblingImplementationListeners;
180
capturingImplementationListeners = null;
181
bubblingImplementationListeners = null;
185
* This method allows the dispatch of events into the
186
* implementations event model. Events dispatched in this manner
187
* will have the same capturing and bubbling behavior as events
188
* dispatched directly by the implementation. The target of the
189
* event is the <code> EventTarget</code> on which
190
* <code>dispatchEvent</code> is called.
192
* @param target the target node
193
* @param evt Specifies the event type, behavior, and contextual
194
* information to be used in processing the event.
196
* @return The return value of <code>dispatchEvent</code>
197
* indicates whether any of the listeners which handled the event
198
* called <code>preventDefault</code>. If
199
* <code>preventDefault</code> was called the value is false, else
202
* @exception EventException
203
* UNSPECIFIED_EVENT_TYPE_ERR: Raised if the
204
* <code>Event</code>'s type was not specified by initializing
205
* the event before <code>dispatchEvent</code> was
206
* called. Specification of the <code>Event</code>'s type as
207
* <code>null</code> or an empty string will also trigger this
210
public boolean dispatchEvent(NodeEventTarget target, Event evt)
211
throws EventException {
212
// System.err.println("\t[] dispatching " + e.getType() + " on " + ((Node) target).getNodeName());
216
if (!(evt instanceof AbstractEvent)) {
217
throw createEventException
218
(DOMException.NOT_SUPPORTED_ERR,
222
AbstractEvent e = (AbstractEvent) evt;
223
String type = e.getType();
224
if (type == null || type.length() == 0) {
225
throw createEventException
226
(EventException.UNSPECIFIED_EVENT_TYPE_ERR,
231
setTarget(e, target);
232
stopPropagation(e, false);
233
stopImmediatePropagation(e, false);
234
preventDefault(e, false);
235
// dump the tree hierarchy from top to the target
236
NodeEventTarget[] ancestors = getAncestors(target);
237
int bubbleLimit = e.getBubbleLimit();
239
if (isSingleScopeEvent(e)) {
240
// DOM Mutation events are dispatched only within the
242
AbstractNode targetNode = (AbstractNode) target;
243
Node boundElement = targetNode.getXblBoundElement();
244
if (boundElement != null) {
245
minAncestor = ancestors.length;
246
while (minAncestor > 0) {
247
AbstractNode ancestorNode =
248
(AbstractNode) ancestors[minAncestor - 1];
249
if (ancestorNode.getXblBoundElement() != boundElement) {
255
} else if (bubbleLimit != 0) {
256
// Other events may have a bubble limit (such as UI events)
257
minAncestor = ancestors.length - bubbleLimit + 1;
258
if (minAncestor < 0) {
262
// System.err.println("\t== ancestors:");
263
// for (int i = 0; i < ancestors.length; i++) {
264
// if (i < minAncestor) {
265
// System.err.print("\t ");
267
// System.err.print("\t * ");
269
// System.err.println(((Node) ancestors[i]).getNodeName());
271
AbstractEvent[] es = getRetargettedEvents(target, ancestors, e);
272
boolean preventDefault = false;
273
// CAPTURING_PHASE : fire event listeners from top to EventTarget
274
HashSet stoppedGroups = new HashSet();
275
HashSet toBeStoppedGroups = new HashSet();
276
for (int i = 0; i < minAncestor; i++) {
277
NodeEventTarget node = ancestors[i];
278
// System.err.println("\t-- CAPTURING " + e.getType() + " " + ((Node) node).getNodeName());
279
setCurrentTarget(es[i], node);
280
setEventPhase(es[i], Event.CAPTURING_PHASE);
281
fireImplementationEventListeners(node, es[i], true);
283
for (int i = minAncestor; i < ancestors.length; i++) {
284
NodeEventTarget node = ancestors[i];
285
// System.err.println("\t-- * CAPTURING " + e.getType() + " " + ((Node) node).getNodeName());
286
setCurrentTarget(es[i], node);
287
setEventPhase(es[i], Event.CAPTURING_PHASE);
288
fireImplementationEventListeners(node, es[i], true);
289
fireEventListeners(node, es[i], true, stoppedGroups,
291
fireHandlerGroupEventListeners(node, es[i], true, stoppedGroups,
293
preventDefault = preventDefault || es[i].getDefaultPrevented();
294
stoppedGroups.addAll(toBeStoppedGroups);
295
toBeStoppedGroups.clear();
297
// AT_TARGET : fire local event listeners
298
// System.err.println("\t-- * AT_TARGET " + e.getType() + " " + ((Node) target).getNodeName());
299
setEventPhase(e, Event.AT_TARGET);
300
setCurrentTarget(e, target);
301
fireImplementationEventListeners(target, e, false);
302
fireEventListeners(target, e, false, stoppedGroups,
304
fireHandlerGroupEventListeners(node, e, false, stoppedGroups,
306
stoppedGroups.addAll(toBeStoppedGroups);
307
toBeStoppedGroups.clear();
308
preventDefault = preventDefault || e.getDefaultPrevented();
309
// BUBBLING_PHASE : fire event listeners from target to top
310
if (e.getBubbles()) {
311
for (int i = ancestors.length - 1; i >= minAncestor; i--) {
312
NodeEventTarget node = ancestors[i];
313
// System.err.println("\t-- * BUBBLING " + e.getType() + " " + ((Node) node).getNodeName());
314
setCurrentTarget(es[i], node);
315
setEventPhase(es[i], Event.BUBBLING_PHASE);
316
fireImplementationEventListeners(node, es[i], false);
317
fireEventListeners(node, es[i], false, stoppedGroups,
319
fireHandlerGroupEventListeners
320
(node, es[i], false, stoppedGroups, toBeStoppedGroups);
322
preventDefault || es[i].getDefaultPrevented();
323
stoppedGroups.addAll(toBeStoppedGroups);
324
toBeStoppedGroups.clear();
326
for (int i = minAncestor - 1; i >= 0; i--) {
327
NodeEventTarget node = ancestors[i];
328
// System.err.println("\t-- BUBBLING " + e.getType() + " " + ((Node) node).getNodeName());
329
setCurrentTarget(es[i], node);
330
setEventPhase(es[i], Event.BUBBLING_PHASE);
331
fireImplementationEventListeners(node, es[i], false);
333
preventDefault || es[i].getDefaultPrevented();
336
if (!preventDefault) {
337
runDefaultActions(e);
339
return preventDefault;
343
* Fires the event handlers registered on an XBL 'handlerGroup' element.
345
protected void fireHandlerGroupEventListeners(NodeEventTarget node,
348
HashSet stoppedGroups,
349
HashSet toBeStoppedGroups) {
350
// get the XBL definitions in effect for the event target
351
NodeList defs = ((NodeXBL) node).getXblDefinitions();
352
for (int j = 0; j < defs.getLength(); j++) {
353
// find the 'handlerGroup' element
354
Node n = defs.item(j).getFirstChild();
356
!(n instanceof XBLOMHandlerGroupElement)) {
357
n = n.getNextSibling();
362
node = (NodeEventTarget) n;
363
String type = e.getType();
364
EventSupport support = node.getEventSupport();
365
// check if the event support has been instantiated
366
if (support == null) {
369
EventListenerList list = support.getEventListeners(type, useCapture);
370
// check if the event listeners list is not empty
374
// dump event listeners, we get the registered listeners NOW
375
EventListenerList.Entry[] listeners = list.getEventListeners();
376
fireEventListeners(node, e, listeners, stoppedGroups,
382
* Returns whether the given event should be stopped once it crosses
383
* a shadow scope boundary.
385
protected boolean isSingleScopeEvent(Event evt) {
386
return evt instanceof MutationEvent
387
|| evt instanceof ShadowTreeEvent;
391
* Returns an array of Event objects to be used for each event target
392
* in the event flow. The Event objects are retargetted if an sXBL
393
* shadow scope is crossed and the event is not a DOM mutation event.
395
protected AbstractEvent[] getRetargettedEvents(NodeEventTarget target,
396
NodeEventTarget[] ancestors,
398
boolean singleScope = isSingleScopeEvent(e);
399
AbstractNode targetNode = (AbstractNode) target;
400
AbstractEvent[] es = new AbstractEvent[ancestors.length];
401
if (ancestors.length > 0) {
402
int index = ancestors.length - 1;
403
Node boundElement = targetNode.getXblBoundElement();
404
AbstractNode ancestorNode = (AbstractNode) ancestors[index];
406
ancestorNode.getXblBoundElement() != boundElement) {
407
es[index] = retargetEvent(e, ancestors[index]);
411
while (--index >= 0) {
412
ancestorNode = (AbstractNode) ancestors[index + 1];
413
boundElement = ancestorNode.getXblBoundElement();
414
AbstractNode nextAncestorNode = (AbstractNode) ancestors[index];
415
Node nextBoundElement = nextAncestorNode.getXblBoundElement();
416
if (!singleScope && nextBoundElement != boundElement) {
417
es[index] = retargetEvent(es[index + 1], ancestors[index]);
419
es[index] = es[index + 1];
427
* Clones and retargets the given event.
429
protected AbstractEvent retargetEvent(AbstractEvent e,
430
NodeEventTarget target) {
431
AbstractEvent clonedEvent = e.cloneEvent();
432
setTarget(clonedEvent, target);
437
* Returns the implementation listneers.
439
public EventListenerList getImplementationEventListeners
440
(String type, boolean useCapture) {
441
HashTable listeners = useCapture ? capturingImplementationListeners
442
: bubblingImplementationListeners;
443
if (listeners == null) {
446
return (EventListenerList) listeners.get(type);
450
* Fires the registered implementation listeners on the given event
453
protected void fireImplementationEventListeners(NodeEventTarget node,
455
boolean useCapture) {
456
String type = e.getType();
457
XBLEventSupport support = (XBLEventSupport) node.getEventSupport();
458
// check if the event support has been instantiated
459
if (support == null) {
462
EventListenerList list =
463
support.getImplementationEventListeners(type, useCapture);
464
// check if the event listeners list is not empty
468
// dump event listeners, we get the registered listeners NOW
469
EventListenerList.Entry[] listeners = list.getEventListeners();
470
fireEventListeners(node, e, listeners, null, null);