Source code: org/apache/batik/bridge/BaseScriptingEnvironment.java
1 /*
2
3 Copyright 2002-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.io.IOException;
21 import java.io.InputStreamReader;
22 import java.io.Reader;
23 import java.io.StringReader;
24 import java.net.MalformedURLException;
25 import java.net.URL;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.jar.Manifest;
31
32 import org.apache.batik.dom.svg.XMLBaseSupport;
33 import org.apache.batik.dom.util.XLinkSupport;
34 import org.apache.batik.script.Interpreter;
35 import org.apache.batik.script.InterpreterException;
36 import org.apache.batik.script.ScriptHandler;
37 import org.apache.batik.util.ParsedURL;
38 import org.apache.batik.util.SVGConstants;
39 import org.w3c.dom.Document;
40 import org.w3c.dom.Element;
41 import org.w3c.dom.Node;
42 import org.w3c.dom.NodeList;
43 import org.w3c.dom.events.DocumentEvent;
44 import org.w3c.dom.events.Event;
45 import org.w3c.dom.events.EventListener;
46 import org.w3c.dom.events.EventTarget;
47 import org.w3c.dom.svg.SVGDocument;
48 import org.w3c.dom.svg.SVGSVGElement;
49 import org.w3c.dom.svg.EventListenerInitializer;
50
51 /**
52 * This class is the base class for SVG scripting.
53 *
54 * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
55 * @version $Id: BaseScriptingEnvironment.java,v 1.35 2005/03/27 08:58:30 cam Exp $
56 */
57 public class BaseScriptingEnvironment {
58 /**
59 * Constant used to describe inline scripts.
60 * <pre>
61 * {0} - URL of document containing script.
62 * {1} - Element tag
63 * {2} - line number of element.
64 * </pre>
65 */
66 public static final String INLINE_SCRIPT_DESCRIPTION
67 = "BaseScriptingEnvironment.constant.inline.script.description";
68
69 /**
70 * Constant used to describe inline scripts.
71 * <pre>
72 * {0} - URL of document containing script.
73 * {1} - Event attribute name
74 * {2} - line number of element.
75 * </pre>
76 */
77 public static final String EVENT_SCRIPT_DESCRIPTION
78 = "BaseScriptingEnvironment.constant.event.script.description";
79
80 /**
81 * Tells whether the given SVG document is dynamic.
82 */
83 public static boolean isDynamicDocument(BridgeContext ctx, Document doc) {
84 Element elt = doc.getDocumentElement();
85 if ((elt != null) &&
86 SVGConstants.SVG_NAMESPACE_URI.equals(elt.getNamespaceURI())) {
87 if (elt.getAttributeNS
88 (null, SVGConstants.SVG_ONABORT_ATTRIBUTE).length() > 0) {
89 return true;
90 }
91 if (elt.getAttributeNS
92 (null, SVGConstants.SVG_ONERROR_ATTRIBUTE).length() > 0) {
93 return true;
94 }
95 if (elt.getAttributeNS
96 (null, SVGConstants.SVG_ONRESIZE_ATTRIBUTE).length() > 0) {
97 return true;
98 }
99 if (elt.getAttributeNS
100 (null, SVGConstants.SVG_ONUNLOAD_ATTRIBUTE).length() > 0) {
101 return true;
102 }
103 if (elt.getAttributeNS
104 (null, SVGConstants.SVG_ONSCROLL_ATTRIBUTE).length() > 0) {
105 return true;
106 }
107 if (elt.getAttributeNS
108 (null, SVGConstants.SVG_ONZOOM_ATTRIBUTE).length() > 0) {
109 return true;
110 }
111 return isDynamicElement(ctx, doc.getDocumentElement());
112 }
113 return false;
114 }
115
116 public static boolean isDynamicElement(BridgeContext ctx, Element elt) {
117 List bridgeExtensions = ctx.getBridgeExtensions(elt.getOwnerDocument());
118 return isDynamicElement(elt, ctx, bridgeExtensions);
119 }
120
121 /**
122 * Tells whether the given SVG element is dynamic.
123 */
124 public static boolean isDynamicElement
125 (Element elt, BridgeContext ctx, List bridgeExtensions) {
126 Iterator i = bridgeExtensions.iterator();
127 while (i.hasNext()) {
128 BridgeExtension bridgeExtension = (BridgeExtension) i.next();
129 if (bridgeExtension.isDynamicElement(elt)) {
130 return true;
131 }
132 }
133 if (SVGConstants.SVG_NAMESPACE_URI.equals(elt.getNamespaceURI())) {
134 if (elt.getAttributeNS
135 (null, SVGConstants.SVG_ONKEYUP_ATTRIBUTE).length() > 0) {
136 return true;
137 }
138 if (elt.getAttributeNS
139 (null, SVGConstants.SVG_ONKEYDOWN_ATTRIBUTE).length() > 0) {
140 return true;
141 }
142 if (elt.getAttributeNS
143 (null, SVGConstants.SVG_ONKEYPRESS_ATTRIBUTE).length() > 0) {
144 return true;
145 }
146 if (elt.getAttributeNS
147 (null, SVGConstants.SVG_ONLOAD_ATTRIBUTE).length() > 0) {
148 return true;
149 }
150 if (elt.getAttributeNS
151 (null, SVGConstants.SVG_ONERROR_ATTRIBUTE).length() > 0) {
152 return true;
153 }
154 if (elt.getAttributeNS
155 (null, SVGConstants.SVG_ONACTIVATE_ATTRIBUTE).length() > 0) {
156 return true;
157 }
158 if (elt.getAttributeNS
159 (null, SVGConstants.SVG_ONCLICK_ATTRIBUTE).length() > 0) {
160 return true;
161 }
162 if (elt.getAttributeNS
163 (null, SVGConstants.SVG_ONFOCUSIN_ATTRIBUTE).length() > 0) {
164 return true;
165 }
166 if (elt.getAttributeNS
167 (null, SVGConstants.SVG_ONFOCUSOUT_ATTRIBUTE).length() > 0) {
168 return true;
169 }
170 if (elt.getAttributeNS
171 (null, SVGConstants.SVG_ONMOUSEDOWN_ATTRIBUTE).length() > 0) {
172 return true;
173 }
174 if (elt.getAttributeNS
175 (null, SVGConstants.SVG_ONMOUSEMOVE_ATTRIBUTE).length() > 0) {
176 return true;
177 }
178 if (elt.getAttributeNS
179 (null, SVGConstants.SVG_ONMOUSEOUT_ATTRIBUTE).length() > 0) {
180 return true;
181 }
182 if (elt.getAttributeNS
183 (null, SVGConstants.SVG_ONMOUSEOVER_ATTRIBUTE).length() > 0) {
184 return true;
185 }
186 if (elt.getAttributeNS
187 (null, SVGConstants.SVG_ONMOUSEUP_ATTRIBUTE).length() > 0) {
188 return true;
189 }
190 }
191
192 for (Node n = elt.getFirstChild();
193 n != null;
194 n = n.getNextSibling()) {
195 if (n.getNodeType() == Node.ELEMENT_NODE) {
196 if (isDynamicElement(ctx, (Element)n)) {
197 return true;
198 }
199 }
200 }
201 return false;
202 }
203
204
205 protected final static String EVENT_NAME = "event";
206 protected final static String ALTERNATE_EVENT_NAME = "evt";
207
208 /**
209 * The bridge context.
210 */
211 protected BridgeContext bridgeContext;
212
213 /**
214 * The user-agent.
215 */
216 protected UserAgent userAgent;
217
218 /**
219 * The document to manage.
220 */
221 protected Document document;
222
223 /**
224 * The URL of the document ot manage
225 */
226 protected ParsedURL docPURL;
227
228 protected Set languages = new HashSet();
229
230 /**
231 * The default Interpreter for the document
232 */
233 protected Interpreter interpreter;
234
235 /**
236 * Creates a new BaseScriptingEnvironment.
237 * @param ctx the bridge context
238 */
239 public BaseScriptingEnvironment(BridgeContext ctx) {
240 bridgeContext = ctx;
241 document = ctx.getDocument();
242 docPURL = new ParsedURL(((SVGDocument)document).getURL());
243 userAgent = bridgeContext.getUserAgent();
244 }
245
246 /**
247 * Creates a new Window object.
248 */
249 public org.apache.batik.script.Window createWindow
250 (Interpreter interp, String lang) {
251 return new Window(interp, lang);
252 }
253
254 /**
255 * Creates a new Window object.
256 */
257 public org.apache.batik.script.Window createWindow() {
258 return createWindow(null, null);
259 }
260
261 /**
262 * Returns the default Interpreter for this document.
263 */
264 public Interpreter getInterpreter() {
265 if (interpreter != null)
266 return interpreter;
267
268 SVGSVGElement root = (SVGSVGElement)document.getDocumentElement();
269 String lang = root.getContentScriptType();
270 return getInterpreter(lang);
271 }
272
273 public Interpreter getInterpreter(String lang) {
274 interpreter = bridgeContext.getInterpreter(lang);
275 if (interpreter == null) {
276 if (languages.contains(lang)) {
277 // Already issued warning so just return null;
278 return null;
279 }
280
281 // So we know we have processed this interpreter.
282 languages.add(lang);
283 return null;
284 }
285
286 if (!languages.contains(lang)) {
287 languages.add(lang);
288 initializeEnvironment(interpreter, lang);
289 }
290 return interpreter;
291 }
292
293 /**
294 * Initializes the environment of the given interpreter.
295 */
296 public void initializeEnvironment(Interpreter interp, String lang) {
297 interp.bindObject("window", createWindow(interp, lang));
298 }
299
300 /**
301 * Loads the scripts contained in the <script> elements.
302 */
303 public void loadScripts() {
304 org.apache.batik.script.Window window = null;
305
306 NodeList scripts = document.getElementsByTagNameNS
307 (SVGConstants.SVG_NAMESPACE_URI, SVGConstants.SVG_SCRIPT_TAG);
308 int len = scripts.getLength();
309
310 if (len == 0) {
311 return;
312 }
313
314 for (int i = 0; i < len; i++) {
315 Element script = (Element)scripts.item(i);
316 String type = script.getAttributeNS
317 (null, SVGConstants.SVG_TYPE_ATTRIBUTE);
318
319 if (type.length() == 0) {
320 type = SVGConstants.SVG_SCRIPT_TYPE_DEFAULT_VALUE;
321 }
322
323 //
324 // Java code invocation.
325 //
326 if (type.equals(SVGConstants.SVG_SCRIPT_TYPE_JAVA)) {
327 try {
328 String href = XLinkSupport.getXLinkHref(script);
329 ParsedURL purl = new ParsedURL
330 (XMLBaseSupport.getCascadedXMLBase(script), href);
331
332 checkCompatibleScriptURL(type, purl);
333
334 DocumentJarClassLoader cll;
335 URL docURL = null;
336 try {
337 docURL = new URL(docPURL.toString());
338 } catch (MalformedURLException mue) {
339 /* nothing just let docURL be null */
340 }
341 cll = new DocumentJarClassLoader
342 (new URL(purl.toString()), docURL);
343
344 // Get the 'Script-Handler' entry in the manifest.
345 URL url = cll.findResource("META-INF/MANIFEST.MF");
346 if (url == null) {
347 continue;
348 }
349 Manifest man = new Manifest(url.openStream());
350
351 String sh;
352
353 sh = man.getMainAttributes().getValue("Script-Handler");
354 if (sh != null) {
355 // Run the script handler.
356 ScriptHandler h;
357 h = (ScriptHandler)cll.loadClass(sh).newInstance();
358
359 if (window == null) {
360 window = createWindow();
361 }
362
363 h.run(document, window);
364 }
365
366 sh = man.getMainAttributes().getValue("SVG-Handler-Class");
367 if (sh != null) {
368 // Run the initializer
369 EventListenerInitializer initializer;
370 initializer =
371 (EventListenerInitializer)cll.loadClass(sh).newInstance();
372
373 if (window == null) {
374 window = createWindow();
375 }
376
377 initializer.initializeEventListeners((SVGDocument)document);
378 }
379 } catch (Exception e) {
380 if (userAgent != null) {
381 userAgent.displayError(e);
382 }
383 }
384 continue;
385 }
386
387 //
388 // Scripting language invocation.
389 //
390 Interpreter interpreter = getInterpreter(type);
391 if (interpreter == null)
392 // Can't find interpreter so just skip this script block.
393 continue;
394
395 try {
396 String href = XLinkSupport.getXLinkHref(script);
397 String desc = null;
398 Reader reader;
399
400 if (href.length() > 0) {
401 desc = href;
402
403 // External script.
404 ParsedURL purl = new ParsedURL
405 (XMLBaseSupport.getCascadedXMLBase(script), href);
406
407 checkCompatibleScriptURL(type, purl);
408 reader = new InputStreamReader(purl.openStream());
409 } else {
410 checkCompatibleScriptURL(type, docPURL);
411 DocumentLoader dl = bridgeContext.getDocumentLoader();
412 Element e = script;
413 SVGDocument d = (SVGDocument)e.getOwnerDocument();
414 int line = dl.getLineNumber(script);
415 desc = Messages.formatMessage
416 (INLINE_SCRIPT_DESCRIPTION,
417 new Object [] {d.getURL(),
418 "<"+script.getNodeName()+">",
419 new Integer(line)});
420 // Inline script.
421 Node n = script.getFirstChild();
422 if (n != null) {
423 StringBuffer sb = new StringBuffer();
424 while (n != null) {
425 if (n.getNodeType() == Node.CDATA_SECTION_NODE
426 || n.getNodeType() == Node.TEXT_NODE)
427 sb.append(n.getNodeValue());
428 n = n.getNextSibling();
429 }
430 reader = new StringReader(sb.toString());
431 } else {
432 continue;
433 }
434 }
435
436 interpreter.evaluate(reader, desc);
437
438 } catch (IOException e) {
439 if (userAgent != null) {
440 userAgent.displayError(e);
441 }
442 return;
443 } catch (InterpreterException e) {
444 System.err.println("InterpExcept: " + e);
445 handleInterpreterException(e);
446 return;
447 } catch (SecurityException e) {
448 if (userAgent != null) {
449 userAgent.displayError(e);
450 }
451 }
452 }
453 }
454
455 /**
456 * Checks that the script URLs and the document url are
457 * compatible. A SecurityException is thrown if loading
458 * the script is not allowed.
459 */
460 protected void checkCompatibleScriptURL(String scriptType,
461 ParsedURL scriptPURL){
462 userAgent.checkLoadScript(scriptType, scriptPURL, docPURL);
463 }
464
465 /**
466 * Recursively dispatch the SVG 'onload' event.
467 */
468 public void dispatchSVGLoadEvent() {
469 SVGSVGElement root = (SVGSVGElement)document.getDocumentElement();
470 String lang = root.getContentScriptType();
471 dispatchSVGLoad(root, true, lang);
472 }
473
474 /**
475 * Auxiliary method for dispatchSVGLoad.
476 */
477 protected void dispatchSVGLoad(Element elt,
478 boolean checkCanRun,
479 String lang) {
480 for (Node n = elt.getFirstChild();
481 n != null;
482 n = n.getNextSibling()) {
483 if (n.getNodeType() == Node.ELEMENT_NODE) {
484 dispatchSVGLoad((Element)n, checkCanRun, lang);
485 }
486 }
487
488 Event ev;
489 DocumentEvent de = (DocumentEvent)elt.getOwnerDocument();
490 ev = de.createEvent("SVGEvents");
491 ev.initEvent("SVGLoad", false, false);
492 EventTarget t = (EventTarget)elt;
493
494 final String s =
495 elt.getAttributeNS(null, SVGConstants.SVG_ONLOAD_ATTRIBUTE);
496 if (s.length() == 0) {
497 // No script to run so just dispatch the event to DOM
498 // (For java presumably).
499 t.dispatchEvent(ev);
500 return;
501 }
502
503 final Interpreter interp = getInterpreter();
504 if (interp == null) {
505 // Can't load interpreter so just dispatch normal event
506 // to the DOM (for java presumably).
507 t.dispatchEvent(ev);
508 return;
509 }
510
511 if (checkCanRun) {
512 // Check that it is ok to run embeded scripts
513 checkCompatibleScriptURL(lang, docPURL);
514 checkCanRun = false; // we only check once for onload handlers
515 }
516
517 DocumentLoader dl = bridgeContext.getDocumentLoader();
518 SVGDocument d = (SVGDocument)elt.getOwnerDocument();
519 int line = dl.getLineNumber(elt);
520 final String desc = Messages.formatMessage
521 (EVENT_SCRIPT_DESCRIPTION,
522 new Object [] {d.getURL(),
523 SVGConstants.SVG_ONLOAD_ATTRIBUTE,
524 new Integer(line)});
525
526 EventListener l = new EventListener() {
527 public void handleEvent(Event evt) {
528 try {
529 interp.bindObject(EVENT_NAME, evt);
530 interp.bindObject(ALTERNATE_EVENT_NAME, evt);
531 interp.evaluate(new StringReader(s), desc);
532 } catch (IOException io) {
533 } catch (InterpreterException e) {
534 handleInterpreterException(e);
535 }
536 }
537 };
538 t.addEventListener("SVGLoad", l, false);
539 t.dispatchEvent(ev);
540 t.removeEventListener("SVGLoad", l, false);
541 }
542
543 /**
544 * Method to dispatch SVG Zoom event.
545 */
546 protected void dispatchSVGZoomEvent() {
547 dispatchSVGDocEvent("SVGZoom");
548 }
549
550 /**
551 * Method to dispatch SVG Scroll event.
552 */
553 protected void dispatchSVGScrollEvent() {
554 dispatchSVGDocEvent("SVGScroll");
555 }
556
557 /**
558 * Method to dispatch SVG Resize event.
559 */
560 protected void dispatchSVGResizeEvent() {
561 dispatchSVGDocEvent("SVGResize");
562 }
563
564 protected void dispatchSVGDocEvent(String eventType) {
565 SVGSVGElement root =
566 (SVGSVGElement)document.getDocumentElement();
567 // Event is dispatched on outermost SVG element.
568 EventTarget t = root;
569
570 DocumentEvent de = (DocumentEvent)document;
571 Event ev = de.createEvent("SVGEvents");
572 ev.initEvent(eventType, false, false);
573 t.dispatchEvent(ev);
574 }
575
576 /**
577 * Handles the given exception.
578 */
579 protected void handleInterpreterException(InterpreterException ie) {
580 if (userAgent != null) {
581 Exception ex = ie.getException();
582 userAgent.displayError((ex == null) ? ie : ex);
583 }
584 }
585
586 /**
587 * Handles the given exception.
588 */
589 protected void handleSecurityException(SecurityException se) {
590 if (userAgent != null) {
591 userAgent.displayError(se);
592 }
593 }
594
595 /**
596 * Represents the window object of this environment.
597 */
598 protected class Window implements org.apache.batik.script.Window {
599
600 /**
601 * The associated interpreter.
602 */
603 protected Interpreter interpreter;
604
605 /**
606 * The associated language.
607 */
608 protected String language;
609
610 /**
611 * Creates a new Window.
612 */
613 public Window(Interpreter interp, String lang) {
614 interpreter = interp;
615 language = lang;
616 }
617
618 /**
619 * Implements {@link
620 * org.apache.batik.script.Window#setInterval(String,long)}.
621 */
622 public Object setInterval(final String script, long interval) {
623 return null;
624 }
625
626 /**
627 * Implements {@link
628 * org.apache.batik.script.Window#setInterval(Runnable,long)}.
629 */
630 public Object setInterval(final Runnable r, long interval) {
631 return null;
632 }
633
634 /**
635 * Implements {@link
636 * org.apache.batik.script.Window#clearInterval(Object)}.
637 */
638 public void clearInterval(Object interval) {
639 }
640
641 /**
642 * Implements {@link
643 * org.apache.batik.script.Window#setTimeout(String,long)}.
644 */
645 public Object setTimeout(final String script, long timeout) {
646 return null;
647 }
648
649 /**
650 * Implements {@link
651 * org.apache.batik.script.Window#setTimeout(Runnable,long)}.
652 */
653 public Object setTimeout(final Runnable r, long timeout) {
654 return null;
655 }
656
657 /**
658 * Implements {@link
659 * org.apache.batik.script.Window#clearTimeout(Object)}.
660 */
661 public void clearTimeout(Object timeout) {
662 }
663
664 /**
665 * Parses the given XML string into a DocumentFragment of the
666 * given document or a new document if 'doc' is null.
667 * The implementation in this class always returns 'null'
668 * @return The document/document fragment or null on error.
669 */
670 public Node parseXML(String text, Document doc) {
671 return null;
672 }
673
674 /**
675 * Gets data from the given URI.
676 * @param uri The URI where the data is located.
677 * @param h A handler called when the data is available.
678 */
679 public void getURL(String uri, org.apache.batik.script.Window.URLResponseHandler h) {
680 getURL(uri, h, "UTF8");
681 }
682
683 /**
684 * Gets data from the given URI.
685 * @param uri The URI where the data is located.
686 * @param h A handler called when the data is available.
687 * @param enc The character encoding of the data.
688 */
689 public void getURL(String uri,
690 org.apache.batik.script.Window.URLResponseHandler h,
691 String enc) {
692 }
693
694 public void postURL(String uri, String content,
695 org.apache.batik.script.Window.URLResponseHandler h) {
696 postURL(uri, content, h, "text/plain", null);
697 }
698
699 public void postURL(String uri, String content,
700 org.apache.batik.script.Window.URLResponseHandler h,
701 String mimeType) {
702 postURL(uri, content, h, mimeType, null);
703 }
704
705 public void postURL(String uri,
706 String content,
707 org.apache.batik.script.Window.URLResponseHandler h,
708 String mimeType,
709 String fEnc) {
710 }
711
712
713
714 /**
715 * Displays an alert dialog box.
716 */
717 public void alert(String message) {
718 }
719
720 /**
721 * Displays a confirm dialog box.
722 */
723 public boolean confirm(String message) {
724 return false;
725 }
726
727 /**
728 * Displays an input dialog box.
729 */
730 public String prompt(String message) {
731 return null;
732 }
733
734 /**
735 * Displays an input dialog box, given the default value.
736 */
737 public String prompt(String message, String defVal) {
738 return null;
739 }
740
741 /**
742 * Returns the current BridgeContext.
743 */
744 public BridgeContext getBridgeContext() {
745 return bridgeContext;
746 }
747
748 /**
749 * Returns the associated interpreter.
750 */
751 public Interpreter getInterpreter() {
752 return interpreter;
753 }
754
755 }
756 }