[Practicalxml-commits] SF.net SVN: practicalxml:[40] trunk/src
Brought to you by:
kdgregory
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-08 03:13:57
|
Revision: 40 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=40&view=rev Author: kdgregory Date: 2008-12-08 03:13:51 +0000 (Mon, 08 Dec 2008) Log Message: ----------- add DomUtil.getSiblings(), DomUtil.getAbsolutePath() rename namespace-inheriting variant of DomUtil.appendChild() to appendChildWithNamespace() refactor DomUtil.getChildren() Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/DomUtil.java trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java Modified: trunk/src/main/java/net/sf/practicalxml/DomUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2008-12-03 14:08:45 UTC (rev 39) +++ trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2008-12-08 03:13:51 UTC (rev 40) @@ -2,19 +2,20 @@ import java.util.ArrayList; import java.util.List; - +import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import org.apache.commons.lang.StringUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; +import org.apache.commons.lang.StringUtils; + /** * A collection of static utility methods for working with DOM trees. * Most of these are usability workarounds for the <code>org.w3c.dom</code> @@ -69,6 +70,21 @@ /** + * Appends a child element with the specified name and no namespace, to a + * passed parent element. + * + * @param parent The parent element. + * @param lclName Qualified name for the new element. + * + * @return The newly created child element. + */ + public static Element appendChild(Element parent, String lclName) + { + return appendChild(parent, null, lclName); + } + + + /** * Appends a child element with the specified name and namespace, to a * passed parent element. * @@ -88,9 +104,9 @@ /** - * Appends a child element with the specified name, to a passed parent + * Appends a child element with the specified name to a passed parent * element. The child will inherit the parent's namespace (if any), and - * may inherit the parent's namespace prefix. + * may also inherit the parent's namespace prefix. * * @param parent The parent element. * @param qname Qualified name for the new element. If passed a simple @@ -98,7 +114,7 @@ * * @return The newly created child element. */ - public static Element appendChild(Element parent, String qname) + public static Element appendChildWithNamespace(Element parent, String qname) { String nsUri = parent.getNamespaceURI(); String parentPrefix = parent.getPrefix(); @@ -112,9 +128,58 @@ /** - * Returns all <code>Element</code> children of the passed element, - * in document order. + * Returns all <code>Element</code> children of the passed element's + * parent (ie, the element <em>and</em> its siblings). Result is in + * document order. */ + public static List<Element> getSiblings(Element elem) + { + if (elem.getParentNode() instanceof Element) + return getChildren((Element)elem.getParentNode()); + else + { + List<Element> ret = new ArrayList<Element>(); + ret.add(elem); + return ret; + } + } + + + /** + * Returns all <code>Element</code> children of the passed element's + * parent that have the specified <em>localname</em>, ignoring namespace. + * Result is in document order (and will only contain the passed element + * if it satisfies the name test). + */ + public static List<Element> getSiblings(Element elem, String lclName) + { + if (elem.getParentNode() instanceof Element) + return getChildren((Element)elem.getParentNode(), lclName); + else + return new ArrayList<Element>(); + } + + + /** + * Returns all <code>Element</code> children of the passed element's + * parent that have the specified namespace and local name. Result is + * in document order (note that it may not contain the passed element). + * Specified namespace may be <code>null</code>, in which case selected + * children must not have a namespace. + */ + public static List<Element> getSiblings(Element elem, String nsUri, String lclName) + { + if (elem.getParentNode() instanceof Element) + return getChildren((Element)elem.getParentNode(), nsUri, lclName); + else + return new ArrayList<Element>(); + } + + + /** + * Returns all <code>Element</code> children of the passed element, in + * document order. + */ public static List<Element> getChildren(Element parent) { List<Element> ret = new ArrayList<Element>(); @@ -133,8 +198,7 @@ /** * Returns the children of the passed element that have the given - * <em>localname</em>. This method ignores namespace and name - * prefix -- it's probably the best choice for most needs. + * <em>localname</em>, ignoring namespace. * <p> * Returns the children in document order. Returns an empty list if * there are no children matching the specified name. @@ -158,7 +222,9 @@ /** * Returns the children of the passed element that have the given - * namespace and <em>localname</em> (ignoring prefix). + * namespace and <em>localname</em> (ignoring prefix). Namespace may + * be <code>null</code>, in which case the child element must not + * have a namespace. * <p> * Returns the children in document order. Returns an empty list if * there are no children matching the specified namespace/name. @@ -170,9 +236,7 @@ for (int ii = 0 ; ii < children.getLength() ; ii++) { Node child = children.item(ii); - if ((child instanceof Element) - && (nsUri.equals(child.getNamespaceURI())) - && (lclName.equals(getLocalName((Element)child)))) + if ((child instanceof Element) && isNamed((Element)child, nsUri, lclName)) { ret.add((Element)child); } @@ -352,7 +416,7 @@ /** * Determines whether the passed element has the expected namespace URI * and local name. The expected namespace may be null, in which case - * the the element must not have a namespace. + * the element must not have a namespace. */ public static boolean isNamed(Element elem, String nsUri, String localName) { @@ -384,8 +448,9 @@ * limitations: First, it doesn't handle namespaces, although it does * 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 nodes with the same name - * and attribute values (ie, no positional predicates). + * important, it doesn't differentiate between sibling nodes with the + * same name and attribute values; if that's important, use {@link + * #getAbsolutePath}. */ public static String getPath(Element elem, String... attrNames) { @@ -395,6 +460,43 @@ } + /** + * 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 local name, ignoring + * namespace. + */ + public static String getAbsolutePath(Element elem) + { + StringBuilder sb = new StringBuilder(); + buildAbsolutePath(elem, sb, null, null); + 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 all namespaces can + * be resolved to a prefix, then the returned path may be evaluated + * against the document to retrieve the element. + * <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. + */ + public static String getAbsolutePath(Element elem, NamespaceContext nsLookup) + { + StringBuilder sb = new StringBuilder(); + buildAbsolutePath(elem, sb, nsLookup, new int[] {0}); + return sb.toString(); + } + + //---------------------------------------------------------------------------- // Internals //---------------------------------------------------------------------------- @@ -428,8 +530,13 @@ /** - * Implementation code for {@link #getPath}, which recursively works + * Implementation code for {@link #getPath}. Recursively works * its way up the tree and adds information for each node. + * + * @param elem The current element, which is appended to the buffer + * after all parent nodes. + * @param sb A buffer used to build the path. + * @param attrNames Attribute names to include as predicates in path. */ private static void buildPath(Element elem, StringBuilder sb, String[] attrNames) { @@ -448,4 +555,83 @@ } } } + + + /** + * Implementation code for {@link #getAbsolutePath}. Recursively works + * its way up the tree and adds information for each node. + * + * @param elem The current element, which is appended to the buffer + * after all parent nodes. + * @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 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) + { + Node parent = elem.getParentNode(); + if (parent instanceof Element) + { + buildAbsolutePath((Element)parent, sb, nsLookup, nsCounter); + } + + String prefix = getPrefix(elem, nsLookup, nsCounter); + String localName = getLocalName(elem); + List<Element> siblings = (nsLookup == null) + ? getSiblings(elem, getLocalName(elem)) + : getSiblings(elem, elem.getNamespaceURI(), getLocalName(elem)); + + sb.append("/"); + if (prefix != null) + sb.append(prefix).append(":"); + sb.append(localName); + if (siblings.size() > 1) + sb.append("[").append(getIndex(elem, siblings)).append("]"); + } + + + /** + * Helper method for {@link #buildAbsolutePath} that returns the prefix + * for a given element, using the passed namespace resolver. + */ + private static String getPrefix(Element elem, NamespaceContext nsLookup, int[] nsCounter) + { + if (nsLookup == null) + return null; + + String nsUri = elem.getNamespaceURI(); + if (nsUri == null) + return null; + + String prefix = nsLookup.getPrefix(nsUri); + if (prefix == null) + prefix = "NS" + nsCounter[0]++; + return prefix; + } + + + /** + * Helper method for {@link #buildAbsolutePath} that returns the position + * of the specified element in a list of its siblings. This may graduate + * to a public method if it's found generally useful. + * + * @throws IllegalArgumentException if <code>siblings</code> does not + * contain <code>elem</code> ... should never happen. + */ + private static int getIndex(Element elem, List<Element> siblings) + { + int elemPos = 0; + for (Element sibling : siblings) + { + elemPos++; + if (sibling == elem) + return elemPos; + } + throw new IllegalArgumentException("element not amongs its siblings"); + } } Modified: trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2008-12-03 14:08:45 UTC (rev 39) +++ trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2008-12-08 03:13:51 UTC (rev 40) @@ -2,11 +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; + public class TestDomUtil extends AbstractTestCase { @@ -32,7 +36,7 @@ // variant with explicit namespace - Element child1 = DomUtil.appendChild(root, null, "baz"); + Element child1 = DomUtil.appendChild(root, "baz"); assertNotNull(child1); assertSame(root, child1.getParentNode()); assertNull(child1.getNamespaceURI()); @@ -53,23 +57,21 @@ assertEquals("w", child3.getPrefix()); assertEquals("wargle", child3.getLocalName()); - // variant that inherits namespace - - Element grandchild1 = DomUtil.appendChild(child1, "qwe"); + Element grandchild1 = DomUtil.appendChildWithNamespace(child1, "qwe"); assertNotNull(grandchild1); assertSame(child1, grandchild1.getParentNode()); assertEquals(child1.getNamespaceURI(), grandchild1.getNamespaceURI()); assertEquals(child1.getPrefix(), grandchild1.getPrefix()); assertEquals("qwe", grandchild1.getLocalName()); - Element grandchild2 = DomUtil.appendChild(child2, "asd"); + Element grandchild2 = DomUtil.appendChildWithNamespace(child2, "asd"); assertNotNull(grandchild2); assertSame(child2, grandchild2.getParentNode()); assertEquals(child2.getNamespaceURI(), grandchild2.getNamespaceURI()); assertEquals(child2.getPrefix(), grandchild2.getPrefix()); assertEquals("asd", grandchild2.getLocalName()); - Element grandchild3 = DomUtil.appendChild(child3, "zxc"); + Element grandchild3 = DomUtil.appendChildWithNamespace(child3, "zxc"); assertNotNull(grandchild3); assertSame(child3, grandchild3.getParentNode()); assertEquals(child3.getNamespaceURI(), grandchild3.getNamespaceURI()); @@ -85,7 +87,7 @@ { Element root = DomUtil.newDocument("foo"); Element child = DomUtil.appendChild(root, "MyNSURI", "argle:bargle"); - Element grandchild = DomUtil.appendChild(child, "zippy:pinhead"); + Element grandchild = DomUtil.appendChildWithNamespace(child, "zippy:pinhead"); assertEquals("zippy", grandchild.getPrefix()); } @@ -109,7 +111,7 @@ DomUtil.setText(root, t3); assertEquals(t3, DomUtil.getText(root)); - Element child = DomUtil.appendChild(root, "bar"); + Element child = DomUtil.appendChildWithNamespace(root, "bar"); assertNull(DomUtil.getText(child)); DomUtil.appendText(child, t1); @@ -130,11 +132,51 @@ } + public void testGetSiblings() throws Exception + { + Element root = DomUtil.newDocument("foo"); + Element child1 = DomUtil.appendChildWithNamespace(root, "bargle"); + Element child2 = DomUtil.appendChild(root, "argle", "bargle"); + Element child3 = DomUtil.appendChild(root, "argle", "w:wargle"); + DomUtil.appendText(root, "should never be returned"); + + List<Element> ret1 = DomUtil.getSiblings(root); + assertEquals(1, ret1.size()); + assertSame(root, ret1.get(0)); + + List<Element> ret2 = DomUtil.getSiblings(child1); + assertEquals(3, ret2.size()); + assertSame(child1, ret2.get(0)); + assertSame(child2, ret2.get(1)); + assertSame(child3, ret2.get(2)); + + List<Element> ret3 = DomUtil.getSiblings(child1, "bargle"); + assertEquals(2, ret3.size()); + assertSame(child1, ret3.get(0)); + assertSame(child2, ret3.get(1)); + + List<Element> ret4 = DomUtil.getSiblings(child1, "wargle"); + assertEquals(1, ret4.size()); + assertSame(child3, ret4.get(0)); + + List<Element> ret5 = DomUtil.getSiblings(child1, "argle", "bargle"); + assertEquals(1, ret5.size()); + assertSame(child2, ret5.get(0)); + + List<Element> ret6 = DomUtil.getSiblings(child1, null, "bargle"); + assertEquals(1, ret6.size()); + assertSame(child1, ret6.get(0)); + + List<Element> ret7 = DomUtil.getSiblings(child1, "fargle", "bargle"); + assertEquals(0, ret7.size()); + } + + public void testGetChildren() throws Exception { Element root = DomUtil.newDocument("foo"); DomUtil.appendText(root, "bar"); - Element child1 = DomUtil.appendChild(root, "bargle"); + Element child1 = DomUtil.appendChildWithNamespace(root, "bargle"); Element child2 = DomUtil.appendChild(root, "argle", "bargle"); Element child3 = DomUtil.appendChild(root, "argle", "w:wargle"); @@ -208,8 +250,8 @@ final String TEXT2_WS = " "; Element root = DomUtil.newDocument("foo"); - Element child1 = DomUtil.appendChild(root, "foo"); - Element child2 = DomUtil.appendChild(root, "foo"); + Element child1 = DomUtil.appendChildWithNamespace(root, "foo"); + Element child2 = DomUtil.appendChildWithNamespace(root, "foo"); DomUtil.setText(child1, TEXT1_WS); DomUtil.setText(child2, TEXT2_WS); @@ -231,8 +273,8 @@ public void testRemoveEmptyText() throws Exception { Element root = DomUtil.newDocument("foo"); - Element child1 = DomUtil.appendChild(root, "foo"); - Element child2 = DomUtil.appendChild(root, "foo"); + Element child1 = DomUtil.appendChildWithNamespace(root, "foo"); + Element child2 = DomUtil.appendChildWithNamespace(root, "foo"); DomUtil.setText(child1, "foo"); DomUtil.setText(child2, " "); @@ -244,13 +286,38 @@ } + public void testIsNamed() throws Exception + { + Element root = DomUtil.newDocument("foo"); + Element child = DomUtil.appendChild(root, "bar", "argle:bargle"); + + assertTrue(DomUtil.isNamed(root, null, "foo")); + assertTrue(DomUtil.isNamed(child, "bar", "bargle")); + + assertFalse(DomUtil.isNamed(root, null, "blimey")); + assertFalse(DomUtil.isNamed(child, "bar", "blimey")); + assertFalse(DomUtil.isNamed(child, "bar", "argle:bargle")); + assertFalse(DomUtil.isNamed(child, null, "bargle")); + + try + { + assertFalse(DomUtil.isNamed(root, null, null)); + fail("accepted null localName"); + } + catch (IllegalArgumentException e) + { + // success + } + } + + public void testGetPath() throws Exception { Element root = DomUtil.newDocument("foo"); - Element child1 = DomUtil.appendChild(root, "bargle"); + 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, "zargle"); + Element child1a = DomUtil.appendChild(child1, null, "zargle"); child1.setAttribute("poi", "1234"); child2.setAttribute("poi", "5678"); child2.setAttribute("qwe", "asd"); @@ -276,27 +343,71 @@ } - public void testIsNamed() throws Exception + public void testGetAbsolutePath() throws Exception { Element root = DomUtil.newDocument("foo"); - Element child = DomUtil.appendChild(root, "bar", "argle:bargle"); + 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"); - assertTrue(DomUtil.isNamed(root, null, "foo")); - assertTrue(DomUtil.isNamed(child, "bar", "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)); - assertFalse(DomUtil.isNamed(root, null, "blimey")); - assertFalse(DomUtil.isNamed(child, "bar", "blimey")); - assertFalse(DomUtil.isNamed(child, "bar", "argle:bargle")); - assertFalse(DomUtil.isNamed(child, null, "bargle")); + NamespaceContext nsLookup = new SimpleNamespaceResolver("arg", "argle"); - try - { - assertFalse(DomUtil.isNamed(root, null, null)); - fail("accepted null localName"); - } - catch (IllegalArgumentException e) - { - // success - } + 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)); } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |