package client.net.sf.saxon.ce.dom; import client.net.sf.saxon.ce.Configuration; import client.net.sf.saxon.ce.lib.NamespaceConstant; import client.net.sf.saxon.ce.om.Axis; import client.net.sf.saxon.ce.om.DocumentInfo; import client.net.sf.saxon.ce.om.NamePool; import client.net.sf.saxon.ce.om.NamespaceBinding; import client.net.sf.saxon.ce.om.NodeInfo; import client.net.sf.saxon.ce.om.StandardNames; import client.net.sf.saxon.ce.pattern.NodeKindTest; import client.net.sf.saxon.ce.tree.iter.AxisIterator; import client.net.sf.saxon.ce.type.Type; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Node; import java.util.HashMap; /** * The document node of a tree implemented as a wrapper around an XML DOM Document. */ public class HTMLDocumentWrapper extends HTMLNodeWrapper implements DocumentInfo { protected Configuration config; protected String baseURI; protected int documentNumber; protected boolean domLevel3; private HashMap<String, Object> userData; private HashMap<String, HTMLNodeWrapper> idIndex; private boolean isHttpRequested; /** * Wrap a DOM Document or DocumentFragment node * @param doc a DOM Document or DocumentFragment node * @param baseURI the base URI of the document * @param config the Saxon configuration */ public HTMLDocumentWrapper(Node doc, String baseURI, Configuration config) { this(doc, baseURI, config, DocType.UNKNOWN); } public final native String getBaseURI(Document doc) /*-{ if (doc.documentURI) { return doc.doumentURI; } else { return null; } }-*/; public HTMLDocumentWrapper(Node doc, String baseURI, Configuration config, DocType newDocType) { super(doc, null, 0); // if (doc.getNodeType() != Node.DOCUMENT_NODE) { // throw new IllegalArgumentException("Node must be a DOM Document"); // } nodeKind = Type.DOCUMENT; if ((baseURI == null || baseURI == "") && doc.getNodeType() == Type.DOCUMENT) { baseURI = ((Document)doc).getURL(); this.baseURI = (baseURI != null && baseURI != "")? baseURI : getBaseURI((Document)doc); } else { this.baseURI = baseURI; } // affects selectID() behaviour: isHttpRequested = (newDocType == DocType.NONHTML); docWrapper = this; domLevel3 = true; setConfiguration(config); // crude test for XHTML by seeing if there's an identifying xmlns attribute on any // top-level html element if (newDocType != DocType.UNKNOWN) { this.htmlType = newDocType; return; } // need to determine whether HTML, XHTML or neither so as to control case of node names // and distinguish other XML/HTML behaviours - like SelectID try { AxisIterator iter = this.iterateAxis(Axis.CHILD); while (true) { NodeInfo n = (NodeInfo)iter.next(); if (n == null) { break; } else if (n.getNodeKind() == Type.ELEMENT) { // String uri = n.getURI(); // if (uri != null && uri.length() > 0) { // htmlType = (uri.equals(NamespaceConstant.XHTML))? DocType.XHTML : DocType.NONHTML; // return; // } String rawLocal = ((HTMLNodeWrapper)n).getRawLocalName().toLowerCase(); if (rawLocal.equals("html")) { NamespaceBinding[] nb = n.getDeclaredNamespaces(null); htmlType = DocType.HTML; for (NamespaceBinding nBinding: nb) { if (nBinding.getURI().equals(NamespaceConstant.XHTML)) { htmlType = DocType.XHTML; break; } } } else { htmlType = DocType.NONHTML; } break; // only check first element } } } catch(Exception e) {} } private DocType htmlType = DocType.UNKNOWN; public enum DocType { XHTML, HTML, UNKNOWN, NONHTML } private boolean getIsXMLObjectTypeFromURIext() { int pos = baseURI.indexOf('?'); String testUri = (pos < 0)? baseURI : baseURI.substring(pos); return testUri.toLowerCase().endsWith("xhtml"); } /** * @return type of document determined by tag name */ public DocType getDocType() { return htmlType; } /** * @return type of document determined by object type */ public boolean isXMLDocumentObject() { return isHttpRequested; } // does not seem to be a prescriptive way of distinguishing object types private boolean isNodeXMLDocument() { String nodeString = node.toString(); if (nodeString.endsWith("XMLDocument]")) { return true; } else if(nodeString.endsWith("HTMLDocument]")){ return false; } else { return !supportsGetElementById((Document)node); } } public static native boolean supportsGetElementById(Document doc) /*-{ return !!(doc.getElementById); }-*/; /** * Create a wrapper for a node in this document * * @param node the DOM node to be wrapped. This must be a node within the document wrapped by this * XMLDocumentWrapper * @throws IllegalArgumentException if the node is not a descendant of the Document node wrapped by * this XMLDocumentWrapper */ public HTMLNodeWrapper wrap(Node node) { if (node == this.node) { return this; } Document doc = node.getOwnerDocument(); if (doc == this.node) { return makeWrapper(node, this); } else { throw new IllegalArgumentException( "XMLDocumentWrapper#wrap: supplied node does not belong to the wrapped DOM document"); } } /** * Set the Configuration that contains this document */ public void setConfiguration(Configuration config) { this.config = config; documentNumber = config.getDocumentNumberAllocator().allocateDocumentNumber(); } /** * Get the configuration previously set using setConfiguration */ public Configuration getConfiguration() { return config; } /** * Get the name pool used for the names in this document */ public NamePool getNamePool() { return config.getNamePool(); } /** * Get the unique document number */ public int getDocumentNumber() { return documentNumber; } /** * Get the element with a given ID, if any * * @param id the required ID value * @return a NodeInfo representing the element with the given ID, or null if there * is no such element. This relies on the getElementById() method in the * underlying DOM. */ public NodeInfo selectID(String id) { Node el; // IE does not support getElementById for XML documents // but it does support it for XHTML if its the host page Document doc = ((Document)node); if (!isHttpRequested) { el = (doc).getElementById(id); if (el == null) { return null; } return wrap(el); } else { if (idIndex != null) { return idIndex.get(id); } else { idIndex = new HashMap(); AxisIterator iter = iterateAxis(Axis.DESCENDANT, NodeKindTest.ELEMENT); boolean useNS = isNSok(node); while (true) { NodeInfo node = (NodeInfo)iter.next(); if (node == null) { break; } Node testNode = (Node)((HTMLNodeWrapper)node).getUnderlyingNode(); String xmlId = (useNS)? getXmlIdNS(testNode) : getXmlId(testNode); //String xmlId = ((Element)((HTMLNodeWrapper)node).getUnderlyingNode()).getAttribute("xml:id"); if (xmlId != null && !xmlId.isEmpty()) { idIndex.put(xmlId, (HTMLNodeWrapper)node); } } return idIndex.get(id); } } } public static native String getXmlIdNS(Node inNode) /*-{ return inNode.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'id'); }-*/; public static native String getXmlId(Node inNode) /*-{ return inNode.getAttribute("xml:id"); }-*/; private static native boolean isNSok(Node inNode) /*-{ return !!(inNode.getAttributeNS); }-*/; /** * Determine whether this is the same node as another node. <br /> * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b) * * @return true if this Node object and the supplied Node object represent the * same node in the tree. */ public boolean isSameNodeInfo(NodeInfo other) { return other instanceof HTMLDocumentWrapper && node == ((HTMLDocumentWrapper)other).node; } /** * Get the type annotation. Always XS_UNTYPED. */ public int getTypeAnnotation() { return StandardNames.XS_UNTYPED; } /** * Set user data on the document node. The user data can be retrieved subsequently * using {@link #getUserData} * @param key A string giving the name of the property to be set. Clients are responsible * for choosing a key that is likely to be unique. Must not be null. * @param value The value to be set for the property. May be null, which effectively * removes the existing value for the property. */ public void setUserData(String key, Object value) { if (userData == null) { userData = new HashMap(4); } if (value == null) { userData.remove(key); } else { userData.put(key, value); } } /** * Get user data held in the document node. This retrieves properties previously set using * {@link #setUserData} * @param key A string giving the name of the property to be retrieved. * @return the value of the property, or null if the property has not been defined. */ public Object getUserData(String key) { if (userData == null) { return null; } else { return userData.get(key); } } /** * Create a DocumentFragment node. Method not available from GWT */ public static native Node createDocumentFragment(Document doc) /*-{ return doc.createDocumentFragment(); }-*/; } // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.