Skip to Content.
Sympa Menu

xom-interest - Re: [XOM-interest] XOM 1.1: XPath

xom-interest AT lists.ibiblio.org

Subject: XOM API for Processing XML with Java

List archive

Chronological Thread  
  • From: Wolfgang Hoschek <whoschek AT lbl.gov>
  • To: Elliotte Harold <elharo AT metalab.unc.edu>
  • Cc: "'Bradley S. Huffman'" <hip AT a.cs.okstate.edu>, xom-interest AT lists.ibiblio.org
  • Subject: Re: [XOM-interest] XOM 1.1: XPath
  • Date: Sun, 9 Jan 2005 01:27:18 -0800

On Jan 8, 2005, at 4:31 PM, Elliotte Harold wrote:

Wolfgang Hoschek wrote:


Either that (output 1), or a list with a single XOM text node having no parent, containing the concatenation of t1, t2 and t3 (output 2). The problem is that neither output is satisfactory from a certain angle. The only really satisfactory output would be a single XOM text node having the parent of t1, t2 and t3, containing the concatenation of t1, t2 and t3 (output 3). But output 3 isn't possible with XOM unless the texts were merged before passing them into XPath because XPath is a read-only model.
A user might expect output 1, 2 or 3 when matching on text = "helloworldnux", depending how he looks at the problem. What do we do? Any ideas what other engines output when running over external tree models?

I disagree. I think output 1 is clearly correct. Anything else is suboptimal. If a user is confused by output 1, then it is because they are confused by XPath and the unfortunate impedance mismatch between XPath and XOM. The more I think about this the more I'm convinced that output 1 is right.

Well, it would be helpful if you could state why this is "clearly correct".

I'll add the following method to the next Nux release candidate, unless a convincing argument can be made that one can easily do without it:

/**
* Recursively walks the given node subtree and merges runs of consecutive
* (adjacent) {@link Text} nodes (if present) into a single Text node
* containing their string concatenation. Empty Text nodes are
removed.
* The semantics are the same as with the DOM method
* {@link org.w3c.dom.Node#normalize() org.w3c.dom.Node.normalize()}.
* <p>
* Note that documents build by a {@link nu.xom.Builder} are guaranteed to never
* have adjacent Text nodes. However, subsequent manual removal or insertion of nodes
* to the tree can cause Text nodes to become adjacent.
* <p>
* Text normalization is necessary to achieve fully
standards-compliant
* XPath and XQuery semantics if a query compares or extracts the value of individual
* Text nodes that (unfortunately) happens to be adjacent to other Text nodes.
* Luckily, such use cases are rare in practical real-world scenarios and thus this method is often unnecessary.
* <p>
* Example Usage:
*
* <pre>
* Element foo = new Element("foo");
* foo.appendChild("");
* foo.appendChild("bar");
* foo.appendChild("");
*
* Element elem = new Element("elem");
* elem.appendChild("");
* elem.appendChild(foo);
* elem.appendChild("hello");
* elem.appendChild("world");
* elem.appendChild(foo.copy());
* elem.appendChild("");
*
* XOMUtil.normalizeTexts(elem);
*
* for (int i = 0; i &lt; elem.getChildCount(); i++) {
* System.out.println(elem.getChild(i).toString());
* if (elem.getChild(i) instanceof Element) {
* Element nested = (Element) elem.getChild(i);
* for (int j = 0; j &lt; nested.getChildCount(); j++) {
* System.out.println(" " + nested.getChild(j).toString());
* }
* }
* }
* </pre>
*
* returns the following normalized output:
*
* <pre>
* [nu.xom.Element: foo]
* [nu.xom.Text: bar]
* [nu.xom.Text: helloworld]
* [nu.xom.Element: foo]
* [nu.xom.Text: bar]
* </pre>
*
* Copyright (c) 2003, The Regents of the University of California, through
* Lawrence Berkeley National Laboratory (subject to receipt of any required
* approvals from the U.S. Dept. of Energy). All rights reserved.
*
* @param node
* the subtree to normalize
*/
public static void normalizeTexts(ParentNode node) {
/*
* Rather efficient implementation. At least in comparison to
* nu.xom.samples.TextMerger which is not only buggy but also a textbook
* example for algorithmicly dismal performance, spending some 99% of time
* doing completely unnecessary work even if there are no
adjacent
* Texts, in addition to potential degradation to quadratic performance
* in the worst case (where all Texts are adjacent), in
addition to
* increasing tree memory footprint even if there are no adjacent Texts.
*/
for (int i=node.getChildCount(); --i >= 0; ) {
Node child = node.getChild(i);
if (child instanceof Element) { // recursively walk
the tree
normalizeTexts((Element)child);
}
else if (child instanceof Text) {
// scan to beginning of adjacent run, if any
int j = i;
while (--i >= 0 && node.getChild(i)
instanceof Text) ;

i++;
if (j == i) { // remove free-standing empty
Text node
if (child.getValue().length() == 0)
node.removeChild(i);
}
else { // found more than one adjacent Text
node; merge them
// append forwards and remove backwards to minimize memory copies of list elems
int k = i;
StringBuffer buf = new
StringBuffer(node.getChild(k++).getValue());
while (k <= j)
buf.append(node.getChild(k++).getValue());
k = j;
while (k >= i) node.removeChild(k--);

// replace run with merged Text node
unless empty
if (buf.length() > 0) node.insertChild(new Text(buf.substring(0)), i);
}
}
}
}





Archive powered by MHonArc 2.6.24.

Top of Page