[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.
|