KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > bridge > SVGUseElementBridge


1 /*
2
3    Copyright 2001-2004 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.bridge;
19
20 import java.awt.Cursor JavaDoc;
21 import java.awt.RenderingHints JavaDoc;
22 import java.awt.geom.AffineTransform JavaDoc;
23 import java.awt.geom.Rectangle2D JavaDoc;
24
25 import org.apache.batik.css.engine.CSSEngine;
26 import org.apache.batik.dom.svg.SVGOMCSSImportedElementRoot;
27 import org.apache.batik.dom.svg.SVGOMDocument;
28 import org.apache.batik.dom.svg.SVGOMUseElement;
29 import org.apache.batik.dom.util.XLinkSupport;
30 import org.apache.batik.gvt.CompositeGraphicsNode;
31 import org.apache.batik.gvt.GraphicsNode;
32 import org.w3c.dom.Attr JavaDoc;
33 import org.w3c.dom.Element JavaDoc;
34 import org.w3c.dom.NamedNodeMap JavaDoc;
35 import org.w3c.dom.Node JavaDoc;
36 import org.w3c.dom.events.Event JavaDoc;
37 import org.w3c.dom.events.EventListener JavaDoc;
38 import org.w3c.dom.events.EventTarget JavaDoc;
39 import org.w3c.dom.events.MutationEvent JavaDoc;
40
41 /**
42  * Bridge class for the <use> element.
43  *
44  * @author <a HREF="mailto:tkormann@apache.org">Thierry Kormann</a>
45  * @version $Id: SVGUseElementBridge.java,v 1.47 2005/03/03 01:19:53 deweese Exp $
46  */

47 public class SVGUseElementBridge extends AbstractGraphicsNodeBridge {
48     /*
49      * Used to handle mutation of the referenced content. This is
50      * only used in dynamic context and only for reference to local
51      * content.
52      */

53     protected ReferencedElementMutationListener l;
54
55     protected BridgeContext subCtx=null;
56
57     /**
58      * Constructs a new bridge for the &lt;use> element.
59      */

60     public SVGUseElementBridge() {}
61
62     /**
63      * Returns 'use'.
64      */

65     public String JavaDoc getLocalName() {
66         return SVG_USE_TAG;
67     }
68
69     /**
70      * Returns a new instance of this bridge.
71      */

72     public Bridge getInstance(){
73         return new SVGUseElementBridge();
74     }
75
76     /**
77      * Creates a <tt>GraphicsNode</tt> according to the specified parameters.
78      *
79      * @param ctx the bridge context to use
80      * @param e the element that describes the graphics node to build
81      * @return a graphics node that represents the specified element
82      */

83     public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
84         // 'requiredFeatures', 'requiredExtensions' and 'systemLanguage'
85
if (!SVGUtilities.matchUserAgent(e, ctx.getUserAgent()))
86             return null;
87
88         CompositeGraphicsNode gn = buildCompositeGraphicsNode(ctx, e, null);
89
90         return gn;
91     }
92
93     /**
94      * Creates a <tt>GraphicsNode</tt> from the input element and
95      * populates the input <tt>CompositeGraphicsNode</tt>
96      *
97      * @param ctx the bridge context to use
98      * @param e the element that describes the graphics node to build
99      * @param gn the CompositeGraphicsNode where the use graphical
100      * content will be appended. The composite node is emptied
101      * before appending new content.
102      */

103     public CompositeGraphicsNode buildCompositeGraphicsNode
104         (BridgeContext ctx, Element e,
105          CompositeGraphicsNode gn) {
106         // get the referenced element
107
String JavaDoc uri = XLinkSupport.getXLinkHref(e);
108         if (uri.length() == 0) {
109             throw new BridgeException(e, ERR_ATTRIBUTE_MISSING,
110                                       new Object JavaDoc[] {"xlink:href"});
111         }
112
113         Element refElement = ctx.getReferencedElement(e, uri);
114
115         SVGOMDocument document
116             = (SVGOMDocument)e.getOwnerDocument();
117         SVGOMDocument refDocument
118             = (SVGOMDocument)refElement.getOwnerDocument();
119         boolean isLocal = (refDocument == document);
120
121         BridgeContext theCtx = ctx;
122         subCtx = null;
123         if (!isLocal) {
124             CSSEngine eng = refDocument.getCSSEngine();
125             subCtx = (BridgeContext)refDocument.getCSSEngine().getCSSContext();
126             theCtx = subCtx;
127         }
128             
129         // import or clone the referenced element in current document
130
Element localRefElement =
131             (Element)document.importNode(refElement, true, true);
132
133         if (SVG_SYMBOL_TAG.equals(localRefElement.getLocalName())) {
134             // The referenced 'symbol' and its contents are deep-cloned into
135
// the generated tree, with the exception that the 'symbol' is
136
// replaced by an 'svg'.
137
Element svgElement
138                 = document.createElementNS(SVG_NAMESPACE_URI, SVG_SVG_TAG);
139             // move the attributes from <symbol> to the <svg> element
140
NamedNodeMap JavaDoc attrs = localRefElement.getAttributes();
141             int len = attrs.getLength();
142             for (int i = 0; i < len; i++) {
143                 Attr JavaDoc attr = (Attr JavaDoc)attrs.item(i);
144                 svgElement.setAttributeNS(attr.getNamespaceURI(),
145                                           attr.getName(),
146                                           attr.getValue());
147             }
148             // move the children from <symbol> to the <svg> element
149
for (Node n = localRefElement.getFirstChild();
150                  n != null;
151                  n = localRefElement.getFirstChild()) {
152                 svgElement.appendChild(n);
153             }
154             localRefElement = svgElement;
155         }
156
157         if (SVG_SVG_TAG.equals(localRefElement.getLocalName())) {
158             // The referenced 'svg' and its contents are deep-cloned into the
159
// generated tree. If attributes width and/or height are provided
160
// on the 'use' element, then these values will override the
161
// corresponding attributes on the 'svg' in the generated tree.
162
String JavaDoc wStr = e.getAttributeNS(null, SVG_WIDTH_ATTRIBUTE);
163             if (wStr.length() != 0) {
164                 localRefElement.setAttributeNS
165                     (null, SVG_WIDTH_ATTRIBUTE, wStr);
166             }
167             String JavaDoc hStr = e.getAttributeNS(null, SVG_HEIGHT_ATTRIBUTE);
168             if (hStr.length() != 0) {
169                 localRefElement.setAttributeNS
170                     (null, SVG_HEIGHT_ATTRIBUTE, hStr);
171             }
172         }
173
174         // attach the referenced element to the current document
175
SVGOMCSSImportedElementRoot root;
176         root = new SVGOMCSSImportedElementRoot(document, e, isLocal);
177         root.appendChild(localRefElement);
178
179         if (gn == null) {
180             gn = new CompositeGraphicsNode();
181         } else {
182             int s = gn.size();
183             for (int i=0; i<s; i++)
184                 gn.remove(0);
185         }
186
187         SVGOMUseElement ue = (SVGOMUseElement)e;
188         Node oldRoot = ue.getCSSImportedElementRoot();
189         if (oldRoot != null) {
190             disposeTree(oldRoot);
191         }
192         ue.setCSSImportedElementRoot(root);
193
194         Element g = localRefElement;
195
196         // compute URIs and style sheets for the used element
197
CSSUtilities.computeStyleAndURIs(refElement, localRefElement, uri);
198
199         GVTBuilder builder = ctx.getGVTBuilder();
200         GraphicsNode refNode = builder.build(ctx, g);
201
202         ///////////////////////////////////////////////////////////////////////
203

204         gn.getChildren().add(refNode);
205
206         gn.setTransform(computeTransform(e, ctx));
207
208         // set an affine transform to take into account the (x, y)
209
// coordinates of the <use> element
210

211         // 'visibility'
212
gn.setVisible(CSSUtilities.convertVisibility(e));
213
214         RenderingHints JavaDoc hints = null;
215         hints = CSSUtilities.convertColorRendering(e, hints);
216         if (hints != null)
217             gn.setRenderingHints(hints);
218
219         // 'enable-background'
220
Rectangle2D JavaDoc r = CSSUtilities.convertEnableBackground(e);
221         if (r != null)
222             gn.setBackgroundEnable(r);
223
224         if (l != null) {
225             // Remove event listeners
226
EventTarget JavaDoc target = l.target;
227             target.removeEventListener("DOMAttrModified", l, true);
228             target.removeEventListener("DOMNodeInserted", l, true);
229             target.removeEventListener("DOMNodeRemoved", l, true);
230             target.removeEventListener("DOMCharacterDataModified",l, true);
231             l = null;
232         }
233
234         ///////////////////////////////////////////////////////////////////////
235

236         // Handle mutations on content referenced in the same file if
237
// we are in a dynamic context.
238
if (isLocal && ctx.isDynamic()) {
239             l = new ReferencedElementMutationListener();
240         
241             EventTarget JavaDoc target = (EventTarget JavaDoc)refElement;
242             l.target = target;
243             
244             target.addEventListener("DOMAttrModified", l, true);
245             theCtx.storeEventListener(target, "DOMAttrModified", l, true);
246             
247             target.addEventListener("DOMNodeInserted", l, true);
248             theCtx.storeEventListener(target, "DOMNodeInserted", l, true);
249             
250             target.addEventListener("DOMNodeRemoved", l, true);
251             theCtx.storeEventListener(target, "DOMNodeRemoved", l, true);
252             
253             target.addEventListener("DOMCharacterDataModified", l, true);
254             theCtx.storeEventListener
255                 (target, "DOMCharacterDataModified", l, true);
256         }
257         
258         return gn;
259     }
260
261     public void dispose() {
262         if (l != null) {
263             // Remove event listeners
264
EventTarget JavaDoc target = l.target;
265             target.removeEventListener("DOMAttrModified", l, true);
266             target.removeEventListener("DOMNodeInserted", l, true);
267             target.removeEventListener("DOMNodeRemoved", l, true);
268             target.removeEventListener("DOMCharacterDataModified",l, true);
269             l = null;
270         }
271
272         SVGOMUseElement ue = (SVGOMUseElement)e;
273         if ((ue != null) && (ue.getCSSImportedElementRoot() != null)) {
274             disposeTree(ue.getCSSImportedElementRoot());
275         }
276
277         super.dispose();
278
279         subCtx = null;
280     }
281
282     /**
283      * Computes the AffineTransform for the node
284      */

285     protected AffineTransform JavaDoc computeTransform(Element e, BridgeContext ctx) {
286         UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e);
287
288         // 'x' attribute - default is 0
289
float x = 0;
290         String JavaDoc s = e.getAttributeNS(null, SVG_X_ATTRIBUTE);
291         if (s.length() != 0) {
292             x = UnitProcessor.svgHorizontalCoordinateToUserSpace
293                 (s, SVG_X_ATTRIBUTE, uctx);
294         }
295
296         // 'y' attribute - default is 0
297
float y = 0;
298         s = e.getAttributeNS(null, SVG_Y_ATTRIBUTE);
299         if (s.length() != 0) {
300             y = UnitProcessor.svgVerticalCoordinateToUserSpace
301                 (s, SVG_Y_ATTRIBUTE, uctx);
302         }
303
304         // set an affine transform to take into account the (x, y)
305
// coordinates of the <use> element
306
s = e.getAttributeNS(null, SVG_TRANSFORM_ATTRIBUTE);
307         AffineTransform JavaDoc at = AffineTransform.getTranslateInstance(x, y);
308
309         // 'transform'
310
if (s.length() != 0) {
311             at.preConcatenate
312                 (SVGUtilities.convertTransform(e, SVG_TRANSFORM_ATTRIBUTE, s));
313         }
314
315         return at;
316      }
317
318     /**
319      * Creates the GraphicsNode depending on the GraphicsNodeBridge
320      * implementation.
321      */

322     protected GraphicsNode instantiateGraphicsNode() {
323         return null; // nothing to do, createGraphicsNode is fully overriden
324
}
325
326     /**
327      * Returns false as the &lt;use> element is a not container.
328      */

329     public boolean isComposite() {
330         return false;
331     }
332
333     /**
334      * Builds using the specified BridgeContext and element, the
335      * specified graphics node.
336      *
337      * @param ctx the bridge context to use
338      * @param e the element that describes the graphics node to build
339      * @param node the graphics node to build
340      */

341     public void buildGraphicsNode(BridgeContext ctx,
342                                   Element e,
343                                   GraphicsNode node) {
344
345         super.buildGraphicsNode(ctx, e, node);
346
347         if (ctx.isInteractive()) {
348             EventTarget JavaDoc target = (EventTarget JavaDoc)e;
349             EventListener JavaDoc l = new CursorMouseOverListener(ctx);
350             target.addEventListener(SVG_EVENT_MOUSEOVER, l, false);
351             ctx.storeEventListener(target, SVG_EVENT_MOUSEOVER, l, false);
352         }
353     }
354
355     /**
356      * To handle a mouseover on an anchor and set the cursor.
357      */

358     public static class CursorMouseOverListener implements EventListener JavaDoc {
359
360         protected BridgeContext ctx;
361         public CursorMouseOverListener(BridgeContext ctx) {
362             this.ctx = ctx;
363         }
364
365         public void handleEvent(Event JavaDoc evt) {
366             //
367
// Only modify the cursor if the current target's (i.e., the <use>) cursor
368
// property is *not* 'auto'.
369
//
370
Element currentTarget = (Element)evt.getCurrentTarget();
371
372             if (!CSSUtilities.isAutoCursor(currentTarget)) {
373                 Cursor JavaDoc cursor;
374                 cursor = CSSUtilities.convertCursor(currentTarget, ctx);
375                 if (cursor != null) {
376                     ctx.getUserAgent().setSVGCursor(cursor);
377                 }
378             }
379         }
380     }
381
382     /**
383      * Used to handle modifications to the referenced content
384      */

385     public class ReferencedElementMutationListener implements EventListener JavaDoc {
386         EventTarget JavaDoc target;
387
388         public void handleEvent(Event JavaDoc evt) {
389             // We got a mutation in the referenced content. We need to
390
// build the content again, just in case.
391
// Note that this is way sub-optimal, because multiple changes
392
// to the referenced content will cause multiple updates to the
393
// referencing <use>. However, this provides the desired behavior
394
buildCompositeGraphicsNode(ctx, e, (CompositeGraphicsNode)node);
395         }
396     }
397
398     // BridgeUpdateHandler implementation //////////////////////////////////
399

400     /**
401      * Invoked when an MutationEvent of type 'DOMAttrModified' is fired.
402      */

403     public void handleDOMAttrModifiedEvent(MutationEvent JavaDoc evt) {
404         String JavaDoc attrName = evt.getAttrName();
405         Node evtNode = evt.getRelatedNode();
406
407         if ((evtNode.getNamespaceURI() == null) &&
408             (attrName.equals(SVG_X_ATTRIBUTE) ||
409              attrName.equals(SVG_Y_ATTRIBUTE) ||
410              attrName.equals(SVG_TRANSFORM_ATTRIBUTE))) {
411             node.setTransform(computeTransform(e, ctx));
412             handleGeometryChanged();
413         } else if (((evtNode.getNamespaceURI() == null) &&
414                    (attrName.equals(SVG_WIDTH_ATTRIBUTE) ||
415                     attrName.equals(SVG_HEIGHT_ATTRIBUTE))) ||
416                    (( XLinkSupport.XLINK_NAMESPACE_URI.equals
417                      (evtNode.getNamespaceURI()) ) &&
418                     SVG_HREF_ATTRIBUTE.equals(evtNode.getLocalName()))) {
419             buildCompositeGraphicsNode(ctx, e, (CompositeGraphicsNode)node);
420         }
421     }
422 }
423
Popular Tags