jkesselm 00/12/15 10:03:42
Modified: java/src/org/apache/xpath DOMHelper.java
Log:
Improved parent-of-Attr lookup (trust the DOM), some docs.
The code changes seem to have fixed one known bug, and
should run faster.
Revision Changes Path
1.17 +134 -62 xml-xalan/java/src/org/apache/xpath/DOMHelper.java
Index: DOMHelper.java
===================================================================
RCS file: /home/cvs/xml-xalan/java/src/org/apache/xpath/DOMHelper.java,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- DOMHelper.java 2000/12/15 15:55:11 1.16
+++ DOMHelper.java 2000/12/15 18:03:41 1.17
@@ -572,11 +572,16 @@
protected Vector m_candidateNoAncestorXMLNS = new Vector();
/**
- * Returns the namespace of the given node.
- *
- * NEEDSDOC @param n
- *
- * NEEDSDOC ($objectName$) @return
+ * Returns the namespace of the given node. Differs from simply getting
+ * the node's prefix and using getNamespaceForPrefix in that it attempts
+ * to cache some of the data in NSINFO objects, to avoid repeated lookup.
+ * TODO: Should we consider moving that logic into getNamespaceForPrefix?
+ *
+ * @param n Node to be examined.
+ *
+ * @return String containing the Namespace Name (uri) for this node.
+ * Note that this is undefined for any nodes other than Elements and
+ * Attributes.
*/
public String getNamespaceOfNode(Node n)
{
@@ -777,11 +782,13 @@
}
/**
- * Returns the local name of the given node.
+ * Returns the local name of the given node. If the node's name begins
+ * with a namespace prefix, this is the part after the colon; otherwise
+ * it's the full node name.
*
- * NEEDSDOC @param n
+ * @param n the node to be examined.
*
- * NEEDSDOC ($objectName$) @return
+ * @return String containing the Local Name
*/
public String getLocalNameOfNode(Node n)
{
@@ -793,11 +800,16 @@
}
/**
- * Returns the element name with the namespace expanded.
- *
- * NEEDSDOC @param elem
- *
- * NEEDSDOC ($objectName$) @return
+ * Returns the element name with the namespace prefix (if any) replaced
+ * by the Namespace URI it was bound to. This is not a standard
+ * representation of a node name, but it allows convenient
+ * single-string comparison of the "universal" names of two nodes.
+ *
+ * @param elem Element to be examined.
+ *
+ * @return String in the form "namespaceURI:localname" if the node
+ * belongs to a namespace, or simply "localname" if it doesn't.
+ * @see getExpandedAttributeName
*/
public String getExpandedElementName(Element elem)
{
@@ -810,11 +822,16 @@
}
/**
- * Returns the attribute name with the namespace expanded.
- *
- * NEEDSDOC @param attr
- *
- * NEEDSDOC ($objectName$) @return
+ * Returns the attribute name with the namespace prefix (if any) replaced
+ * by the Namespace URI it was bound to. This is not a standard
+ * representation of a node name, but it allows convenient
+ * single-string comparison of the "universal" names of two nodes.
+ *
+ * @param attr Attr to be examined
+ *
+ * @return String in the form "namespaceURI:localname" if the node
+ * belongs to a namespace, or simply "localname" if it doesn't.
+ * @see getExpandedElementName
*/
public String getExpandedAttributeName(Attr attr)
{
@@ -831,7 +848,10 @@
//==========================================================
/**
- * Tell if the node is ignorable whitespace.
+ * Tell if the node is ignorable whitespace. Note that this can
+ * be determined only in the context of a DTD or other Schema,
+ * and that DOM Level 2 has nostandardized DOM API which can
+ * return that information.
* @deprecated
*
* NEEDSDOC @param node
@@ -846,6 +866,9 @@
// TODO: I can probably do something to figure out if this
// space is ignorable from just the information in
// the DOM tree.
+ // -- You need to be able to distinguish whitespace
+ // that is #PCDATA from whitespace that isn't. That requires
+ // DTD support, which won't be standardized until DOM Level 3.
return isIgnorable;
}
@@ -853,9 +876,9 @@
* Get the first unparented node in the ancestor chain.
* @deprecated
*
- * NEEDSDOC @param node
+ * @param node Starting node, to specify which chain to chase
*
- * NEEDSDOC ($objectName$) @return
+ * @return the topmost ancestor.
*/
public Node getRoot(Node node)
{
@@ -874,10 +897,20 @@
/**
* Get the root node of the document tree, regardless of
* whether or not the node passed in is a document node.
- *
- * NEEDSDOC @param n
+ * <p>
+ * TODO: This doesn't handle DocumentFragments or "orphaned" subtrees
+ * -- it's currently returning ownerDocument even when the tree is
+ * not actually part of the main Document tree. We should either
+ * rewrite the description to say that it finds the Document node,
+ * or change the code to walk up the ancestor chain.
+
*
- * NEEDSDOC ($objectName$) @return
+ * @param n Node to be examined
+ *
+ * @return the Document node. Note that this is not the correct answer
+ * if n was (or was a child of) a DocumentFragment or an orphaned node,
+ * as can arise if the DOM has been edited rather than being generated
+ * by a parser.
*/
public Node getRootNode(Node n)
{
@@ -887,11 +920,14 @@
}
/**
- * Tell if the given node is a namespace decl node.
+ * Test whether the given node is a namespace decl node. In DOM Level 2
+ * this can be done in a namespace-aware manner, but in Level 1 DOMs
+ * it has to be done by testing the node name.
*
- * NEEDSDOC @param n
+ * @param n Node to be examined.
*
- * NEEDSDOC ($objectName$) @return
+ * @return boolean -- true iff the node is an Attr whose name is
+ * "xmlns" or has the "xmlns:" prefix.
*/
public boolean isNamespaceNode(Node n)
{
@@ -907,34 +943,63 @@
}
/**
- * I have to write this silly, and expensive function,
- * because the DOM WG decided that attributes don't
- * have parents. If Xalan is used with a DOM implementation
- * that reuses attribute nodes, this will not work correctly.
- *
- * NEEDSDOC @param node
- *
- * NEEDSDOC ($objectName$) @return
- *
- * @throws RuntimeException
+ * Obtain the XPath-model parent of a DOM node -- ownerElement for Attrs,
+ * parent for other nodes.
+ * <p>
+ * Background: The DOM believes that you must be your Parent's
+ * Child, and thus Attrs don't have parents. XPath said that Attrs
+ * do have their owning Element as their parent. This function
+ * bridges the difference, either by using the DOM Level 2 ownerElement
+ * function or by using a "silly and expensive function" in Level 1
+ * DOMs.
+ * <p>
+ * (There's some discussion of future DOMs generalizing ownerElement
+ * into ownerNode and making it work on all types of nodes. This
+ * still wouldn't help the users of Level 1 or Level 2 DOMs)
+ * <p>
+ *
+ * @param node Node whose XPath parent we want to obtain
+ *
+ * @return the parent of the node, or the ownerElement if it's an
+ * Attr node, or null if the node is an orphan.
+ *
+ * @throws RuntimeException if the Document has no root element.
+ * This can't arise if the Document was created
+ * via the DOM Level 2 factory methods, but is possible if other
+ * mechanisms were used to obtain it
*/
public Node getParentOfNode(Node node) throws RuntimeException
{
-
Node parent;
short nodeType = node.getNodeType();
if (Node.ATTRIBUTE_NODE == nodeType)
{
Document doc = node.getOwnerDocument();
-
- /*
+ /*
TBD:
if(null == doc)
{
throw new
RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CHILD_HAS_NO_OWNER_DOCUMENT,
null));//"Attribute child does not have an owner document!");
}
*/
+
+ // Given how expensive the tree walk may be, we should first ask
+ // whether this DOM can answer the question for us. The additional
+ // test does slow down Level 1 DOMs slightly... should/do
+ // we have a DOM2Helper, deciding between them when we first connect?
+ //
+ // (Shouldn't have to check whether impl is null in a compliant DOM,
+ // but let's be paranoid for a moment...)
+ DOMImplementation impl=doc.getImplementation();
+ if(impl!=null && impl.hasFeature("Core","2.0"))
+ {
+ parent=((Attr)node).getOwnerElement();
+ return parent;
+ }
+
+ // DOM Level 1 solution, as fallback. Hugely expensive.
+
Element rootElem = doc.getDocumentElement();
if (null == rootElem)
@@ -946,7 +1011,8 @@
}
parent = locateAttrParent(rootElem, node);
- }
+
+ }
else
{
parent = node.getParentNode();
@@ -1031,33 +1097,39 @@
}
/**
- * Support for getParentOfNode.
- *
- * NEEDSDOC @param elem
- * NEEDSDOC @param attr
- *
- * NEEDSDOC ($objectName$) @return
+ * Support for getParentOfNode; walks a DOM tree until it finds
+ * the Element which owns the Attr. This is hugely expensive, and
+ * if at all possible you should use the DOM Level 2 Attr.ownerElement()
+ * method instead.
+ * <p>
+ * The DOM Level 1 developers expected that folks would keep track
+ * of the last Element they'd seen and could recover the info from
+ * that source. Obviously that doesn't work very well if the only
+ * information you've been presented with is the Attr. The DOM Level 2
+ * getOwnerElement() method fixes that, but only for Level 2 and
+ * later DOMs.
+ *
+ * @param elem Element whose subtree is to be searched for this Attr
+ * @param attr Attr whose owner is to be located.
+ *
+ * @return the first Element whose attribute list includes the provided
+ * attr. In modern DOMs, this will also be the only such Element. (Early
+ * DOMs had some hope that Attrs might be sharable, but this idea has
+ * been abandoned.)
*/
private Node locateAttrParent(Element elem, Node attr)
{
Node parent = null;
- NamedNodeMap attrs = elem.getAttributes();
-
- if (null != attrs)
- {
- int nAttrs = attrs.getLength();
- for (int i = 0; i < nAttrs; i++)
- {
- if (attr == attrs.item(i))
- {
- parent = elem;
-
- break;
- }
- }
- }
+ // This should only be called for Level 1 DOMs, so we don't have to
+ // worry about namespace issues. In later levels, it's possible
+ // for a DOM to have two Attrs with the same NodeName but
+ // different namespaces, and we'd need to get getAttributeNodeNS...
+ // but later levels also have Attr.getOwnerElement.
+ Attr check=elem.getAttributeNode(attr.getNodeName());
+ if(check==attr)
+ parent = elem;
if (null == parent)
{