001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.bridge.svg12;
020:
021: import java.util.Iterator;
022:
023: import org.apache.batik.bridge.BridgeContext;
024: import org.apache.batik.bridge.BridgeUpdateHandler;
025: import org.apache.batik.bridge.DocumentLoader;
026: import org.apache.batik.bridge.ScriptingEnvironment;
027: import org.apache.batik.bridge.URIResolver;
028: import org.apache.batik.bridge.UserAgent;
029: import org.apache.batik.css.engine.CSSEngine;
030: import org.apache.batik.dom.AbstractDocument;
031: import org.apache.batik.dom.AbstractNode;
032: import org.apache.batik.dom.events.EventSupport;
033: import org.apache.batik.dom.events.NodeEventTarget;
034: import org.apache.batik.dom.svg.SVGOMDocument;
035: import org.apache.batik.dom.svg12.XBLEventSupport;
036: import org.apache.batik.dom.svg12.XBLOMShadowTreeElement;
037: import org.apache.batik.dom.xbl.NodeXBL;
038: import org.apache.batik.dom.xbl.XBLManager;
039: import org.apache.batik.script.Interpreter;
040: import org.apache.batik.script.InterpreterPool;
041: import org.apache.batik.util.SVGConstants;
042: import org.apache.batik.util.XMLConstants;
043:
044: import org.w3c.dom.Document;
045: import org.w3c.dom.Element;
046: import org.w3c.dom.Node;
047: import org.w3c.dom.events.Event;
048: import org.w3c.dom.events.EventListener;
049: import org.w3c.dom.events.EventTarget;
050: import org.w3c.dom.svg.SVGDocument;
051:
052: /**
053: * Bridge context for SVG 1.2 documents. This is primarily for dispatching
054: * XBL events to bridges and for handling resource documents.
055: *
056: * @author <a href="mailto:cam%40mcc%2eid%2eau">Cameron McCormack</a>
057: * @version $Id: SVG12BridgeContext.java 502489 2007-02-02 04:57:56Z cam $
058: */
059: public class SVG12BridgeContext extends BridgeContext {
060:
061: /**
062: * The BindingListener for XBL binding events.
063: */
064: protected XBLBindingListener bindingListener;
065:
066: /**
067: * The ContentSelectionChangedListener for xbl:content element events.
068: */
069: protected XBLContentListener contentListener;
070:
071: /**
072: * The EventTarget that has the mouse capture.
073: */
074: protected EventTarget mouseCaptureTarget;
075:
076: /**
077: * Whether the mouse capture event target will receive events
078: * that do not intersect with its geometry.
079: */
080: protected boolean mouseCaptureSendAll;
081:
082: /**
083: * Whether the mouse capture will be released on mouse up.
084: */
085: protected boolean mouseCaptureAutoRelease;
086:
087: /**
088: * Constructs a new bridge context.
089: * @param userAgent the user agent
090: */
091: public SVG12BridgeContext(UserAgent userAgent) {
092: super (userAgent);
093: }
094:
095: /**
096: * Constructs a new bridge context.
097: * @param userAgent the user agent
098: * @param loader document loader
099: */
100: public SVG12BridgeContext(UserAgent userAgent, DocumentLoader loader) {
101: super (userAgent, loader);
102: }
103:
104: /**
105: * Constructs a new bridge context.
106: * @param userAgent the user agent
107: * @param interpreterPool the interpreter pool
108: * @param documentLoader document loader
109: */
110: public SVG12BridgeContext(UserAgent userAgent,
111: InterpreterPool interpreterPool,
112: DocumentLoader documentLoader) {
113: super (userAgent, interpreterPool, documentLoader);
114: }
115:
116: /**
117: * Returns a new URIResolver object.
118: */
119: public URIResolver createURIResolver(SVGDocument doc,
120: DocumentLoader dl) {
121: return new SVG12URIResolver(doc, dl);
122: }
123:
124: /**
125: * Adds the GVT listener for AWT event support.
126: */
127: public void addGVTListener(Document doc) {
128: SVG12BridgeEventSupport.addGVTListener(this , doc);
129: }
130:
131: /**
132: * Disposes this BridgeContext.
133: */
134: public void dispose() {
135: clearChildContexts();
136:
137: synchronized (eventListenerSet) {
138: // remove all listeners added by Bridges
139: Iterator iter = eventListenerSet.iterator();
140: while (iter.hasNext()) {
141: EventListenerMememto m = (EventListenerMememto) iter
142: .next();
143: NodeEventTarget et = m.getTarget();
144: EventListener el = m.getListener();
145: boolean uc = m.getUseCapture();
146: String t = m.getEventType();
147: boolean in = m.getNamespaced();
148: if (et == null || el == null || t == null) {
149: continue;
150: }
151: if (m instanceof ImplementationEventListenerMememto) {
152: String ns = m.getNamespaceURI();
153: Node nde = (Node) et;
154: AbstractNode n = (AbstractNode) nde
155: .getOwnerDocument();
156: if (n != null) {
157: XBLEventSupport es;
158: es = (XBLEventSupport) n
159: .initializeEventSupport();
160: es.removeImplementationEventListenerNS(ns, t,
161: el, uc);
162: }
163: } else if (in) {
164: String ns = m.getNamespaceURI();
165: et.removeEventListenerNS(ns, t, el, uc);
166: } else {
167: et.removeEventListener(t, el, uc);
168: }
169: }
170: }
171:
172: if (document != null) {
173: removeDOMListeners();
174: removeBindingListener();
175: }
176:
177: if (animationEngine != null) {
178: animationEngine.dispose();
179: animationEngine = null;
180: }
181:
182: Iterator iter = interpreterMap.values().iterator();
183: while (iter.hasNext()) {
184: Interpreter interpreter = (Interpreter) iter.next();
185: if (interpreter != null)
186: interpreter.dispose();
187: }
188: interpreterMap.clear();
189:
190: if (focusManager != null) {
191: focusManager.dispose();
192: }
193: }
194:
195: /**
196: * Adds a BindingListener to the XBLManager for the document, so that
197: * XBL binding events can be passed on to the BridgeUpdateHandlers.
198: */
199: public void addBindingListener() {
200: AbstractDocument doc = (AbstractDocument) document;
201: DefaultXBLManager xm = (DefaultXBLManager) doc.getXBLManager();
202: if (xm != null) {
203: bindingListener = new XBLBindingListener();
204: xm.addBindingListener(bindingListener);
205: contentListener = new XBLContentListener();
206: xm.addContentSelectionChangedListener(contentListener);
207: }
208: }
209:
210: /**
211: * Removes the BindingListener from the XBLManager.
212: */
213: public void removeBindingListener() {
214: AbstractDocument doc = (AbstractDocument) document;
215: XBLManager xm = doc.getXBLManager();
216: if (xm instanceof DefaultXBLManager) {
217: DefaultXBLManager dxm = (DefaultXBLManager) xm;
218: dxm.removeBindingListener(bindingListener);
219: dxm.removeContentSelectionChangedListener(contentListener);
220: }
221: }
222:
223: /**
224: * Adds EventListeners to the DOM and CSSEngineListener to the
225: * CSSEngine to handle any modifications on the DOM tree or style
226: * properties and update the GVT tree in response. This overriden
227: * method adds implementation event listeners, so that mutations in
228: * shadow trees can be caught.
229: */
230: public void addDOMListeners() {
231: SVGOMDocument doc = (SVGOMDocument) document;
232: XBLEventSupport evtSupport = (XBLEventSupport) doc
233: .initializeEventSupport();
234:
235: domAttrModifiedEventListener = new EventListenerWrapper(
236: new DOMAttrModifiedEventListener());
237: evtSupport.addImplementationEventListenerNS(
238: XMLConstants.XML_EVENTS_NAMESPACE_URI,
239: "DOMAttrModified", domAttrModifiedEventListener, true);
240:
241: domNodeInsertedEventListener = new EventListenerWrapper(
242: new DOMNodeInsertedEventListener());
243: evtSupport.addImplementationEventListenerNS(
244: XMLConstants.XML_EVENTS_NAMESPACE_URI,
245: "DOMNodeInserted", domNodeInsertedEventListener, true);
246:
247: domNodeRemovedEventListener = new EventListenerWrapper(
248: new DOMNodeRemovedEventListener());
249: evtSupport.addImplementationEventListenerNS(
250: XMLConstants.XML_EVENTS_NAMESPACE_URI,
251: "DOMNodeRemoved", domNodeRemovedEventListener, true);
252:
253: domCharacterDataModifiedEventListener = new EventListenerWrapper(
254: new DOMCharacterDataModifiedEventListener());
255: evtSupport.addImplementationEventListenerNS(
256: XMLConstants.XML_EVENTS_NAMESPACE_URI,
257: "DOMCharacterDataModified",
258: domCharacterDataModifiedEventListener, true);
259:
260: animatedAttributeListener = new AnimatedAttrListener();
261: doc.addAnimatedAttributeListener(animatedAttributeListener);
262:
263: focusManager = new SVG12FocusManager(document);
264:
265: CSSEngine cssEngine = doc.getCSSEngine();
266: cssPropertiesChangedListener = new CSSPropertiesChangedListener();
267: cssEngine.addCSSEngineListener(cssPropertiesChangedListener);
268: }
269:
270: /**
271: * Adds EventListeners to the input document to handle the cursor
272: * property.
273: * This is not done in the addDOMListeners method because
274: * addDOMListeners is only used for dynamic content whereas
275: * cursor support is provided for all content.
276: * Also note that it is very important that the listeners be
277: * registered for the capture phase as the 'default' behavior
278: * for cursors is handled by the BridgeContext during the
279: * capture phase and the 'custom' behavior (handling of 'auto'
280: * on anchors, for example), is handled during the bubbling phase.
281: */
282: public void addUIEventListeners(Document doc) {
283: EventTarget evtTarget = (EventTarget) doc.getDocumentElement();
284: AbstractNode n = (AbstractNode) evtTarget;
285: XBLEventSupport evtSupport = (XBLEventSupport) n
286: .initializeEventSupport();
287:
288: EventListener domMouseOverListener = new EventListenerWrapper(
289: new DOMMouseOverEventListener());
290: evtSupport.addImplementationEventListenerNS(
291: XMLConstants.XML_EVENTS_NAMESPACE_URI,
292: SVGConstants.SVG_EVENT_MOUSEOVER, domMouseOverListener,
293: true);
294: storeImplementationEventListenerNS(evtTarget,
295: XMLConstants.XML_EVENTS_NAMESPACE_URI,
296: SVGConstants.SVG_EVENT_MOUSEOVER, domMouseOverListener,
297: true);
298:
299: EventListener domMouseOutListener = new EventListenerWrapper(
300: new DOMMouseOutEventListener());
301: evtSupport.addImplementationEventListenerNS(
302: XMLConstants.XML_EVENTS_NAMESPACE_URI,
303: SVGConstants.SVG_EVENT_MOUSEOUT, domMouseOutListener,
304: true);
305: storeImplementationEventListenerNS(evtTarget,
306: XMLConstants.XML_EVENTS_NAMESPACE_URI,
307: SVGConstants.SVG_EVENT_MOUSEOUT, domMouseOutListener,
308: true);
309: }
310:
311: public void removeUIEventListeners(Document doc) {
312: EventTarget evtTarget = (EventTarget) doc.getDocumentElement();
313: AbstractNode n = (AbstractNode) evtTarget;
314: XBLEventSupport es = (XBLEventSupport) n
315: .initializeEventSupport();
316:
317: synchronized (eventListenerSet) {
318: Iterator i = eventListenerSet.iterator();
319: while (i.hasNext()) {
320: EventListenerMememto elm = (EventListenerMememto) i
321: .next();
322: NodeEventTarget et = elm.getTarget();
323: if (et == evtTarget) {
324: EventListener el = elm.getListener();
325: boolean uc = elm.getUseCapture();
326: String t = elm.getEventType();
327: boolean in = elm.getNamespaced();
328: if (et == null || el == null || t == null) {
329: continue;
330: }
331: if (elm instanceof ImplementationEventListenerMememto) {
332: String ns = elm.getNamespaceURI();
333: es.removeImplementationEventListenerNS(ns, t,
334: el, uc);
335: } else if (in) {
336: String ns = elm.getNamespaceURI();
337: et.removeEventListenerNS(ns, t, el, uc);
338: } else {
339: et.removeEventListener(t, el, uc);
340: }
341: }
342: }
343: }
344: }
345:
346: /**
347: * Removes event listeners from the DOM and CSS engine.
348: */
349: protected void removeDOMListeners() {
350: SVGOMDocument doc = (SVGOMDocument) document;
351:
352: doc.removeEventListenerNS(
353: XMLConstants.XML_EVENTS_NAMESPACE_URI,
354: "DOMAttrModified", domAttrModifiedEventListener, true);
355: doc.removeEventListenerNS(
356: XMLConstants.XML_EVENTS_NAMESPACE_URI,
357: "DOMNodeInserted", domNodeInsertedEventListener, true);
358: doc.removeEventListenerNS(
359: XMLConstants.XML_EVENTS_NAMESPACE_URI,
360: "DOMNodeRemoved", domNodeRemovedEventListener, true);
361: doc.removeEventListenerNS(
362: XMLConstants.XML_EVENTS_NAMESPACE_URI,
363: "DOMCharacterDataModified",
364: domCharacterDataModifiedEventListener, true);
365:
366: doc.removeAnimatedAttributeListener(animatedAttributeListener);
367:
368: CSSEngine cssEngine = doc.getCSSEngine();
369: if (cssEngine != null) {
370: cssEngine
371: .removeCSSEngineListener(cssPropertiesChangedListener);
372: cssEngine.dispose();
373: doc.setCSSEngine(null);
374: }
375: }
376:
377: /**
378: * Adds to the eventListenerSet the specified implementation event
379: * listener registration.
380: */
381: protected void storeImplementationEventListenerNS(EventTarget t,
382: String ns, String s, EventListener l, boolean b) {
383: synchronized (eventListenerSet) {
384: ImplementationEventListenerMememto m = new ImplementationEventListenerMememto(
385: t, ns, s, l, b, this );
386: eventListenerSet.add(m);
387: }
388: }
389:
390: public BridgeContext createSubBridgeContext(SVGOMDocument newDoc) {
391: CSSEngine eng = newDoc.getCSSEngine();
392: if (eng != null) {
393: return (BridgeContext) newDoc.getCSSEngine()
394: .getCSSContext();
395: }
396:
397: BridgeContext subCtx = super .createSubBridgeContext(newDoc);
398: if (isDynamic() && subCtx.isDynamic()) {
399: setUpdateManager(subCtx, updateManager);
400: if (updateManager != null) {
401: ScriptingEnvironment se;
402: if (newDoc.isSVG12()) {
403: se = new SVG12ScriptingEnvironment(subCtx);
404: } else {
405: se = new ScriptingEnvironment(subCtx);
406: }
407: se.loadScripts();
408: se.dispatchSVGLoadEvent();
409: if (newDoc.isSVG12()) {
410: DefaultXBLManager xm = new DefaultXBLManager(
411: newDoc, subCtx);
412: setXBLManager(subCtx, xm);
413: newDoc.setXBLManager(xm);
414: xm.startProcessing();
415: }
416: }
417: }
418: return subCtx;
419: }
420:
421: /**
422: * Starts mouse capture.
423: */
424: public void startMouseCapture(EventTarget target, boolean sendAll,
425: boolean autoRelease) {
426: mouseCaptureTarget = target;
427: mouseCaptureSendAll = sendAll;
428: mouseCaptureAutoRelease = autoRelease;
429: }
430:
431: /**
432: * Stops mouse capture.
433: */
434: public void stopMouseCapture() {
435: mouseCaptureTarget = null;
436: }
437:
438: /**
439: * A class used to store an implementation EventListener added to the DOM.
440: */
441: protected static class ImplementationEventListenerMememto extends
442: EventListenerMememto {
443:
444: /**
445: * Creates a new ImplementationEventListenerMememto.
446: */
447: public ImplementationEventListenerMememto(EventTarget t,
448: String s, EventListener l, boolean b, BridgeContext c) {
449: super (t, s, l, b, c);
450: }
451:
452: /**
453: * Creates a new ImplementationEventListenerMememto.
454: */
455: public ImplementationEventListenerMememto(EventTarget t,
456: String n, String s, EventListener l, boolean b,
457: BridgeContext c) {
458: super (t, n, s, l, b, c);
459: }
460: }
461:
462: /**
463: * Wrapper for DOM event listeners so that they will see only
464: * original events (i.e., not retargetted).
465: */
466: protected class EventListenerWrapper implements EventListener {
467:
468: /**
469: * The wrapped listener.
470: */
471: protected EventListener listener;
472:
473: /**
474: * Creates a new EventListenerWrapper.
475: */
476: public EventListenerWrapper(EventListener l) {
477: listener = l;
478: }
479:
480: /**
481: * Handles the event.
482: */
483: public void handleEvent(Event evt) {
484: listener.handleEvent(EventSupport
485: .getUltimateOriginalEvent(evt));
486: }
487:
488: /**
489: * String representation of this listener wrapper.
490: */
491: public String toString() {
492: return super .toString() + " [wrapping "
493: + listener.toString() + "]";
494: }
495: }
496:
497: /**
498: * The BindingListener.
499: */
500: protected class XBLBindingListener implements BindingListener {
501:
502: /**
503: * Invoked when the specified bindable element's binding has changed.
504: */
505: public void bindingChanged(Element bindableElement,
506: Element shadowTree) {
507: BridgeUpdateHandler h = getBridgeUpdateHandler(bindableElement);
508: if (h instanceof SVG12BridgeUpdateHandler) {
509: SVG12BridgeUpdateHandler h12 = (SVG12BridgeUpdateHandler) h;
510: try {
511: h12.handleBindingEvent(bindableElement, shadowTree);
512: } catch (Exception e) {
513: userAgent.displayError(e);
514: }
515: }
516: }
517: }
518:
519: /**
520: * The ContentSelectionChangedListener.
521: */
522: protected class XBLContentListener implements
523: ContentSelectionChangedListener {
524:
525: /**
526: * Invoked after an xbl:content element has updated its selected
527: * nodes list.
528: * @param csce the ContentSelectionChangedEvent object
529: */
530: public void contentSelectionChanged(
531: ContentSelectionChangedEvent csce) {
532: Element e = (Element) csce.getContentElement()
533: .getParentNode();
534: if (e instanceof XBLOMShadowTreeElement) {
535: e = ((NodeXBL) e).getXblBoundElement();
536: }
537: BridgeUpdateHandler h = getBridgeUpdateHandler(e);
538: if (h instanceof SVG12BridgeUpdateHandler) {
539: SVG12BridgeUpdateHandler h12 = (SVG12BridgeUpdateHandler) h;
540: try {
541: h12.handleContentSelectionChangedEvent(csce);
542: } catch (Exception ex) {
543: userAgent.displayError(ex);
544: }
545: }
546: }
547: }
548: }
|