[Practicalxml-commits] SF.net SVN: practicalxml:[52] trunk/src
Brought to you by:
kdgregory
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-14 17:07:49
|
Revision: 52 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=52&view=rev Author: kdgregory Date: 2008-12-14 17:07:45 +0000 (Sun, 14 Dec 2008) Log Message: ----------- DomUtil: add variant of getAbsolutePath() that takes NamespaceResolver (so path can be used) Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/DomUtil.java trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java Added Paths: ----------- trunk/src/test/java/net/sf/practicalxml/TestDomUtilGetPath.java Modified: trunk/src/main/java/net/sf/practicalxml/DomUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2008-12-14 15:15:44 UTC (rev 51) +++ trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2008-12-14 17:07:45 UTC (rev 52) @@ -13,6 +13,8 @@ import org.w3c.dom.NodeList; import org.w3c.dom.Text; +import net.sf.practicalxml.misc.NamespaceResolver; + import org.apache.commons.lang.StringUtils; @@ -439,9 +441,9 @@ /** * Returns the path from the root of the document to the specified - * element, consisting of each node's nodename separated by slashes. - * Accepts an arbitrary number of attribute names, and inserts these - * as predicates into the path nodes where they apply. + * element, consisting of each node's qualified name, separated by + * slashes. Accepts an arbitrary number of attribute names, and + * inserts these as predicates into the path nodes where they apply. * <p> * This method is meant primarily for logging and debugging. While the * returned path can by passed to an XPath evaluator, it has several @@ -449,8 +451,10 @@ * use qualified names where they appear in the document. Second, it * doesn't properly escape quotes in attribute values. Third, and most * important, it doesn't differentiate between sibling nodes with the - * same name and attribute values; if that's important, use {@link - * #getAbsolutePath}. + * same name and attribute values. + * <p> + * If you want a path that can later be used to select the element, + * see {@link #getAbsolutePath} */ public static String getPath(Element elem, String... attrNames) { @@ -465,11 +469,15 @@ * element, as an XPath expression using positional predicates to * differentiate between nodes with the same local name, ignoring * namespace. + * <p> + * <em>Warning:</em> if your document has namespaces, you will not + * be able to use the returned path to select the same node. Use + * one of the other variants of this method instead. */ public static String getAbsolutePath(Element elem) { StringBuilder sb = new StringBuilder(); - buildAbsolutePath(elem, sb, null, null); + buildAbsolutePath(elem, sb, null, null, null); return sb.toString(); } @@ -486,17 +494,55 @@ * <p> * If <code>nsLookup</code> does not have a mapping for a given * namespace, the returned path will contain a dummy prefix of the - * form "NSx", where "x" is incremented for each unknown namespace, - * whether or not the namespace has been seen elsewhere in the path. + * form "NSx", where "x" is incremented for each unknown namespace. + * In this case, you will not be able to use the returned path to + * select the element, without adding context entries for those + * generated namespaces. + * <p> + * Note that any prefixes in the source document are ignored. If an + * element has a prefix in the source document, but that element's + * namespace is not present in <code>nsLookup</code>, the path will + * contain a generated prefix. Similarly, if <code>nsLookup.getPrefix() + * </code> returns a value for the prefix, that value is used for the + * generated path. */ public static String getAbsolutePath(Element elem, NamespaceContext nsLookup) { StringBuilder sb = new StringBuilder(); - buildAbsolutePath(elem, sb, nsLookup, new int[] {0}); + buildAbsolutePath(elem, sb, nsLookup, new NamespaceResolver(), new int[] {0}); return sb.toString(); } + /** + * Returns the path from the root of the document to the specified + * element, as an XPath expression using positional predicates to + * differentiate between nodes with the same name and namespace. + * <p> + * The <code>nsLookup</code> parameter is used to retrieve prefixes + * for the passed element and its ancestors. If it does not contain + * a mapping for a given namespace, one will be added with a prefix + * of the form "NSx" (where "x" is a number that's incremented for + * each unknown namespace). + * <p> + * Note that any prefixes in the source document are ignored. If an + * element has a prefix in the source document, but that element's + * namespace is not present in <code>nsLookup</code>, the path will + * contain a generated prefix. Similarly, if <code>nsLookup.getPrefix() + * </code> returns a value for the prefix, that value is used for the + * generated path. + * <p> + * The returned path may be used to select the element, provided that + * <code>nsLookup</code> is provided as the namespace context. + */ + public static String getAbsolutePath(Element elem, NamespaceResolver nsLookup) + { + StringBuilder sb = new StringBuilder(); + buildAbsolutePath(elem, sb, nsLookup, nsLookup, new int[] {0}); + return sb.toString(); + } + + //---------------------------------------------------------------------------- // Internals //---------------------------------------------------------------------------- @@ -566,21 +612,22 @@ * @param sb A buffer used to build the path. * @param nsLookup Used to resolve defined namespaces. May be null, in * which case returned path will not have any namespaces. + * @param genLookup Holds generated namespace mappings. * @param nsCounter Used to generate prefixes for unresolved namespaces: * contains a single element that is incremented for each * unmapped namespace. */ private static void buildAbsolutePath( Element elem, StringBuilder sb, - NamespaceContext nsLookup, int[] nsCounter) + NamespaceContext nsLookup, NamespaceResolver genLookup, int[] nsCounter) { Node parent = elem.getParentNode(); if (parent instanceof Element) { - buildAbsolutePath((Element)parent, sb, nsLookup, nsCounter); + buildAbsolutePath((Element)parent, sb, nsLookup, genLookup, nsCounter); } - String prefix = getPrefix(elem, nsLookup, nsCounter); + String prefix = getPrefix(elem, nsLookup, genLookup, nsCounter); String localName = getLocalName(elem); List<Element> siblings = (nsLookup == null) ? getSiblings(elem, getLocalName(elem)) @@ -597,9 +644,14 @@ /** * Helper method for {@link #buildAbsolutePath} that returns the prefix - * for a given element, using the passed namespace resolver. + * for an element. Will first look in <code>nsLookup</code>; if it doesn't + * find the namespace there, will look in <code>genLookup</code>; if still + * unable to resolve the namespace, will use <code>nsCounter</code> to + * generate a new mapping, that's added to <code>genLookup</code>. */ - private static String getPrefix(Element elem, NamespaceContext nsLookup, int[] nsCounter) + private static String getPrefix( + Element elem, NamespaceContext nsLookup, + NamespaceResolver genLookup, int[] nsCounter) { if (nsLookup == null) return null; @@ -609,8 +661,21 @@ return null; String prefix = nsLookup.getPrefix(nsUri); - if (prefix == null) + if (prefix != null) + return prefix; + + prefix = genLookup.getPrefix(nsUri); + if (prefix != null) + return prefix; + + // make sure we don't reuse a prefix + while (prefix == null) + { prefix = "NS" + nsCounter[0]++; + if ((nsLookup.getNamespaceURI(prefix) != null) || (genLookup.getNamespaceURI(prefix) != null)) + prefix = null; + } + genLookup.addNamespace(prefix, nsUri); return prefix; } Modified: trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2008-12-14 15:15:44 UTC (rev 51) +++ trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2008-12-14 17:07:45 UTC (rev 52) @@ -2,15 +2,15 @@ import java.util.List; -import javax.xml.namespace.NamespaceContext; - import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Text; -import net.sf.practicalxml.misc.SimpleNamespaceResolver; - +/** + * Tests all DomUtil methods except <code>getPath()</code> and + * <code>getAbsolutePath()</code>. + */ public class TestDomUtil extends AbstractTestCase { @@ -309,105 +309,4 @@ // success } } - - - public void testGetPath() throws Exception - { - Element root = DomUtil.newDocument("foo"); - Element child1 = DomUtil.appendChild(root, null, "bargle"); - Element child2 = DomUtil.appendChild(root, "argle", "bargle"); - Element child3 = DomUtil.appendChild(root, "argle", "w:wargle"); - Element child1a = DomUtil.appendChild(child1, null, "zargle"); - child1.setAttribute("poi", "1234"); - child2.setAttribute("poi", "5678"); - child2.setAttribute("qwe", "asd"); - child1a.setAttribute("qwe", "zxc"); - - assertEquals("/foo", - DomUtil.getPath(root)); - assertEquals("/foo/bargle", - DomUtil.getPath(child1)); - assertEquals("/foo/bargle", - DomUtil.getPath(child2)); - assertEquals("/foo/w:wargle", - DomUtil.getPath(child3)); - assertEquals("/foo/bargle/zargle", - DomUtil.getPath(child1a)); - - assertEquals("/foo", - DomUtil.getPath(root, "poi", "qwe")); - assertEquals("/foo/bargle[poi='1234']", - DomUtil.getPath(child1, "poi", "qwe")); - assertEquals("/foo/bargle[poi='5678'][qwe='asd']", - DomUtil.getPath(child2, "poi", "qwe")); - } - - - public void testGetAbsolutePath() throws Exception - { - Element root = DomUtil.newDocument("foo"); - Element child1 = DomUtil.appendChild(root, null, "bargle"); - Element child2 = DomUtil.appendChild(root, null, "wargle"); - Element child3 = DomUtil.appendChild(root, null, "wargle"); - Element child4 = DomUtil.appendChild(root, "argle", "w:zargle"); - Element child5 = DomUtil.appendChild(root, "argle", "zargle"); - Element child6 = DomUtil.appendChild(root, "qargle", "zargle"); - Element child7 = DomUtil.appendChild(root, "argle", "zargle"); - Element child1a = DomUtil.appendChild(child1, null, "bargle"); - Element child3a = DomUtil.appendChild(child3, null, "zargle"); - Element child4a = DomUtil.appendChild(child4, null, "bargle"); - Element child4b = DomUtil.appendChild(child4, "argle", "bargle"); - - assertEquals("/foo", - DomUtil.getAbsolutePath(root)); - assertEquals("/foo/bargle", - DomUtil.getAbsolutePath(child1)); - assertEquals("/foo/bargle/bargle", - DomUtil.getAbsolutePath(child1a)); - assertEquals("/foo/wargle[1]", - DomUtil.getAbsolutePath(child2)); - assertEquals("/foo/wargle[2]", - DomUtil.getAbsolutePath(child3)); - assertEquals("/foo/wargle[2]/zargle", - DomUtil.getAbsolutePath(child3a)); - assertEquals("/foo/zargle[1]", - DomUtil.getAbsolutePath(child4)); - assertEquals("/foo/zargle[1]/bargle[1]", - DomUtil.getAbsolutePath(child4a)); - assertEquals("/foo/zargle[1]/bargle[2]", - DomUtil.getAbsolutePath(child4b)); - assertEquals("/foo/zargle[2]", - DomUtil.getAbsolutePath(child5)); - assertEquals("/foo/zargle[3]", - DomUtil.getAbsolutePath(child6)); - assertEquals("/foo/zargle[4]", - DomUtil.getAbsolutePath(child7)); - - NamespaceContext nsLookup = new SimpleNamespaceResolver("arg", "argle"); - - assertEquals("/foo", - DomUtil.getAbsolutePath(root, nsLookup)); - assertEquals("/foo/bargle", - DomUtil.getAbsolutePath(child1, nsLookup)); - assertEquals("/foo/bargle/bargle", - DomUtil.getAbsolutePath(child1a, nsLookup)); - assertEquals("/foo/wargle[1]", - DomUtil.getAbsolutePath(child2, nsLookup)); - assertEquals("/foo/wargle[2]", - DomUtil.getAbsolutePath(child3, nsLookup)); - assertEquals("/foo/wargle[2]/zargle", - DomUtil.getAbsolutePath(child3a, nsLookup)); - assertEquals("/foo/arg:zargle[1]", - DomUtil.getAbsolutePath(child4, nsLookup)); - assertEquals("/foo/arg:zargle[1]/bargle", - DomUtil.getAbsolutePath(child4a, nsLookup)); - assertEquals("/foo/arg:zargle[1]/arg:bargle", - DomUtil.getAbsolutePath(child4b, nsLookup)); - assertEquals("/foo/arg:zargle[2]", - DomUtil.getAbsolutePath(child5, nsLookup)); - assertEquals("/foo/NS0:zargle", - DomUtil.getAbsolutePath(child6, nsLookup)); - assertEquals("/foo/arg:zargle[3]", - DomUtil.getAbsolutePath(child7, nsLookup)); - } } Added: trunk/src/test/java/net/sf/practicalxml/TestDomUtilGetPath.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestDomUtilGetPath.java (rev 0) +++ trunk/src/test/java/net/sf/practicalxml/TestDomUtilGetPath.java 2008-12-14 17:07:45 UTC (rev 52) @@ -0,0 +1,205 @@ +package net.sf.practicalxml; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import net.sf.practicalxml.misc.NamespaceResolver; +import net.sf.practicalxml.misc.SimpleNamespaceResolver; + + +/** + * Tests the methods <code>getPath()</code> and <code>getAbsolutePath()</code>, + * because these have multiple tests that use the same complex DOM tree. + */ +public class TestDomUtilGetPath +extends AbstractTestCase +{ +//---------------------------------------------------------------------------- +// Setup -- no need for a method, we can do everything at initialization +// -- I think I want to keep the literal element names, rather than +// making them all constants, because I want to make sure that +// we're really testing different conditions +//---------------------------------------------------------------------------- + + Element root = DomUtil.newDocument("root"); + Element child1 = DomUtil.appendChild(root, null, "bargle"); + Element child2 = DomUtil.appendChild(root, null, "wargle"); + Element child3 = DomUtil.appendChild(root, null, "wargle"); + Element child4 = DomUtil.appendChild(root, "argle", "w:zargle"); + Element child5 = DomUtil.appendChild(root, "argle", "zargle"); + Element child6 = DomUtil.appendChild(root, "qargle", "zargle"); + Element child7 = DomUtil.appendChild(root, "argle", "zargle"); + Element child8 = DomUtil.appendChild(root, "margle", "m:zargle"); + Element child1a = DomUtil.appendChild(child1, null, "bargle"); + Element child3a = DomUtil.appendChild(child3, null, "zargle"); + Element child4a = DomUtil.appendChild(child4, null, "bargle"); + Element child4b = DomUtil.appendChild(child4, "argle", "bargle"); + Element child6a = DomUtil.appendChild(child6, "qargle", "zargle"); + + Document dom = root.getOwnerDocument(); + + Element[] allElements = new Element[] + { + root, + child1, child2, child3, child4, child5, + child6, child7, child8, + child1a, child3a, child4a, child4b, child6a + }; + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + public void testGetPath() throws Exception + { + child1.setAttribute("poi", "1234"); + child2.setAttribute("poi", "5678"); + child2.setAttribute("qwe", "asd"); + child1a.setAttribute("qwe", "zxc"); + + assertEquals("/root", + DomUtil.getPath(root)); + assertEquals("/root/bargle", + DomUtil.getPath(child1)); + assertEquals("/root/wargle", + DomUtil.getPath(child2)); + assertEquals("/root/w:zargle", + DomUtil.getPath(child4)); + assertEquals("/root/wargle/zargle", + DomUtil.getPath(child3a)); + + assertEquals("/root", + DomUtil.getPath(root, "poi", "qwe")); + assertEquals("/root/bargle[poi='1234']", + DomUtil.getPath(child1, "poi", "qwe")); + assertEquals("/root/wargle[poi='5678'][qwe='asd']", + DomUtil.getPath(child2, "poi", "qwe")); + } + + + public void testGetAbsolutePathWithoutNamespaces() throws Exception + { + assertEquals("/root", + DomUtil.getAbsolutePath(root)); + assertEquals("/root/bargle", + DomUtil.getAbsolutePath(child1)); + assertEquals("/root/bargle/bargle", + DomUtil.getAbsolutePath(child1a)); + assertEquals("/root/wargle[1]", + DomUtil.getAbsolutePath(child2)); + assertEquals("/root/wargle[2]", + DomUtil.getAbsolutePath(child3)); + assertEquals("/root/wargle[2]/zargle", + DomUtil.getAbsolutePath(child3a)); + assertEquals("/root/zargle[1]", + DomUtil.getAbsolutePath(child4)); + assertEquals("/root/zargle[1]/bargle[1]", + DomUtil.getAbsolutePath(child4a)); + assertEquals("/root/zargle[1]/bargle[2]", + DomUtil.getAbsolutePath(child4b)); + assertEquals("/root/zargle[2]", + DomUtil.getAbsolutePath(child5)); + assertEquals("/root/zargle[3]", + DomUtil.getAbsolutePath(child6)); + assertEquals("/root/zargle[4]", + DomUtil.getAbsolutePath(child7)); + } + + + public void testGetAbsolutePathWithPredefinedNamespaces() throws Exception + { + NamespaceContext nsLookup1 = new SimpleNamespaceResolver("arg", "argle"); + + assertEquals("/root", + DomUtil.getAbsolutePath(root, nsLookup1)); + assertEquals("/root/bargle", + DomUtil.getAbsolutePath(child1, nsLookup1)); + assertEquals("/root/bargle/bargle", + DomUtil.getAbsolutePath(child1a, nsLookup1)); + assertEquals("/root/wargle[1]", + DomUtil.getAbsolutePath(child2, nsLookup1)); + assertEquals("/root/wargle[2]", + DomUtil.getAbsolutePath(child3, nsLookup1)); + assertEquals("/root/wargle[2]/zargle", + DomUtil.getAbsolutePath(child3a, nsLookup1)); + assertEquals("/root/arg:zargle[1]", + DomUtil.getAbsolutePath(child4, nsLookup1)); + assertEquals("/root/arg:zargle[1]/bargle", + DomUtil.getAbsolutePath(child4a, nsLookup1)); + assertEquals("/root/arg:zargle[1]/arg:bargle", + DomUtil.getAbsolutePath(child4b, nsLookup1)); + assertEquals("/root/arg:zargle[2]", + DomUtil.getAbsolutePath(child5, nsLookup1)); + assertEquals("/root/NS0:zargle", + DomUtil.getAbsolutePath(child6, nsLookup1)); + assertEquals("/root/NS0:zargle/NS0:zargle", + DomUtil.getAbsolutePath(child6a, nsLookup1)); + assertEquals("/root/arg:zargle[3]", + DomUtil.getAbsolutePath(child7, nsLookup1)); + assertEquals("/root/NS0:zargle", + DomUtil.getAbsolutePath(child8, nsLookup1)); + } + + + public void testGetAbsolutePathWithUpdatableNamespaces() throws Exception + { + NamespaceResolver nsLookup = new NamespaceResolver() + .addNamespace("arg", "argle") + .addNamespace("NS0", "asdf") + .addNamespace("NS1", "asdf"); + + assertEquals("/root", + DomUtil.getAbsolutePath(root, nsLookup)); + assertEquals("/root/bargle", + DomUtil.getAbsolutePath(child1, nsLookup)); + assertEquals("/root/bargle/bargle", + DomUtil.getAbsolutePath(child1a, nsLookup)); + assertEquals("/root/wargle[1]", + DomUtil.getAbsolutePath(child2, nsLookup)); + assertEquals("/root/wargle[2]", + DomUtil.getAbsolutePath(child3, nsLookup)); + assertEquals("/root/wargle[2]/zargle", + DomUtil.getAbsolutePath(child3a, nsLookup)); + assertEquals("/root/arg:zargle[1]", + DomUtil.getAbsolutePath(child4, nsLookup)); + assertEquals("/root/arg:zargle[1]/bargle", + DomUtil.getAbsolutePath(child4a, nsLookup)); + assertEquals("/root/arg:zargle[1]/arg:bargle", + DomUtil.getAbsolutePath(child4b, nsLookup)); + assertEquals("/root/arg:zargle[2]", + DomUtil.getAbsolutePath(child5, nsLookup)); + assertEquals("/root/NS2:zargle", + DomUtil.getAbsolutePath(child6, nsLookup)); + // note: previous call already added the namespace binding + assertEquals("/root/NS2:zargle/NS2:zargle", + DomUtil.getAbsolutePath(child6a, nsLookup)); + assertEquals("/root/arg:zargle[3]", + DomUtil.getAbsolutePath(child7, nsLookup)); + assertEquals("/root/NS3:zargle", + DomUtil.getAbsolutePath(child8, nsLookup)); + + // verify that the resolver has been updated from all calls + + assertEquals("qargle", nsLookup.getNamespaceURI("NS2")); + assertEquals("margle", nsLookup.getNamespaceURI("NS3")); + } + + + public void testGetAbsolutePathWillSelectElement() throws Exception + { + for (Element elem : allElements) + { + NamespaceResolver nsLookup = new NamespaceResolver(); + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(nsLookup); + assertSame(elem, xpath.evaluate( + DomUtil.getAbsolutePath(elem, nsLookup), + dom, XPathConstants.NODE)); + } + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |