0001: /*
0002:
0003: Licensed to the Apache Software Foundation (ASF) under one or more
0004: contributor license agreements. See the NOTICE file distributed with
0005: this work for additional information regarding copyright ownership.
0006: The ASF licenses this file to You under the Apache License, Version 2.0
0007: (the "License"); you may not use this file except in compliance with
0008: the License. You may obtain a copy of the License at
0009:
0010: http://www.apache.org/licenses/LICENSE-2.0
0011:
0012: Unless required by applicable law or agreed to in writing, software
0013: distributed under the License is distributed on an "AS IS" BASIS,
0014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015: See the License for the specific language governing permissions and
0016: limitations under the License.
0017:
0018: */
0019: package org.apache.batik.bridge;
0020:
0021: import java.awt.Cursor;
0022: import java.awt.geom.Dimension2D;
0023: import java.io.IOException;
0024: import java.io.InterruptedIOException;
0025: import java.lang.ref.SoftReference;
0026: import java.lang.ref.WeakReference;
0027: import java.net.MalformedURLException;
0028: import java.net.URL;
0029: import java.util.HashMap;
0030: import java.util.HashSet;
0031: import java.util.Iterator;
0032: import java.util.LinkedList;
0033: import java.util.List;
0034: import java.util.ListIterator;
0035: import java.util.Map;
0036: import java.util.Set;
0037: import java.util.WeakHashMap;
0038:
0039: import org.apache.batik.bridge.svg12.SVG12BridgeContext;
0040: import org.apache.batik.bridge.svg12.SVG12BridgeExtension;
0041: import org.apache.batik.css.engine.CSSContext;
0042: import org.apache.batik.css.engine.CSSEngine;
0043: import org.apache.batik.css.engine.CSSEngineEvent;
0044: import org.apache.batik.css.engine.CSSEngineListener;
0045: import org.apache.batik.css.engine.CSSEngineUserAgent;
0046: import org.apache.batik.css.engine.SVGCSSEngine;
0047: import org.apache.batik.css.engine.SystemColorSupport;
0048: import org.apache.batik.css.engine.value.Value;
0049: import org.apache.batik.dom.AbstractNode;
0050: import org.apache.batik.dom.events.NodeEventTarget;
0051: import org.apache.batik.dom.svg.AnimatedAttributeListener;
0052: import org.apache.batik.dom.svg.AnimatedLiveAttributeValue;
0053: import org.apache.batik.dom.svg.SVGContext;
0054: import org.apache.batik.dom.svg.SVGDOMImplementation;
0055: import org.apache.batik.dom.svg.SVGOMDocument;
0056: import org.apache.batik.dom.svg.SVGOMElement;
0057: import org.apache.batik.dom.svg.SVGStylableElement;
0058: import org.apache.batik.dom.xbl.XBLManager;
0059: import org.apache.batik.gvt.CompositeGraphicsNode;
0060: import org.apache.batik.gvt.GraphicsNode;
0061: import org.apache.batik.gvt.TextPainter;
0062: import org.apache.batik.script.Interpreter;
0063: import org.apache.batik.script.InterpreterPool;
0064: import org.apache.batik.util.CSSConstants;
0065: import org.apache.batik.util.CleanerThread;
0066: import org.apache.batik.util.ParsedURL;
0067: import org.apache.batik.util.SVGConstants;
0068: import org.apache.batik.util.Service;
0069: import org.apache.batik.util.XMLConstants;
0070:
0071: import org.w3c.dom.Document;
0072: import org.w3c.dom.Element;
0073: import org.w3c.dom.Node;
0074: import org.w3c.dom.events.Event;
0075: import org.w3c.dom.events.EventListener;
0076: import org.w3c.dom.events.EventTarget;
0077: import org.w3c.dom.events.MouseEvent;
0078: import org.w3c.dom.events.MutationEvent;
0079: import org.w3c.dom.svg.SVGDocument;
0080:
0081: /**
0082: * This class represents a context used by the various bridges and the
0083: * builder. A bridge context is associated to a particular document
0084: * and cannot be reused.
0085: *
0086: * The context encapsulates the dynamic bindings between DOM elements
0087: * and GVT nodes, graphic contexts such as a <tt>GraphicsNodeRenderContext</tt>,
0088: * and the different objects required by the GVT builder to interpret
0089: * a SVG DOM tree such as the current viewport or the user agent.
0090: *
0091: * @author <a href="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
0092: * @version $Id: BridgeContext.java 504819 2007-02-08 08:23:19Z dvholten $
0093: */
0094: public class BridgeContext implements ErrorConstants, CSSContext {
0095:
0096: /**
0097: * The document is bridge context is dedicated to.
0098: */
0099: protected Document document;
0100:
0101: /**
0102: * Whether the document is an SVG 1.2 document.
0103: */
0104: protected boolean isSVG12;
0105:
0106: /**
0107: * The GVT builder that might be used to create a GVT subtree.
0108: */
0109: protected GVTBuilder gvtBuilder;
0110:
0111: /**
0112: * The interpreter cache per document.
0113: * key is the language -
0114: * value is a Interpreter
0115: */
0116: protected Map interpreterMap = new HashMap(7);
0117:
0118: /**
0119: * A Map of all the font families already matched. This is
0120: * to reduce the number of instances of GVTFontFamilies and to
0121: * hopefully reduce the time taken to search for a matching SVG font.
0122: */
0123: private Map fontFamilyMap;
0124:
0125: /**
0126: * The viewports.
0127: * key is an Element -
0128: * value is a Viewport
0129: */
0130: protected Map viewportMap = new WeakHashMap();
0131:
0132: /**
0133: * The viewport stack. Used in building time.
0134: */
0135: protected List viewportStack = new LinkedList();
0136:
0137: /**
0138: * The user agent.
0139: */
0140: protected UserAgent userAgent;
0141:
0142: /**
0143: * Binding Map:
0144: * key is an SVG Element -
0145: * value is a GraphicsNode
0146: */
0147: protected Map elementNodeMap;
0148:
0149: /**
0150: * Binding Map:
0151: * key is GraphicsNode -
0152: * value is a SVG Element.
0153: */
0154: protected Map nodeElementMap;
0155:
0156: /**
0157: * Bridge Map:
0158: * Keys are namespace URI - values are HashMap (with keys are local
0159: * name and values are a Bridge instance).
0160: */
0161: protected Map namespaceURIMap;
0162:
0163: /**
0164: * Default bridge.
0165: * When a bridge is requested for an element type that does not have a
0166: * bridge, and there is no other bridge for elements in the same namespace,
0167: * the default bridge is returned. This is used for custom elements,
0168: * which all use the same bridge type.
0169: */
0170: protected Bridge defaultBridge;
0171:
0172: /**
0173: * Default bridge reserved namespaces set.
0174: * Default bridges will not be created for elements that have a
0175: * namespace URI present in this set.
0176: */
0177: protected Set reservedNamespaceSet;
0178:
0179: /**
0180: * Element Data Map:
0181: * This is a general location for elements to 'cache'
0182: * data. Such as the graphics tree for a pattern or
0183: * the Gradient arrays.
0184: *
0185: * This is a weak hash map and the data is referenced
0186: * by SoftReference so both must be referenced elsewhere
0187: * to stay live.
0188: */
0189: protected Map elementDataMap;
0190:
0191: /**
0192: * The interpreter pool used to handle scripts.
0193: */
0194: protected InterpreterPool interpreterPool;
0195:
0196: /**
0197: * The document loader used to load/create Document.
0198: */
0199: protected DocumentLoader documentLoader;
0200:
0201: /**
0202: * The size of the document.
0203: */
0204: protected Dimension2D documentSize;
0205:
0206: /**
0207: * The text painter to use. Typically, you can specify the text painter that
0208: * will be used be text nodes.
0209: */
0210: protected TextPainter textPainter;
0211:
0212: /**
0213: * Indicates that no DOM listeners should be registered. In this
0214: * case the generated GVT tree should be totally independent of
0215: * the DOM tree (in practice text holds references to the source
0216: * text elements for font resolution).
0217: */
0218: public static final int STATIC = 0;
0219:
0220: /**
0221: * Indicates that DOM listeners should be registered to support,
0222: * 'interactivity' this includes anchors and cursors, but does not
0223: * include support for DOM modifications.
0224: */
0225: public static final int INTERACTIVE = 1;
0226:
0227: /**
0228: * Indicates that all DOM listeners should be registered. This supports
0229: * 'interactivity' (anchors and cursors), as well as DOM modifications
0230: * listeners to update the GVT rendering tree.
0231: */
0232: public static final int DYNAMIC = 2;
0233:
0234: /**
0235: * Whether the bridge should support dynamic, or interactive features.
0236: */
0237: protected int dynamicStatus = STATIC;
0238:
0239: /**
0240: * The update manager.
0241: */
0242: protected UpdateManager updateManager;
0243:
0244: /**
0245: * The XBL manager.
0246: */
0247: protected XBLManager xblManager;
0248:
0249: /**
0250: * The bridge context for the primary document, if this is a bridge
0251: * context for a resource document.
0252: */
0253: protected BridgeContext primaryContext;
0254:
0255: /**
0256: * Set of WeakReferences to child BridgeContexts.
0257: */
0258: protected HashSet childContexts = new HashSet();
0259:
0260: /**
0261: * The animation engine for the document.
0262: */
0263: protected SVGAnimationEngine animationEngine;
0264:
0265: /**
0266: * The animation limiting mode.
0267: */
0268: protected int animationLimitingMode;
0269:
0270: /**
0271: * The amount of animation limiting.
0272: */
0273: protected float animationLimitingAmount;
0274:
0275: /**
0276: * By default we share a unique instance of InterpreterPool.
0277: */
0278: private static InterpreterPool sharedPool = new InterpreterPool();
0279:
0280: /**
0281: * Constructs a new empty bridge context.
0282: */
0283: protected BridgeContext() {
0284: }
0285:
0286: /**
0287: * Constructs a new bridge context.
0288: * @param userAgent the user agent
0289: */
0290: public BridgeContext(UserAgent userAgent) {
0291: this (userAgent, sharedPool, new DocumentLoader(userAgent));
0292: }
0293:
0294: /**
0295: * Constructs a new bridge context.
0296: * @param userAgent the user agent
0297: * @param loader document loader
0298: */
0299: public BridgeContext(UserAgent userAgent, DocumentLoader loader) {
0300: this (userAgent, sharedPool, loader);
0301: }
0302:
0303: /**
0304: * Constructs a new bridge context.
0305: * @param userAgent the user agent
0306: * @param interpreterPool the interpreter pool
0307: * @param documentLoader document loader
0308: */
0309: public BridgeContext(UserAgent userAgent,
0310: InterpreterPool interpreterPool,
0311: DocumentLoader documentLoader) {
0312: this .userAgent = userAgent;
0313: this .viewportMap.put(userAgent,
0314: new UserAgentViewport(userAgent));
0315: this .interpreterPool = interpreterPool;
0316: this .documentLoader = documentLoader;
0317: }
0318:
0319: /**
0320: * Calls dispose on this BridgeContext, if it is a child context.
0321: */
0322: protected void finalize() {
0323: if (primaryContext != null) {
0324: dispose();
0325: }
0326: }
0327:
0328: /**
0329: * This function creates a new 'sub' BridgeContext to associated
0330: * with 'newDoc' if one currently doesn't exist, otherwise it
0331: * returns the BridgeContext currently associated with the
0332: * document.
0333: * @param newDoc The document to get/create a BridgeContext for.
0334: */
0335: public BridgeContext createSubBridgeContext(SVGOMDocument newDoc) {
0336: BridgeContext subCtx;
0337:
0338: CSSEngine eng = newDoc.getCSSEngine();
0339: if (eng != null) {
0340: subCtx = (BridgeContext) newDoc.getCSSEngine()
0341: .getCSSContext();
0342: return subCtx;
0343: }
0344:
0345: subCtx = createBridgeContext(newDoc);
0346: subCtx.primaryContext = primaryContext != null ? primaryContext
0347: : this ;
0348: subCtx.primaryContext.childContexts.add(new WeakReference(
0349: subCtx));
0350: subCtx.dynamicStatus = dynamicStatus;
0351: subCtx.setGVTBuilder(getGVTBuilder());
0352: subCtx.setTextPainter(getTextPainter());
0353: subCtx.setDocument(newDoc);
0354: subCtx.initializeDocument(newDoc);
0355: if (isInteractive())
0356: subCtx.addUIEventListeners(newDoc);
0357: return subCtx;
0358: }
0359:
0360: /**
0361: * This function creates a new BridgeContext, it mostly
0362: * exists so subclasses can provide an instance of
0363: * themselves when a sub BridgeContext is needed.
0364: */
0365: public BridgeContext createBridgeContext(SVGOMDocument doc) {
0366: if (doc.isSVG12()) {
0367: return new SVG12BridgeContext(getUserAgent(),
0368: getDocumentLoader());
0369: }
0370: return new BridgeContext(getUserAgent(), getDocumentLoader());
0371: }
0372:
0373: /**
0374: * Initializes the given document.
0375: */
0376: protected void initializeDocument(Document document) {
0377: SVGOMDocument doc = (SVGOMDocument) document;
0378: CSSEngine eng = doc.getCSSEngine();
0379: if (eng == null) {
0380: SVGDOMImplementation impl;
0381: impl = (SVGDOMImplementation) doc.getImplementation();
0382: eng = impl.createCSSEngine(doc, this );
0383: eng.setCSSEngineUserAgent(new CSSEngineUserAgentWrapper(
0384: userAgent));
0385: doc.setCSSEngine(eng);
0386: eng.setMedia(userAgent.getMedia());
0387: String uri = userAgent.getUserStyleSheetURI();
0388: if (uri != null) {
0389: try {
0390: URL url = new URL(uri);
0391: eng.setUserAgentStyleSheet(eng.parseStyleSheet(url,
0392: "all"));
0393: } catch (MalformedURLException e) {
0394: userAgent.displayError(e);
0395: }
0396: }
0397: eng.setAlternateStyleSheet(userAgent
0398: .getAlternateStyleSheet());
0399: }
0400: }
0401:
0402: /**
0403: * Returns the CSS engine associated with given element.
0404: */
0405: public CSSEngine getCSSEngineForElement(Element e) {
0406: SVGOMDocument doc = (SVGOMDocument) e.getOwnerDocument();
0407: return doc.getCSSEngine();
0408: }
0409:
0410: // properties ////////////////////////////////////////////////////////////
0411:
0412: /**
0413: * Sets the text painter that will be used by text nodes. This attributes
0414: * might be used by bridges (especially SVGTextElementBridge) to set the
0415: * text painter of each TextNode.
0416: *
0417: * @param textPainter the text painter for text nodes
0418: */
0419: public void setTextPainter(TextPainter textPainter) {
0420: this .textPainter = textPainter;
0421: }
0422:
0423: /**
0424: * Returns the text painter that will be used be text nodes.
0425: */
0426: public TextPainter getTextPainter() {
0427: return textPainter;
0428: }
0429:
0430: /**
0431: * Returns the document this bridge context is dedicated to.
0432: */
0433: public Document getDocument() {
0434: return document;
0435: }
0436:
0437: /**
0438: * Sets the document this bridge context is dedicated to, to the
0439: * specified document.
0440: * @param document the document
0441: */
0442: protected void setDocument(Document document) {
0443: if (this .document != document) {
0444: fontFamilyMap = null;
0445: }
0446: this .document = document;
0447: this .isSVG12 = ((SVGOMDocument) document).isSVG12();
0448: registerSVGBridges();
0449: }
0450:
0451: /**
0452: * Returns the map of font families
0453: */
0454: public Map getFontFamilyMap() {
0455: if (fontFamilyMap == null) {
0456: fontFamilyMap = new HashMap();
0457: }
0458: return fontFamilyMap;
0459: }
0460:
0461: /**
0462: * Sets the map of font families to the specified value.
0463: *
0464: *@param fontFamilyMap the map of font families
0465: */
0466: protected void setFontFamilyMap(Map fontFamilyMap) {
0467: this .fontFamilyMap = fontFamilyMap;
0468: }
0469:
0470: /**
0471: * Associates a data object with a node so it can be retrieved later.
0472: * This is primarily used for caching the graphics node generated from
0473: * a 'pattern' element. A soft reference to the data object is used.
0474: */
0475: public void setElementData(Node n, Object data) {
0476: if (elementDataMap == null) {
0477: elementDataMap = new WeakHashMap();
0478: }
0479: elementDataMap.put(n, new SoftReference(data));
0480: }
0481:
0482: /**
0483: * Retrieves a data object associated with the given node.
0484: */
0485: public Object getElementData(Node n) {
0486: if (elementDataMap == null)
0487: return null;
0488: Object o = elementDataMap.get(n);
0489: if (o == null)
0490: return null;
0491: SoftReference sr = (SoftReference) o;
0492: o = sr.get();
0493: if (o == null) {
0494: elementDataMap.remove(n);
0495: }
0496: return o;
0497: }
0498:
0499: /**
0500: * Returns the user agent of this bridge context.
0501: */
0502: public UserAgent getUserAgent() {
0503: return userAgent;
0504: }
0505:
0506: /**
0507: * Sets the user agent to the specified user agent.
0508: * @param userAgent the user agent
0509: */
0510: protected void setUserAgent(UserAgent userAgent) {
0511: this .userAgent = userAgent;
0512: }
0513:
0514: /**
0515: * Returns the GVT builder that is currently used to build the GVT tree.
0516: */
0517: public GVTBuilder getGVTBuilder() {
0518: return gvtBuilder;
0519: }
0520:
0521: /**
0522: * Sets the GVT builder that uses this context.
0523: */
0524: protected void setGVTBuilder(GVTBuilder gvtBuilder) {
0525: this .gvtBuilder = gvtBuilder;
0526: }
0527:
0528: /**
0529: * Returns the interpreter pool used to handle scripts.
0530: */
0531: public InterpreterPool getInterpreterPool() {
0532: return interpreterPool;
0533: }
0534:
0535: /**
0536: * Returns the focus manager.
0537: */
0538: public FocusManager getFocusManager() {
0539: return focusManager;
0540: }
0541:
0542: /**
0543: * Returns the cursor manager
0544: */
0545: public CursorManager getCursorManager() {
0546: return cursorManager;
0547: }
0548:
0549: /**
0550: * Sets the interpreter pool used to handle scripts to the
0551: * specified interpreter pool.
0552: * @param interpreterPool the interpreter pool
0553: */
0554: protected void setInterpreterPool(InterpreterPool interpreterPool) {
0555: this .interpreterPool = interpreterPool;
0556: }
0557:
0558: /**
0559: * Returns a Interpreter for the specified language.
0560: *
0561: * @param language the scripting language
0562: */
0563: public Interpreter getInterpreter(String language) {
0564: if (document == null) {
0565: throw new RuntimeException("Unknown document");
0566: }
0567: Interpreter interpreter = (Interpreter) interpreterMap
0568: .get(language);
0569: if (interpreter == null) {
0570: try {
0571: interpreter = interpreterPool.createInterpreter(
0572: document, language);
0573: interpreterMap.put(language, interpreter);
0574: } catch (Exception e) {
0575: if (userAgent != null) {
0576: userAgent.displayError(e);
0577: return null;
0578: }
0579: }
0580: }
0581:
0582: if (interpreter == null) {
0583: if (userAgent != null) {
0584: userAgent.displayError(new Exception(
0585: "Unknown language: " + language));
0586: }
0587: }
0588:
0589: return interpreter;
0590: }
0591:
0592: /**
0593: * Returns the document loader used to load external documents.
0594: */
0595: public DocumentLoader getDocumentLoader() {
0596: return documentLoader;
0597: }
0598:
0599: /**
0600: * Sets the document loader used to load external documents.
0601: * @param newDocumentLoader the new document loader
0602: */
0603: protected void setDocumentLoader(DocumentLoader newDocumentLoader) {
0604: this .documentLoader = newDocumentLoader;
0605: }
0606:
0607: /**
0608: * Returns the actual size of the document or null if the document
0609: * has not been built yet.
0610: */
0611: public Dimension2D getDocumentSize() {
0612: return documentSize;
0613: }
0614:
0615: /**
0616: * Sets the size of the document to the specified dimension.
0617: *
0618: * @param d the actual size of the SVG document
0619: */
0620: protected void setDocumentSize(Dimension2D d) {
0621: this .documentSize = d;
0622: }
0623:
0624: /**
0625: * Returns true if the document is dynamic, false otherwise.
0626: */
0627: public boolean isDynamic() {
0628: return (dynamicStatus == DYNAMIC);
0629: }
0630:
0631: /**
0632: * Returns true if the document is interactive, false otherwise.
0633: */
0634: public boolean isInteractive() {
0635: return (dynamicStatus != STATIC);
0636: }
0637:
0638: /**
0639: * Sets the document as a STATIC, INTERACTIVE or DYNAMIC document.
0640: * Call this method before the build phase
0641: * (ie. before <tt>gvtBuilder.build(...)</tt>)
0642: * otherwise, that will have no effect.
0643: *
0644: *@param status the document dynamicStatus
0645: */
0646: public void setDynamicState(int status) {
0647: dynamicStatus = status;
0648: }
0649:
0650: /**
0651: * Sets the document as DYNAMIC if <tt>dynamic</tt> is true
0652: * STATIC otherwise.
0653: */
0654: public void setDynamic(boolean dynamic) {
0655: if (dynamic)
0656: setDynamicState(DYNAMIC);
0657: else
0658: setDynamicState(STATIC);
0659: }
0660:
0661: /**
0662: * Sets the document as INTERACTIVE if <tt>interactive</tt> is
0663: * true STATIC otherwise.
0664: */
0665: public void setInteractive(boolean interactive) {
0666: if (interactive)
0667: setDynamicState(INTERACTIVE);
0668: else
0669: setDynamicState(STATIC);
0670: }
0671:
0672: /**
0673: * Returns the update manager, if the bridge supports dynamic features.
0674: */
0675: public UpdateManager getUpdateManager() {
0676: return updateManager;
0677: }
0678:
0679: /**
0680: * Sets the update manager.
0681: */
0682: protected void setUpdateManager(UpdateManager um) {
0683: updateManager = um;
0684: }
0685:
0686: /**
0687: * Sets the update manager on the given BridgeContext.
0688: */
0689: protected void setUpdateManager(BridgeContext ctx, UpdateManager um) {
0690: ctx.setUpdateManager(um);
0691: }
0692:
0693: /**
0694: * Sets the xblManager variable of the given BridgeContext.
0695: */
0696: protected void setXBLManager(BridgeContext ctx, XBLManager xm) {
0697: ctx.xblManager = xm;
0698: }
0699:
0700: /**
0701: * Returns whether the managed document is an SVG 1.2 document.
0702: */
0703: public boolean isSVG12() {
0704: return isSVG12;
0705: }
0706:
0707: /**
0708: * Returns the primary bridge context.
0709: */
0710: public BridgeContext getPrimaryBridgeContext() {
0711: if (primaryContext != null) {
0712: return primaryContext;
0713: }
0714: return this ;
0715: }
0716:
0717: /**
0718: * Returns an array of the child contexts.
0719: */
0720: public BridgeContext[] getChildContexts() {
0721: BridgeContext[] res = new BridgeContext[childContexts.size()];
0722: Iterator it = childContexts.iterator();
0723: for (int i = 0; i < res.length; i++) {
0724: WeakReference wr = (WeakReference) it.next();
0725: res[i] = (BridgeContext) wr.get();
0726: }
0727: return res;
0728: }
0729:
0730: /**
0731: * Returns the AnimationEngine for the document. Creates one if
0732: * it doesn't exist.
0733: */
0734: public SVGAnimationEngine getAnimationEngine() {
0735: if (animationEngine == null) {
0736: animationEngine = new SVGAnimationEngine(document, this );
0737: setAnimationLimitingMode();
0738: }
0739: return animationEngine;
0740: }
0741:
0742: // reference management //////////////////////////////////////////////////
0743:
0744: /**
0745: * Returns a new URIResolver object.
0746: */
0747: public URIResolver createURIResolver(SVGDocument doc,
0748: DocumentLoader dl) {
0749: return new URIResolver(doc, dl);
0750: }
0751:
0752: /**
0753: * Returns the node referenced by the specified element by the specified
0754: * uri. The referenced node can be either an element given by a fragment
0755: * ID, or the document node.
0756: * @param e the element referencing
0757: * @param uri the uri of the referenced node
0758: */
0759: public Node getReferencedNode(Element e, String uri) {
0760: try {
0761: SVGDocument document = (SVGDocument) e.getOwnerDocument();
0762: URIResolver ur = createURIResolver(document, documentLoader);
0763: Node ref = ur.getNode(uri, e);
0764: if (ref == null) {
0765: throw new BridgeException(this , e, ERR_URI_BAD_TARGET,
0766: new Object[] { uri });
0767: } else {
0768: SVGOMDocument refDoc = (SVGOMDocument) (ref
0769: .getNodeType() == Node.DOCUMENT_NODE ? ref
0770: : ref.getOwnerDocument());
0771: // This is new rather than attaching this BridgeContext
0772: // with the new document we now create a whole new
0773: // BridgeContext to go with the new document.
0774: // This means that the new document has it's own
0775: // world of stuff and it should avoid memory leaks
0776: // since the new document isn't 'tied into' this
0777: // bridge context.
0778: if (refDoc != document) {
0779: createSubBridgeContext(refDoc);
0780: }
0781: return ref;
0782: }
0783: } catch (MalformedURLException ex) {
0784: throw new BridgeException(this , e, ex, ERR_URI_MALFORMED,
0785: new Object[] { uri });
0786: } catch (InterruptedIOException ex) {
0787: throw new InterruptedBridgeException();
0788: } catch (IOException ex) {
0789: ex.printStackTrace();
0790: throw new BridgeException(this , e, ex, ERR_URI_IO,
0791: new Object[] { uri });
0792: } catch (SecurityException ex) {
0793: throw new BridgeException(this , e, ex, ERR_URI_UNSECURE,
0794: new Object[] { uri });
0795: }
0796: }
0797:
0798: /**
0799: * Returns the element referenced by the specified element by the
0800: * specified uri. The referenced element can not be a Document.
0801: *
0802: * @param e the element referencing
0803: * @param uri the uri of the referenced element
0804: */
0805: public Element getReferencedElement(Element e, String uri) {
0806: Node ref = getReferencedNode(e, uri);
0807: if (ref != null && ref.getNodeType() != Node.ELEMENT_NODE) {
0808: throw new BridgeException(this , e,
0809: ERR_URI_REFERENCE_A_DOCUMENT, new Object[] { uri });
0810: }
0811: return (Element) ref;
0812: }
0813:
0814: // Viewport //////////////////////////////////////////////////////////////
0815:
0816: /**
0817: * Returns the viewport of the specified element.
0818: *
0819: * @param e the element interested in its viewport
0820: */
0821: public Viewport getViewport(Element e) {
0822: if (viewportStack != null) {
0823: // building time
0824: if (viewportStack.size() == 0) {
0825: // outermost svg element
0826: return (Viewport) viewportMap.get(userAgent);
0827: } else {
0828: // current viewport
0829: return (Viewport) viewportStack.get(0);
0830: }
0831: } else {
0832: // search the first parent which has defined a viewport
0833: e = SVGUtilities.getParentElement(e);
0834: while (e != null) {
0835: Viewport viewport = (Viewport) viewportMap.get(e);
0836: if (viewport != null) {
0837: return viewport;
0838: }
0839: e = SVGUtilities.getParentElement(e);
0840: }
0841: return (Viewport) viewportMap.get(userAgent);
0842: }
0843: }
0844:
0845: /**
0846: * Starts a new viewport from the specified element.
0847: *
0848: * @param e the element that defines a new viewport
0849: * @param viewport the viewport of the element
0850: */
0851: public void openViewport(Element e, Viewport viewport) {
0852: viewportMap.put(e, viewport);
0853: if (viewportStack == null) {
0854: viewportStack = new LinkedList();
0855: }
0856: viewportStack.add(0, viewport);
0857: }
0858:
0859: public void removeViewport(Element e) {
0860: viewportMap.remove(e);
0861: }
0862:
0863: /**
0864: * Closes the viewport associated to the specified element.
0865: * @param e the element that closes its viewport
0866: */
0867: public void closeViewport(Element e) {
0868: //viewportMap.remove(e); FIXME: potential memory leak
0869: viewportStack.remove(0);
0870: if (viewportStack.size() == 0) {
0871: viewportStack = null;
0872: }
0873: }
0874:
0875: // Bindings //////////////////////////////////////////////////////////////
0876:
0877: /**
0878: * Binds the specified GraphicsNode to the specified Element. This method
0879: * automatically bind the graphics node to the element and the element to
0880: * the graphics node.
0881: *
0882: * @param element the element to bind to the specified graphics node
0883: * @param node the graphics node to bind to the specified element
0884: */
0885: public void bind(Element element, GraphicsNode node) {
0886: if (elementNodeMap == null) {
0887: elementNodeMap = new WeakHashMap();
0888: nodeElementMap = new WeakHashMap();
0889: }
0890: elementNodeMap.put(element, new SoftReference(node));
0891: nodeElementMap.put(node, new SoftReference(element));
0892: }
0893:
0894: /**
0895: * Removes the binding of the specified Element.
0896: *
0897: * @param element the element to unbind
0898: */
0899: public void unbind(Element element) {
0900: if (elementNodeMap == null) {
0901: return;
0902: }
0903: GraphicsNode node = null;
0904: SoftReference sr = (SoftReference) elementNodeMap.get(element);
0905: if (sr != null)
0906: node = (GraphicsNode) sr.get();
0907: elementNodeMap.remove(element);
0908: if (node != null)
0909: nodeElementMap.remove(node);
0910: }
0911:
0912: /**
0913: * Returns the GraphicsNode associated to the specified Element or
0914: * null if any.
0915: *
0916: * @param element the element associated to the graphics node to return
0917: */
0918: public GraphicsNode getGraphicsNode(Element element) {
0919: if (elementNodeMap != null) {
0920: SoftReference sr = (SoftReference) elementNodeMap
0921: .get(element);
0922: if (sr != null)
0923: return (GraphicsNode) sr.get();
0924: }
0925: return null;
0926: }
0927:
0928: /**
0929: * Returns the Element associated to the specified GraphicsNode or
0930: * null if any.
0931: *
0932: * @param node the graphics node associated to the element to return
0933: */
0934: public Element getElement(GraphicsNode node) {
0935: if (nodeElementMap != null) {
0936: SoftReference sr = (SoftReference) nodeElementMap.get(node);
0937: if (sr != null)
0938: return (Element) sr.get();
0939: }
0940: return null;
0941: }
0942:
0943: // Bridge management /////////////////////////////////////////////////////
0944:
0945: /**
0946: * Returns true if the specified element has a GraphicsNodeBridge
0947: * associated to it, false otherwise.
0948: *
0949: * @param element the element
0950: */
0951: public boolean hasGraphicsNodeBridge(Element element) {
0952: if (namespaceURIMap == null || element == null) {
0953: return false;
0954: }
0955: String localName = element.getLocalName();
0956: String namespaceURI = element.getNamespaceURI();
0957: namespaceURI = ((namespaceURI == null) ? "" : namespaceURI);
0958: HashMap localNameMap = (HashMap) namespaceURIMap
0959: .get(namespaceURI);
0960: if (localNameMap == null) {
0961: return false;
0962: }
0963: return (localNameMap.get(localName) instanceof GraphicsNodeBridge);
0964: }
0965:
0966: /**
0967: * Returns the bridge associated with the specified element.
0968: *
0969: * @param element the element
0970: */
0971: public Bridge getBridge(Element element) {
0972: if (namespaceURIMap == null || element == null) {
0973: return null;
0974: }
0975: String localName = element.getLocalName();
0976: String namespaceURI = element.getNamespaceURI();
0977: namespaceURI = ((namespaceURI == null) ? "" : namespaceURI);
0978: return getBridge(namespaceURI, localName);
0979: }
0980:
0981: /**
0982: * Returns the bridge associated with the element type
0983: *
0984: * @param namespaceURI namespace of the requested element
0985: * @param localName element's local name
0986: *
0987: */
0988: public Bridge getBridge(String namespaceURI, String localName) {
0989: Bridge bridge = null;
0990: if (namespaceURIMap != null) {
0991: HashMap localNameMap = (HashMap) namespaceURIMap
0992: .get(namespaceURI);
0993: if (localNameMap != null) {
0994: bridge = (Bridge) localNameMap.get(localName);
0995: }
0996: }
0997: if (bridge == null
0998: && (reservedNamespaceSet == null || !reservedNamespaceSet
0999: .contains(namespaceURI))) {
1000: bridge = defaultBridge;
1001: }
1002: if (isDynamic()) {
1003: return bridge == null ? null : bridge.getInstance();
1004: } else {
1005: return bridge;
1006: }
1007: }
1008:
1009: /**
1010: * Associates the specified <tt>Bridge</tt> object with the specified
1011: * namespace URI and local name.
1012: * @param namespaceURI the namespace URI
1013: * @param localName the local name
1014: * @param bridge the bridge that manages the element
1015: */
1016: public void putBridge(String namespaceURI, String localName,
1017: Bridge bridge) {
1018: // start assert
1019: if (!(namespaceURI.equals(bridge.getNamespaceURI()) && localName
1020: .equals(bridge.getLocalName()))) {
1021: throw new Error("Invalid Bridge: " + namespaceURI + "/"
1022: + bridge.getNamespaceURI() + " " + localName + "/"
1023: + bridge.getLocalName() + " " + bridge.getClass());
1024: }
1025: // end assert
1026: if (namespaceURIMap == null) {
1027: namespaceURIMap = new HashMap();
1028: }
1029: namespaceURI = ((namespaceURI == null) ? "" : namespaceURI);
1030: HashMap localNameMap = (HashMap) namespaceURIMap
1031: .get(namespaceURI);
1032: if (localNameMap == null) {
1033: localNameMap = new HashMap();
1034: namespaceURIMap.put(namespaceURI, localNameMap);
1035: }
1036: localNameMap.put(localName, bridge);
1037: }
1038:
1039: /**
1040: * Associates the specified <tt>Bridge</tt> object with it's
1041: * namespace URI and local name.
1042: *
1043: * @param bridge the bridge that manages the element
1044: */
1045: public void putBridge(Bridge bridge) {
1046: putBridge(bridge.getNamespaceURI(), bridge.getLocalName(),
1047: bridge);
1048: }
1049:
1050: /**
1051: * Removes the <tt>Bridge</tt> object associated to the specified
1052: * namespace URI and local name.
1053: *
1054: * @param namespaceURI the namespace URI
1055: * @param localName the local name
1056: */
1057: public void removeBridge(String namespaceURI, String localName) {
1058: if (namespaceURIMap == null) {
1059: return;
1060: }
1061: namespaceURI = ((namespaceURI == null) ? "" : namespaceURI);
1062: HashMap localNameMap = (HashMap) namespaceURIMap
1063: .get(namespaceURI);
1064: if (localNameMap != null) {
1065: localNameMap.remove(localName);
1066: if (localNameMap.isEmpty()) {
1067: namespaceURIMap.remove(namespaceURI);
1068: if (namespaceURIMap.isEmpty()) {
1069: namespaceURIMap = null;
1070: }
1071: }
1072: }
1073: }
1074:
1075: /**
1076: * Sets the <tt>Bridge</tt> object to be used for foreign
1077: * namespace elements.
1078: *
1079: * @param bridge the bridge that manages the element
1080: */
1081: public void setDefaultBridge(Bridge bridge) {
1082: defaultBridge = bridge;
1083: }
1084:
1085: /**
1086: * Adds a namespace URI to avoid when creating default bridges.
1087: */
1088: public void putReservedNamespaceURI(String namespaceURI) {
1089: if (namespaceURI == null) {
1090: namespaceURI = "";
1091: }
1092: if (reservedNamespaceSet == null) {
1093: reservedNamespaceSet = new HashSet();
1094: }
1095: reservedNamespaceSet.add(namespaceURI);
1096: }
1097:
1098: /**
1099: * Removes a namespace URI to avoid when creating default bridges.
1100: */
1101: public void removeReservedNamespaceURI(String namespaceURI) {
1102: if (namespaceURI == null) {
1103: namespaceURI = "";
1104: }
1105: if (reservedNamespaceSet != null) {
1106: reservedNamespaceSet.remove(namespaceURI);
1107: if (reservedNamespaceSet.isEmpty()) {
1108: reservedNamespaceSet = null;
1109: }
1110: }
1111: }
1112:
1113: // dynamic support ////////////////////////////////////////////////////////
1114:
1115: /**
1116: * The list of all EventListener attached by bridges that need to
1117: * be removed on a dispose() call.
1118: */
1119: protected Set eventListenerSet = new HashSet();
1120:
1121: /**
1122: * The DOM EventListener to receive 'DOMCharacterDataModified' event.
1123: */
1124: protected EventListener domCharacterDataModifiedEventListener;
1125:
1126: /**
1127: * The DOM EventListener to receive 'DOMAttrModified' event.
1128: */
1129: protected EventListener domAttrModifiedEventListener;
1130:
1131: /**
1132: * The DOM EventListener to receive 'DOMNodeInserted' event.
1133: */
1134: protected EventListener domNodeInsertedEventListener;
1135:
1136: /**
1137: * The DOM EventListener to receive 'DOMNodeRemoved' event.
1138: */
1139: protected EventListener domNodeRemovedEventListener;
1140:
1141: /**
1142: * The CSSEngine listener to receive CSSEngineEvent.
1143: */
1144: protected CSSEngineListener cssPropertiesChangedListener;
1145:
1146: /**
1147: * The listener to receive notification of animated attribute changes.
1148: */
1149: protected AnimatedAttributeListener animatedAttributeListener;
1150:
1151: /**
1152: * The EventListener that is responsible of managing DOM focus event.
1153: */
1154: protected FocusManager focusManager;
1155:
1156: /**
1157: * Manages cursors and performs caching when appropriate
1158: */
1159: protected CursorManager cursorManager = new CursorManager(this );
1160:
1161: /**
1162: * Adds EventListeners to the input document to handle the cursor
1163: * property.
1164: * This is not done in the addDOMListeners method because
1165: * addDOMListeners is only used for dynamic content whereas
1166: * cursor support is provided for all content.
1167: * Also note that it is very important that the listeners be
1168: * registered for the capture phase as the 'default' behavior
1169: * for cursors is handled by the BridgeContext during the
1170: * capture phase and the 'custom' behavior (handling of 'auto'
1171: * on anchors, for example), is handled during the bubbling phase.
1172: */
1173: public void addUIEventListeners(Document doc) {
1174: NodeEventTarget evtTarget = (NodeEventTarget) doc
1175: .getDocumentElement();
1176:
1177: DOMMouseOverEventListener domMouseOverListener = new DOMMouseOverEventListener();
1178: evtTarget.addEventListenerNS(
1179: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1180: SVGConstants.SVG_EVENT_MOUSEOVER, domMouseOverListener,
1181: true, null);
1182: storeEventListenerNS(evtTarget,
1183: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1184: SVGConstants.SVG_EVENT_MOUSEOVER, domMouseOverListener,
1185: true);
1186:
1187: DOMMouseOutEventListener domMouseOutListener = new DOMMouseOutEventListener();
1188: evtTarget.addEventListenerNS(
1189: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1190: SVGConstants.SVG_EVENT_MOUSEOUT, domMouseOutListener,
1191: true, null);
1192: storeEventListenerNS(evtTarget,
1193: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1194: SVGConstants.SVG_EVENT_MOUSEOUT, domMouseOutListener,
1195: true);
1196:
1197: }
1198:
1199: public void removeUIEventListeners(Document doc) {
1200: EventTarget evtTarget = (EventTarget) doc.getDocumentElement();
1201: synchronized (eventListenerSet) {
1202: Iterator i = eventListenerSet.iterator();
1203: while (i.hasNext()) {
1204: EventListenerMememto elm = (EventListenerMememto) i
1205: .next();
1206: NodeEventTarget et = elm.getTarget();
1207: if (et == evtTarget) {
1208: EventListener el = elm.getListener();
1209: boolean uc = elm.getUseCapture();
1210: String t = elm.getEventType();
1211: boolean n = elm.getNamespaced();
1212: if (et == null || el == null || t == null) {
1213: continue;
1214: }
1215: if (n) {
1216: String ns = elm.getNamespaceURI();
1217: et.removeEventListenerNS(ns, t, el, uc);
1218: } else {
1219: et.removeEventListener(t, el, uc);
1220: }
1221: }
1222: }
1223: }
1224: }
1225:
1226: /**
1227: * Adds EventListeners to the DOM and CSSEngineListener to the
1228: * CSSEngine to handle any modifications on the DOM tree or style
1229: * properties and update the GVT tree in response.
1230: */
1231: public void addDOMListeners() {
1232: SVGOMDocument doc = (SVGOMDocument) document;
1233:
1234: domAttrModifiedEventListener = new DOMAttrModifiedEventListener();
1235: doc.addEventListenerNS(XMLConstants.XML_EVENTS_NAMESPACE_URI,
1236: "DOMAttrModified", domAttrModifiedEventListener, true,
1237: null);
1238:
1239: domNodeInsertedEventListener = new DOMNodeInsertedEventListener();
1240: doc.addEventListenerNS(XMLConstants.XML_EVENTS_NAMESPACE_URI,
1241: "DOMNodeInserted", domNodeInsertedEventListener, true,
1242: null);
1243:
1244: domNodeRemovedEventListener = new DOMNodeRemovedEventListener();
1245: doc.addEventListenerNS(XMLConstants.XML_EVENTS_NAMESPACE_URI,
1246: "DOMNodeRemoved", domNodeRemovedEventListener, true,
1247: null);
1248:
1249: domCharacterDataModifiedEventListener = new DOMCharacterDataModifiedEventListener();
1250: doc.addEventListenerNS(XMLConstants.XML_EVENTS_NAMESPACE_URI,
1251: "DOMCharacterDataModified",
1252: domCharacterDataModifiedEventListener, true, null);
1253:
1254: animatedAttributeListener = new AnimatedAttrListener();
1255: doc.addAnimatedAttributeListener(animatedAttributeListener);
1256:
1257: focusManager = new FocusManager(document);
1258:
1259: CSSEngine cssEngine = doc.getCSSEngine();
1260: cssPropertiesChangedListener = new CSSPropertiesChangedListener();
1261: cssEngine.addCSSEngineListener(cssPropertiesChangedListener);
1262: }
1263:
1264: /**
1265: * Removes event listeners from the DOM and CSS engine.
1266: */
1267: protected void removeDOMListeners() {
1268: SVGOMDocument doc = (SVGOMDocument) document;
1269:
1270: doc.removeEventListenerNS(
1271: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1272: "DOMAttrModified", domAttrModifiedEventListener, true);
1273: doc.removeEventListenerNS(
1274: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1275: "DOMNodeInserted", domNodeInsertedEventListener, true);
1276: doc.removeEventListenerNS(
1277: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1278: "DOMNodeRemoved", domNodeRemovedEventListener, true);
1279: doc.removeEventListenerNS(
1280: XMLConstants.XML_EVENTS_NAMESPACE_URI,
1281: "DOMCharacterDataModified",
1282: domCharacterDataModifiedEventListener, true);
1283:
1284: doc.removeAnimatedAttributeListener(animatedAttributeListener);
1285:
1286: CSSEngine cssEngine = doc.getCSSEngine();
1287: if (cssEngine != null) {
1288: cssEngine
1289: .removeCSSEngineListener(cssPropertiesChangedListener);
1290: cssEngine.dispose();
1291: doc.setCSSEngine(null);
1292: }
1293: }
1294:
1295: /**
1296: * Adds to the eventListenerSet the specified event listener
1297: * registration.
1298: */
1299: protected void storeEventListener(EventTarget t, String s,
1300: EventListener l, boolean b) {
1301: synchronized (eventListenerSet) {
1302: eventListenerSet.add(new EventListenerMememto(t, s, l, b,
1303: this ));
1304: }
1305: }
1306:
1307: /**
1308: * Adds to the eventListenerSet the specified event listener
1309: * registration.
1310: */
1311: protected void storeEventListenerNS(EventTarget t, String n,
1312: String s, EventListener l, boolean b) {
1313: synchronized (eventListenerSet) {
1314: eventListenerSet.add(new EventListenerMememto(t, n, s, l,
1315: b, this ));
1316: }
1317: }
1318:
1319: public static class SoftReferenceMememto extends
1320: CleanerThread.SoftReferenceCleared {
1321: Object mememto;
1322: Set set;
1323:
1324: // String refStr;
1325: SoftReferenceMememto(Object ref, Object mememto, Set set) {
1326: super (ref);
1327: // refStr = ref.toString();
1328: this .mememto = mememto;
1329: this .set = set;
1330: }
1331:
1332: public void cleared() {
1333: synchronized (set) {
1334: // System.err.println("SRClear: " + refStr);
1335: set.remove(mememto);
1336: mememto = null;
1337: set = null;
1338: }
1339: }
1340: }
1341:
1342: /**
1343: * A class used to store an EventListener added to the DOM.
1344: */
1345: protected static class EventListenerMememto {
1346:
1347: public SoftReference target; // Soft ref to EventTarget
1348: public SoftReference listener; // Soft ref to EventListener
1349: public boolean useCapture;
1350: public String namespaceURI;
1351: public String eventType;
1352: public boolean namespaced;
1353:
1354: public EventListenerMememto(EventTarget t, String s,
1355: EventListener l, boolean b, BridgeContext ctx) {
1356: Set set = ctx.eventListenerSet;
1357: target = new SoftReferenceMememto(t, this , set);
1358: listener = new SoftReferenceMememto(l, this , set);
1359: eventType = s;
1360: useCapture = b;
1361: }
1362:
1363: public EventListenerMememto(EventTarget t, String n, String s,
1364: EventListener l, boolean b, BridgeContext ctx) {
1365: this (t, s, l, b, ctx);
1366: namespaceURI = n;
1367: namespaced = true;
1368: }
1369:
1370: public EventListener getListener() {
1371: return (EventListener) listener.get();
1372: }
1373:
1374: public NodeEventTarget getTarget() {
1375: return (NodeEventTarget) target.get();
1376: }
1377:
1378: public boolean getUseCapture() {
1379: return useCapture;
1380: }
1381:
1382: public String getNamespaceURI() {
1383: return namespaceURI;
1384: }
1385:
1386: public String getEventType() {
1387: return eventType;
1388: }
1389:
1390: public boolean getNamespaced() {
1391: return namespaced;
1392: }
1393: }
1394:
1395: /**
1396: * Adds the GVT listener for AWT event support.
1397: */
1398: public void addGVTListener(Document doc) {
1399: BridgeEventSupport.addGVTListener(this , doc);
1400: }
1401:
1402: /**
1403: * Clears the list of child BridgeContexts and disposes them if there are
1404: * no more references to them.
1405: */
1406: protected void clearChildContexts() {
1407: childContexts.clear();
1408: }
1409:
1410: /**
1411: * Disposes this BridgeContext.
1412: */
1413: public void dispose() {
1414: clearChildContexts();
1415:
1416: synchronized (eventListenerSet) {
1417: // remove all listeners added by Bridges
1418: Iterator iter = eventListenerSet.iterator();
1419: while (iter.hasNext()) {
1420: EventListenerMememto m = (EventListenerMememto) iter
1421: .next();
1422: NodeEventTarget et = m.getTarget();
1423: EventListener el = m.getListener();
1424: boolean uc = m.getUseCapture();
1425: String t = m.getEventType();
1426: boolean n = m.getNamespaced();
1427: if (et == null || el == null || t == null) {
1428: continue;
1429: }
1430: if (n) {
1431: String ns = m.getNamespaceURI();
1432: et.removeEventListenerNS(ns, t, el, uc);
1433: } else {
1434: et.removeEventListener(t, el, uc);
1435: }
1436: }
1437: }
1438:
1439: if (document != null) {
1440: removeDOMListeners();
1441: }
1442:
1443: if (animationEngine != null) {
1444: animationEngine.dispose();
1445: animationEngine = null;
1446: }
1447:
1448: Iterator iter = interpreterMap.values().iterator();
1449: while (iter.hasNext()) {
1450: Interpreter interpreter = (Interpreter) iter.next();
1451: if (interpreter != null)
1452: interpreter.dispose();
1453: }
1454: interpreterMap.clear();
1455:
1456: if (focusManager != null) {
1457: focusManager.dispose();
1458: }
1459: if (elementDataMap != null) {
1460: elementDataMap.clear();
1461: }
1462: if (nodeElementMap != null) {
1463: nodeElementMap.clear();
1464: }
1465: if (elementNodeMap != null) {
1466: elementNodeMap.clear();
1467: }
1468: }
1469:
1470: /**
1471: * Returns the SVGContext associated to the specified Node or null if
1472: * there is none.
1473: */
1474: protected static SVGContext getSVGContext(Node node) {
1475: if (node instanceof SVGOMElement) {
1476: return ((SVGOMElement) node).getSVGContext();
1477: } else {
1478: return null;
1479: }
1480: }
1481:
1482: /**
1483: * Returns the BridgeUpdateHandler associated to the specified Node
1484: * or null if there is none.
1485: */
1486: protected static BridgeUpdateHandler getBridgeUpdateHandler(
1487: Node node) {
1488: SVGContext ctx = getSVGContext(node);
1489: return (ctx == null) ? null : (BridgeUpdateHandler) ctx;
1490: }
1491:
1492: /**
1493: * The DOM EventListener invoked when an attribute is modified.
1494: */
1495: protected class DOMAttrModifiedEventListener implements
1496: EventListener {
1497:
1498: /**
1499: * Creates a new DOMAttrModifiedEventListener.
1500: */
1501: public DOMAttrModifiedEventListener() {
1502: }
1503:
1504: /**
1505: * Handles 'DOMAttrModified' event type.
1506: */
1507: public void handleEvent(Event evt) {
1508: Node node = (Node) evt.getTarget();
1509: BridgeUpdateHandler h = getBridgeUpdateHandler(node);
1510: if (h != null) {
1511: try {
1512: h.handleDOMAttrModifiedEvent((MutationEvent) evt);
1513: } catch (Exception e) {
1514: userAgent.displayError(e);
1515: }
1516: }
1517: }
1518: }
1519:
1520: /**
1521: * The DOM EventListener invoked when the mouse exits an element
1522: */
1523: protected class DOMMouseOutEventListener implements EventListener {
1524:
1525: /**
1526: * Creates a new DOMMouseOutEventListener.
1527: */
1528: public DOMMouseOutEventListener() {
1529: }
1530:
1531: /**
1532: * Handles 'mouseout' MouseEvent event type.
1533: */
1534: public void handleEvent(Event evt) {
1535: MouseEvent me = (MouseEvent) evt;
1536: Element newTarget = (Element) me.getRelatedTarget();
1537: Cursor cursor = CursorManager.DEFAULT_CURSOR;
1538: if (newTarget != null)
1539: cursor = CSSUtilities.convertCursor(newTarget,
1540: BridgeContext.this );
1541: if (cursor == null)
1542: cursor = CursorManager.DEFAULT_CURSOR;
1543:
1544: userAgent.setSVGCursor(cursor);
1545: }
1546: }
1547:
1548: /**
1549: * The DOM EventListener invoked when the mouse mouves over a new
1550: * element.
1551: *
1552: * Here is how cursors are handled:
1553: *
1554: */
1555: protected class DOMMouseOverEventListener implements EventListener {
1556:
1557: /**
1558: * Creates a new DOMMouseOverEventListener.
1559: */
1560: public DOMMouseOverEventListener() {
1561: }
1562:
1563: /**
1564: * Handles 'mouseover' MouseEvent event type.
1565: */
1566: public void handleEvent(Event evt) {
1567: Element target = (Element) evt.getTarget();
1568: Cursor cursor = CSSUtilities.convertCursor(target,
1569: BridgeContext.this );
1570:
1571: if (cursor != null) {
1572: userAgent.setSVGCursor(cursor);
1573: }
1574: }
1575: }
1576:
1577: /**
1578: * The DOM EventListener invoked when a node is added.
1579: */
1580: protected class DOMNodeInsertedEventListener implements
1581: EventListener {
1582:
1583: /**
1584: * Creates a new DOMNodeInsertedEventListener.
1585: */
1586: public DOMNodeInsertedEventListener() {
1587: }
1588:
1589: /**
1590: * Handles 'DOMNodeInserted' event type.
1591: */
1592: public void handleEvent(Event evt) {
1593: MutationEvent me = (MutationEvent) evt;
1594: BridgeUpdateHandler h = getBridgeUpdateHandler(me
1595: .getRelatedNode());
1596: if (h != null) {
1597: try {
1598: h.handleDOMNodeInsertedEvent(me);
1599: } catch (InterruptedBridgeException ibe) {
1600: /* do nothing */
1601: } catch (Exception e) {
1602: userAgent.displayError(e);
1603: }
1604: }
1605: }
1606: }
1607:
1608: /**
1609: * The DOM EventListener invoked when a node is removed.
1610: */
1611: protected class DOMNodeRemovedEventListener implements
1612: EventListener {
1613:
1614: /**
1615: * Creates a new DOMNodeRemovedEventListener.
1616: */
1617: public DOMNodeRemovedEventListener() {
1618: }
1619:
1620: /**
1621: * Handles 'DOMNodeRemoved' event type.
1622: */
1623: public void handleEvent(Event evt) {
1624: Node node = (Node) evt.getTarget();
1625: BridgeUpdateHandler h = getBridgeUpdateHandler(node);
1626: if (h != null) {
1627: try {
1628: h.handleDOMNodeRemovedEvent((MutationEvent) evt);
1629: } catch (Exception e) {
1630: userAgent.displayError(e);
1631: }
1632: }
1633: }
1634: }
1635:
1636: /**
1637: * The DOM EventListener invoked when a character data is changed.
1638: */
1639: protected class DOMCharacterDataModifiedEventListener implements
1640: EventListener {
1641:
1642: /**
1643: * Creates a new DOMCharacterDataModifiedEventListener.
1644: */
1645: public DOMCharacterDataModifiedEventListener() {
1646: }
1647:
1648: /**
1649: * Handles 'DOMCharacterDataModified' event type.
1650: */
1651: public void handleEvent(Event evt) {
1652: Node node = (Node) evt.getTarget();
1653: while (node != null && !(node instanceof SVGOMElement)) {
1654: node = (Node) ((AbstractNode) node)
1655: .getParentNodeEventTarget();
1656: }
1657: BridgeUpdateHandler h = getBridgeUpdateHandler(node);
1658: if (h != null) {
1659: try {
1660: h
1661: .handleDOMCharacterDataModified((MutationEvent) evt);
1662: } catch (Exception e) {
1663: userAgent.displayError(e);
1664: }
1665: }
1666: }
1667: }
1668:
1669: /**
1670: * The CSSEngineListener invoked when CSS properties are modified
1671: * on a particular element.
1672: */
1673: protected class CSSPropertiesChangedListener implements
1674: CSSEngineListener {
1675:
1676: /**
1677: * Creates a new CSSPropertiesChangedListener.
1678: */
1679: public CSSPropertiesChangedListener() {
1680: }
1681:
1682: /**
1683: * Handles CSSEngineEvent that describes the CSS properties
1684: * that have changed on a particular element.
1685: */
1686: public void propertiesChanged(CSSEngineEvent evt) {
1687: Element elem = evt.getElement();
1688: SVGContext ctx = getSVGContext(elem);
1689: if (ctx == null) {
1690: GraphicsNode pgn = getGraphicsNode((Element) elem
1691: .getParentNode());
1692: if ((pgn == null)
1693: || !(pgn instanceof CompositeGraphicsNode)) {
1694: // Something changed in this element but we really don't
1695: // care since its parent isn't displayed either.
1696: return;
1697: }
1698: CompositeGraphicsNode parent = (CompositeGraphicsNode) pgn;
1699: // Check if 'display' changed on this element.
1700:
1701: int[] properties = evt.getProperties();
1702: for (int i = 0; i < properties.length; ++i) {
1703: if (properties[i] == SVGCSSEngine.DISPLAY_INDEX) {
1704: if (!CSSUtilities.convertDisplay(elem)) {
1705: // (Still) Not displayed
1706: break;
1707: }
1708: // build the graphics node
1709: GVTBuilder builder = getGVTBuilder();
1710: GraphicsNode childNode = builder.build(
1711: BridgeContext.this , elem);
1712: if (childNode == null) {
1713: // the added element is not a graphic element?
1714: break;
1715: }
1716: int idx = -1;
1717: for (Node ps = elem.getPreviousSibling(); ps != null; ps = ps
1718: .getPreviousSibling()) {
1719: if (ps.getNodeType() != Node.ELEMENT_NODE)
1720: continue;
1721: Element pse = (Element) ps;
1722: GraphicsNode gn = getGraphicsNode(pse);
1723: if (gn == null)
1724: continue;
1725: idx = parent.indexOf(gn);
1726: if (idx == -1)
1727: continue;
1728: break;
1729: }
1730: // insert after prevSibling, if
1731: // it was -1 this becomes 0 (first slot)
1732: idx++;
1733: parent.add(idx, childNode);
1734: break;
1735: }
1736: }
1737: }
1738: if (ctx != null && (ctx instanceof BridgeUpdateHandler)) {
1739: ((BridgeUpdateHandler) ctx).handleCSSEngineEvent(evt);
1740: }
1741: }
1742: }
1743:
1744: /**
1745: * A listener class for changes to animated attributes in the document.
1746: */
1747: protected class AnimatedAttrListener implements
1748: AnimatedAttributeListener {
1749:
1750: /**
1751: * Creates a new AnimatedAttributeListener.
1752: */
1753: public AnimatedAttrListener() {
1754: }
1755:
1756: /**
1757: * Called to notify an object of a change to the animated value of
1758: * an animated XML attribute.
1759: * @param e the owner element of the changed animated attribute
1760: * @param alav the AnimatedLiveAttributeValue that changed
1761: */
1762: public void animatedAttributeChanged(Element e,
1763: AnimatedLiveAttributeValue alav) {
1764: BridgeUpdateHandler h = getBridgeUpdateHandler(e);
1765: if (h != null) {
1766: try {
1767: h.handleAnimatedAttributeChanged(alav);
1768: } catch (Exception ex) {
1769: userAgent.displayError(ex);
1770: }
1771: }
1772: }
1773:
1774: /**
1775: * Called to notify an object of a change to the value of an 'other'
1776: * animation.
1777: * @param e the element being animated
1778: * @param type the type of animation whose value changed
1779: */
1780: public void otherAnimationChanged(Element e, String type) {
1781: BridgeUpdateHandler h = getBridgeUpdateHandler(e);
1782: if (h != null) {
1783: try {
1784: h.handleOtherAnimationChanged(type);
1785: } catch (Exception ex) {
1786: userAgent.displayError(ex);
1787: }
1788: }
1789: }
1790: }
1791:
1792: // CSS context ////////////////////////////////////////////////////////////
1793:
1794: /**
1795: * Returns the Value corresponding to the given system color.
1796: */
1797: public Value getSystemColor(String ident) {
1798: return SystemColorSupport.getSystemColor(ident);
1799: }
1800:
1801: /**
1802: * Returns the value corresponding to the default font.
1803: */
1804: public Value getDefaultFontFamily() {
1805: // No cache needed since the default font family is asked only
1806: // one time on the root element (only if it does not have its
1807: // own font-family).
1808: SVGOMDocument doc = (SVGOMDocument) document;
1809: SVGStylableElement root = (SVGStylableElement) doc
1810: .getRootElement();
1811: String str = userAgent.getDefaultFontFamily();
1812: return doc.getCSSEngine().parsePropertyValue(root,
1813: SVGConstants.CSS_FONT_FAMILY_PROPERTY, str);
1814: }
1815:
1816: /**
1817: * Returns a lighter font-weight.
1818: */
1819: public float getLighterFontWeight(float f) {
1820: return userAgent.getLighterFontWeight(f);
1821: }
1822:
1823: /**
1824: * Returns a bolder font-weight.
1825: */
1826: public float getBolderFontWeight(float f) {
1827: return userAgent.getBolderFontWeight(f);
1828: }
1829:
1830: /**
1831: * Returns the size of a px CSS unit in millimeters.
1832: */
1833: public float getPixelUnitToMillimeter() {
1834: return userAgent.getPixelUnitToMillimeter();
1835: }
1836:
1837: /**
1838: * Returns the size of a px CSS unit in millimeters.
1839: * This will be removed after next release.
1840: * @see #getPixelUnitToMillimeter()
1841: */
1842: public float getPixelToMillimeter() {
1843: return getPixelUnitToMillimeter();
1844:
1845: }
1846:
1847: /**
1848: * Returns the medium font size.
1849: */
1850: public float getMediumFontSize() {
1851: return userAgent.getMediumFontSize();
1852: }
1853:
1854: /**
1855: * Returns the width of the block which directly contains the
1856: * given element.
1857: */
1858: public float getBlockWidth(Element elt) {
1859: return getViewport(elt).getWidth();
1860: }
1861:
1862: /**
1863: * Returns the height of the block which directly contains the
1864: * given element.
1865: */
1866: public float getBlockHeight(Element elt) {
1867: return getViewport(elt).getHeight();
1868: }
1869:
1870: /**
1871: * This method throws a SecurityException if the resource
1872: * found at url and referenced from docURL
1873: * should not be loaded.
1874: *
1875: * This is a convenience method to call checkLoadExternalResource
1876: * on the ExternalResourceSecurity strategy returned by
1877: * getExternalResourceSecurity.
1878: *
1879: * @param resourceURL url for the script, as defined in
1880: * the resource's xlink:href attribute. If that
1881: * attribute was empty, then this parameter should
1882: * be null
1883: * @param docURL url for the document into which the
1884: * resource was found.
1885: */
1886: public void checkLoadExternalResource(ParsedURL resourceURL,
1887: ParsedURL docURL) throws SecurityException {
1888: userAgent.checkLoadExternalResource(resourceURL, docURL);
1889: }
1890:
1891: /**
1892: * Tells whether the given SVG document is dynamic.
1893: */
1894: public boolean isDynamicDocument(Document doc) {
1895: return BaseScriptingEnvironment.isDynamicDocument(this , doc);
1896: }
1897:
1898: /**
1899: * Tells whether the given SVG document is Interactive.
1900: * We say it is, if it has any <title>, <desc>, or <a> elements,
1901: * of if the 'cursor' property is anything but Auto on any element.
1902: */
1903: public boolean isInteractiveDocument(Document doc) {
1904:
1905: Element root = ((SVGDocument) doc).getRootElement();
1906: if (!SVGConstants.SVG_NAMESPACE_URI.equals(root
1907: .getNamespaceURI()))
1908: return false;
1909:
1910: return checkInteractiveElement(root);
1911: }
1912:
1913: /**
1914: * used by isInteractiveDocument to check if document
1915: * contains any 'interactive' elements.
1916: */
1917: public boolean checkInteractiveElement(Element e) {
1918: return checkInteractiveElement((SVGDocument) e
1919: .getOwnerDocument(), e);
1920: }
1921:
1922: /**
1923: * used by isInteractiveDocument to check if document
1924: * contains any 'interactive' elements.
1925: */
1926: public boolean checkInteractiveElement(SVGDocument doc, Element e) {
1927: String tag = e.getLocalName();
1928:
1929: // Check if it's one of our important element.
1930: if (SVGConstants.SVG_A_TAG.equals(tag))
1931: return true;
1932:
1933: // This is a bit of a hack but don't count
1934: // title and desc as children of root SVG since
1935: // we don't show tool tips for them anyways.
1936: if (SVGConstants.SVG_TITLE_TAG.equals(tag)) {
1937: return (e.getParentNode() != doc.getRootElement());
1938: }
1939: if (SVGConstants.SVG_DESC_TAG.equals(tag)) {
1940: return (e.getParentNode() != doc.getRootElement());
1941: }
1942: if (SVGConstants.SVG_CURSOR_TAG.equals(tag))
1943: return true;
1944:
1945: // I am well aware that this is not 100% accurate but it's
1946: // the best I can do w/o booting the CSSEngine.
1947: if (e.getAttribute(CSSConstants.CSS_CURSOR_PROPERTY).length() > 0)
1948: return true;
1949:
1950: /* We would like to do this but the CSS Engine isn't setup when
1951: we want to do this.
1952:
1953: // Check if cursor property is set to something other than 'auto'.
1954: Value cursorValue = CSSUtilities.getComputedStyle
1955: (e, SVGCSSEngine.CURSOR_INDEX);
1956: if ((cursorValue != null) &&
1957: (cursorValue.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) &&
1958: (cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) &&
1959: (SVGConstants.SVG_AUTO_VALUE.equals(cursorValue.getStringValue())))
1960: return true;
1961: */
1962:
1963: // Check all the child elements for any of the above.
1964: final String svg_ns = SVGConstants.SVG_NAMESPACE_URI;
1965: for (Node n = e.getFirstChild(); n != null; n = n
1966: .getNextSibling()) {
1967: if (n.getNodeType() == Node.ELEMENT_NODE) {
1968: Element child = (Element) n;
1969: if (svg_ns.equals(child.getNamespaceURI()))
1970: if (checkInteractiveElement(child))
1971: return true;
1972: }
1973: }
1974: return false;
1975: }
1976:
1977: /**
1978: * Sets the animation limiting mode to "none".
1979: */
1980: public void setAnimationLimitingNone() {
1981: animationLimitingMode = 0;
1982: if (animationEngine != null) {
1983: setAnimationLimitingMode();
1984: }
1985: }
1986:
1987: /**
1988: * Sets the animation limiting mode to a percentage of CPU.
1989: * @param pc the maximum percentage of CPU to use (0 < pc ≤ 1)
1990: */
1991: public void setAnimationLimitingCPU(float pc) {
1992: animationLimitingMode = 1;
1993: animationLimitingAmount = pc;
1994: if (animationEngine != null) {
1995: setAnimationLimitingMode();
1996: }
1997: }
1998:
1999: /**
2000: * Sets the animation limiting mode to a number of frames per second.
2001: * @param fps the maximum number of frames per second (fps > 0)
2002: */
2003: public void setAnimationLimitingFPS(float fps) {
2004: animationLimitingMode = 2;
2005: animationLimitingAmount = fps;
2006: if (animationEngine != null) {
2007: setAnimationLimitingMode();
2008: }
2009: }
2010:
2011: /**
2012: * Set the animationg limiting mode on the animation engine.
2013: */
2014: protected void setAnimationLimitingMode() {
2015: switch (animationLimitingMode) {
2016: case 0: // unlimited
2017: animationEngine.setAnimationLimitingNone();
2018: break;
2019: case 1: // %cpu
2020: animationEngine
2021: .setAnimationLimitingCPU(animationLimitingAmount);
2022: break;
2023: case 2: // fps
2024: animationEngine
2025: .setAnimationLimitingFPS(animationLimitingAmount);
2026: break;
2027: }
2028: }
2029:
2030: // bridge extensions support //////////////////////////////////////////////
2031:
2032: protected List extensions = null;
2033:
2034: /**
2035: * Registers the bridges to handle SVG 1.0 elements.
2036: */
2037: public void registerSVGBridges() {
2038: UserAgent ua = getUserAgent();
2039: List ext = getBridgeExtensions(document);
2040: Iterator iter = ext.iterator();
2041:
2042: while (iter.hasNext()) {
2043: BridgeExtension be = (BridgeExtension) iter.next();
2044: be.registerTags(this );
2045: ua.registerExtension(be);
2046: }
2047: }
2048:
2049: public List getBridgeExtensions(Document doc) {
2050: Element root = ((SVGOMDocument) doc).getRootElement();
2051: String ver = root.getAttributeNS(null,
2052: SVGConstants.SVG_VERSION_ATTRIBUTE);
2053: BridgeExtension svgBE;
2054: if ((ver.length() == 0) || ver.equals("1.0")
2055: || ver.equals("1.1"))
2056: svgBE = new SVGBridgeExtension();
2057: else
2058: svgBE = new SVG12BridgeExtension();
2059:
2060: float priority = svgBE.getPriority();
2061: extensions = new LinkedList(getGlobalBridgeExtensions());
2062:
2063: ListIterator li = extensions.listIterator();
2064: for (;;) {
2065: if (!li.hasNext()) {
2066: li.add(svgBE);
2067: break;
2068: }
2069: BridgeExtension lbe = (BridgeExtension) li.next();
2070: if (lbe.getPriority() > priority) {
2071: li.previous();
2072: li.add(svgBE);
2073: break;
2074: }
2075: }
2076:
2077: return extensions;
2078: }
2079:
2080: /**
2081: * Returns the extensions supported by this bridge context.
2082: */
2083: protected static List globalExtensions = null;
2084:
2085: public static synchronized List getGlobalBridgeExtensions() {
2086: if (globalExtensions != null) {
2087: return globalExtensions;
2088: }
2089: globalExtensions = new LinkedList();
2090:
2091: Iterator iter = Service.providers(BridgeExtension.class);
2092:
2093: while (iter.hasNext()) {
2094: BridgeExtension be = (BridgeExtension) iter.next();
2095: float priority = be.getPriority();
2096: ListIterator li = globalExtensions.listIterator();
2097: for (;;) {
2098: if (!li.hasNext()) {
2099: li.add(be);
2100: break;
2101: }
2102: BridgeExtension lbe = (BridgeExtension) li.next();
2103: if (lbe.getPriority() > priority) {
2104: li.previous();
2105: li.add(be);
2106: break;
2107: }
2108: }
2109: }
2110: return globalExtensions;
2111: }
2112:
2113: public static class CSSEngineUserAgentWrapper implements
2114: CSSEngineUserAgent {
2115: UserAgent ua;
2116:
2117: CSSEngineUserAgentWrapper(UserAgent ua) {
2118: this .ua = ua;
2119: }
2120:
2121: /**
2122: * Displays an error resulting from the specified Exception.
2123: */
2124: public void displayError(Exception ex) {
2125: ua.displayError(ex);
2126: }
2127:
2128: /**
2129: * Displays a message in the User Agent interface.
2130: */
2131: public void displayMessage(String message) {
2132: ua.displayMessage(message);
2133: }
2134: }
2135:
2136: }
|