From: <bo...@us...> - 2010-08-26 14:47:00
|
Revision: 436 http://xmlunit.svn.sourceforge.net/xmlunit/?rev=436&view=rev Author: bodewig Date: 2010-08-26 14:46:54 +0000 (Thu, 26 Aug 2010) Log Message: ----------- allow child elements to be added incrementally in XPathContext Modified Paths: -------------- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/XPathContext.java trunk/xmlunit/src/main/java-core/net/sf/xmlunit/util/Linqy.java trunk/xmlunit/src/main/net-core/diff/XPathContext.cs trunk/xmlunit/src/tests/java-core/net/sf/xmlunit/diff/XPathContextTest.java trunk/xmlunit/src/tests/net-core/diff/XPathContextTest.cs Modified: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/XPathContext.java =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/XPathContext.java 2010-08-23 08:42:44 UTC (rev 435) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/XPathContext.java 2010-08-26 14:46:54 UTC (rev 436) @@ -28,6 +28,15 @@ private final Deque<Level> path = new LinkedList<Level>(); private final Map<String, String> uri2Prefix; + private static final String COMMENT = "comment()"; + private static final String PI = "processing-instruction()"; + private static final String TEXT = "text()"; + private static final String OPEN = "["; + private static final String CLOSE = "]"; + private static final String SEP = "/"; + private static final String ATTR = "@"; + private static final String EMPTY = ""; + public XPathContext() { this(null); } @@ -38,7 +47,7 @@ } else { this.uri2Prefix = Collections.unmodifiableMap(uri2Prefix); } - path.addLast(new Level("")); + path.addLast(new Level(EMPTY)); } public void navigateToChild(int index) { @@ -53,42 +62,63 @@ path.removeLast(); } - public void registerAttributes(Iterable<? extends QName> attributes) { + public void addAttributes(Iterable<? extends QName> attributes) { Level current = path.getLast(); for (QName attribute : attributes) { current.attributes.put(attribute, - new Level("@" + getName(attribute))); + new Level(ATTR + getName(attribute))); } } - public void registerChildren(Iterable<? extends NodeInfo> children) { + public void setChildren(Iterable<? extends NodeInfo> children) { Level current = path.getLast(); + current.children.clear(); + appendChildren(children); + } + + public void appendChildren(Iterable<? extends NodeInfo> children) { + Level current = path.getLast(); int comments, pis, texts; comments = pis = texts = 0; Map<String, Integer> elements = new HashMap<String, Integer>(); + + for (Level l : current.children) { + String childName = l.expression; + if (childName.startsWith(COMMENT)) { + comments++; + } else if (childName.startsWith(PI)) { + pis++; + } else if (childName.startsWith(TEXT)) { + texts++; + } else { + childName = childName.substring(0, childName.indexOf(OPEN)); + add1OrIncrement(childName, elements); + } + } + for (NodeInfo child : children) { Level l = null; switch (child.getType()) { case Node.COMMENT_NODE: - l = new Level("comment()[" + (++comments) + "]"); + l = new Level(COMMENT + OPEN + (++comments) + CLOSE); break; case Node.PROCESSING_INSTRUCTION_NODE: - l = new Level("processing-instruction()[" + (++pis) + "]"); + l = new Level(PI + OPEN + (++pis) + CLOSE); break; case Node.CDATA_SECTION_NODE: case Node.TEXT_NODE: - l = new Level("text()[" + (++texts) + "]"); + l = new Level(TEXT + OPEN + (++texts) + CLOSE); break; case Node.ELEMENT_NODE: String name = getName(child.getName()); - Integer old = elements.get(name); - int index = old == null ? 0 : old.intValue(); - l = new Level(name + "[" + (++index) + "]"); - elements.put(name, Integer.valueOf(index)); + l = new Level(name + OPEN + add1OrIncrement(name, elements) + + CLOSE); break; default: - throw new IllegalArgumentException("unknown node type " + - child.getType()); + // more or less ignore + // FIXME: is this a good thing? + l = new Level(EMPTY); + break; } current.children.add(l); } @@ -97,9 +127,9 @@ public String getXPath() { StringBuilder sb = new StringBuilder(); for (Level l : path) { - sb.append("/").append(l.expression); + sb.append(SEP).append(l.expression); } - return sb.toString().replace("//", "/"); + return sb.toString().replace(SEP + SEP, SEP); } private String getName(QName name) { @@ -108,9 +138,22 @@ if (ns != null) { p = uri2Prefix.get(ns); } - return (p == null ? "" : p + ":") + name.getLocalPart(); + return (p == null ? EMPTY : p + ":") + name.getLocalPart(); } + /** + * Increments the value name maps to or adds 1 as value if name + * isn't present inside the map. + * + * @return the new mapping for name + */ + private static int add1OrIncrement(String name, Map<String, Integer> map) { + Integer old = map.get(name); + int index = old == null ? 1 : (old.intValue() + 1); + map.put(name, Integer.valueOf(index)); + return index; + } + private static class Level { private final String expression; private List<Level> children = new ArrayList<Level>(); Modified: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/util/Linqy.java =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/util/Linqy.java 2010-08-23 08:42:44 UTC (rev 435) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/util/Linqy.java 2010-08-26 14:46:54 UTC (rev 436) @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; public final class Linqy { /** @@ -37,6 +38,14 @@ }; } + public static <E> Iterable<E> singleton(final E single) { + return new Iterable<E>() { + public Iterator<E> iterator() { + return new OnceOnlyIterator<E>(single); + } + }; + } + private static class CastingIterator<E> implements Iterator<E> { private final Iterator i; private CastingIterator(Iterator i) { @@ -52,4 +61,25 @@ return i.hasNext(); } } + + private static class OnceOnlyIterator<E> implements Iterator<E> { + private final E element; + private boolean iterated = false; + private OnceOnlyIterator(E element) { + this.element = element; + } + public void remove() { + throw new UnsupportedOperationException(); + } + public E next() { + if (iterated) { + throw new NoSuchElementException(); + } + iterated = true; + return element; + } + public boolean hasNext() { + return !iterated; + } + } } \ No newline at end of file Modified: trunk/xmlunit/src/main/net-core/diff/XPathContext.cs =================================================================== --- trunk/xmlunit/src/main/net-core/diff/XPathContext.cs 2010-08-23 08:42:44 UTC (rev 435) +++ trunk/xmlunit/src/main/net-core/diff/XPathContext.cs 2010-08-26 14:46:54 UTC (rev 436) @@ -23,6 +23,14 @@ private readonly LinkedList<Level> path = new LinkedList<Level>(); private readonly IDictionary<string, string> uri2Prefix; + private const string COMMENT = "comment()"; + private const string PI = "processing-instruction()"; + private const string TEXT = "text()"; + private const string OPEN = "["; + private const string CLOSE = "]"; + private const string SEP = "/"; + private const string ATTR = "@"; + public XPathContext() : this(null) { } @@ -47,46 +55,66 @@ path.RemoveLast(); } - public void RegisterAttributes<Q>(IEnumerable<Q> attributes) + public void AddAttributes<Q>(IEnumerable<Q> attributes) where Q : XmlQualifiedName { Level current = path.Last.Value; foreach (XmlQualifiedName attribute in attributes) { current.Attributes[attribute] = - new Level("@" + GetName(attribute)); + new Level(ATTR + GetName(attribute)); } } - public void RegisterChildren<N>(IEnumerable<N> children) + public void SetChildren<N>(IEnumerable<N> children) where N : INodeInfo { Level current = path.Last.Value; + current.Children.Clear(); + AppendChildren(children); + } + + public void AppendChildren<N>(IEnumerable<N> children) + where N : INodeInfo { + Level current = path.Last.Value; int comments, pis, texts; comments = pis = texts = 0; IDictionary<string, int> elements = new Dictionary<string, int>(); + + foreach (Level l in current.Children) { + string childName = l.Expression; + if (childName.StartsWith(COMMENT)) { + comments++; + } else if (childName.StartsWith(PI)) { + pis++; + } else if (childName.StartsWith(TEXT)) { + texts++; + } else { + childName = childName.Substring(0, childName.IndexOf(OPEN)); + Add1OrIncrement(childName, elements); + } + } + foreach (INodeInfo child in children) { Level l = null; switch (child.Type) { case XmlNodeType.Comment: - l = new Level("comment()[" + (++comments) + "]"); + l = new Level(COMMENT + OPEN + (++comments) + CLOSE); break; case XmlNodeType.ProcessingInstruction: - l = new Level("processing-instruction()[" + (++pis) + "]"); + l = new Level(PI + OPEN + (++pis) + CLOSE); break; case XmlNodeType.CDATA: case XmlNodeType.Text: - l = new Level("text()[" + (++texts) + "]"); + l = new Level(TEXT + OPEN + (++texts) + CLOSE); break; case XmlNodeType.Element: string name = GetName(child.Name); - int old; - if (!elements.TryGetValue(name, out old)) { - old = 0; - } - l = new Level(name + "[" + (++old) + "]"); - elements[name] = old; + l = new Level(name + OPEN + Add1OrIncrement(name, elements) + + CLOSE); break; default: - throw new ArgumentException("unknown node type " + - child.Type); + // more or less ignore + // FIXME: is this a good thing? + l = new Level(string.Empty); + break; } current.Children.Add(l); } @@ -96,9 +124,9 @@ get { StringBuilder sb = new StringBuilder(); foreach (Level l in path) { - sb.AppendFormat("/{0}", l.Expression); + sb.AppendFormat(SEP + "{0}", l.Expression); } - return sb.Replace("//", "/").ToString(); + return sb.Replace(SEP + SEP, SEP).ToString(); } } @@ -111,6 +139,19 @@ return (p == null ? "" : p + ":") + name.Name; } + /// <summary> + /// Increments the value name maps to or adds 1 as value if name + /// isn't present inside the map. + /// </summary> + /// <returns>the new mapping for name</returns> + private static int Add1OrIncrement(string name, + IDictionary<string, int> map) { + int index = 0; + map.TryGetValue(name, out index); + map[name] = ++index; + return index; + } + internal class Level { internal readonly string Expression; internal readonly IList<Level> Children = new List<Level>(); Modified: trunk/xmlunit/src/tests/java-core/net/sf/xmlunit/diff/XPathContextTest.java =================================================================== --- trunk/xmlunit/src/tests/java-core/net/sf/xmlunit/diff/XPathContextTest.java 2010-08-23 08:42:44 UTC (rev 435) +++ trunk/xmlunit/src/tests/java-core/net/sf/xmlunit/diff/XPathContextTest.java 2010-08-26 14:46:54 UTC (rev 436) @@ -14,9 +14,9 @@ package net.sf.xmlunit.diff; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import javax.xml.namespace.QName; +import net.sf.xmlunit.util.Linqy; import org.junit.Test; import org.w3c.dom.Node; @@ -34,7 +34,7 @@ l.add(new Element("bar")); l.add(new Element("foo")); XPathContext ctx = new XPathContext(); - ctx.registerChildren(l); + ctx.setChildren(l); ctx.navigateToChild(0); assertEquals("/foo[1]", ctx.getXPath()); ctx.navigateToParent(); @@ -48,6 +48,29 @@ assertEquals("/foo[3]", ctx.getXPath()); } + @Test public void appendChildren() { + ArrayList<Element> l = new ArrayList<Element>(); + l.add(new Element("foo")); + l.add(new Element("foo")); + XPathContext ctx = new XPathContext(); + ctx.setChildren(l); + l = new ArrayList<Element>(); + l.add(new Element("bar")); + l.add(new Element("foo")); + ctx.appendChildren(l); + ctx.navigateToChild(0); + assertEquals("/foo[1]", ctx.getXPath()); + ctx.navigateToParent(); + ctx.navigateToChild(1); + assertEquals("/foo[2]", ctx.getXPath()); + ctx.navigateToParent(); + ctx.navigateToChild(2); + assertEquals("/bar[1]", ctx.getXPath()); + ctx.navigateToParent(); + ctx.navigateToChild(3); + assertEquals("/foo[3]", ctx.getXPath()); + } + @Test public void twoLevelsOfElements() { ArrayList<Element> l = new ArrayList<Element>(); l.add(new Element("foo")); @@ -55,10 +78,10 @@ l.add(new Element("bar")); l.add(new Element("foo")); XPathContext ctx = new XPathContext(); - ctx.registerChildren(l); + ctx.setChildren(l); ctx.navigateToChild(0); assertEquals("/foo[1]", ctx.getXPath()); - ctx.registerChildren(l); + ctx.setChildren(l); ctx.navigateToChild(3); assertEquals("/foo[1]/foo[3]", ctx.getXPath()); ctx.navigateToParent(); @@ -70,11 +93,11 @@ @Test public void attributes() { XPathContext ctx = new XPathContext(); - ctx.registerChildren(Collections.singletonList(new Element("foo"))); + ctx.setChildren(Linqy.singleton(new Element("foo"))); ctx.navigateToChild(0); ArrayList<QName> l = new ArrayList<QName>(); l.add(new QName("bar")); - ctx.registerAttributes(l); + ctx.addAttributes(l); ctx.navigateToAttribute(new QName("bar")); assertEquals("/foo[1]/@bar", ctx.getXPath()); } @@ -90,7 +113,7 @@ l.add(new PI()); l.add(new Text()); XPathContext ctx = new XPathContext(); - ctx.registerChildren(l); + ctx.setChildren(l); ctx.navigateToChild(0); assertEquals("/text()[1]", ctx.getXPath()); ctx.navigateToParent(); @@ -124,7 +147,7 @@ HashMap<String, String> m = new HashMap<String, String>(); m.put("urn:foo:bar", "bar"); XPathContext ctx = new XPathContext(m); - ctx.registerChildren(l); + ctx.setChildren(l); ctx.navigateToChild(0); assertEquals("/foo[1]", ctx.getXPath()); ctx.navigateToParent(); @@ -139,14 +162,12 @@ HashMap<String, String> m = new HashMap<String, String>(); m.put("urn:foo:bar", "bar"); XPathContext ctx = new XPathContext(m); - ctx.registerChildren(Collections.singletonList(new Element("foo", - "urn:foo:bar")) - ); + ctx.setChildren(Linqy.singleton(new Element("foo", "urn:foo:bar"))); ctx.navigateToChild(0); ArrayList<QName> l = new ArrayList<QName>(); l.add(new QName("baz")); l.add(new QName("urn:foo:bar", "baz")); - ctx.registerAttributes(l); + ctx.addAttributes(l); ctx.navigateToAttribute(new QName("baz")); assertEquals("/bar:foo[1]/@baz", ctx.getXPath()); ctx.navigateToParent(); Modified: trunk/xmlunit/src/tests/net-core/diff/XPathContextTest.cs =================================================================== --- trunk/xmlunit/src/tests/net-core/diff/XPathContextTest.cs 2010-08-23 08:42:44 UTC (rev 435) +++ trunk/xmlunit/src/tests/net-core/diff/XPathContextTest.cs 2010-08-26 14:46:54 UTC (rev 436) @@ -27,6 +27,30 @@ } [Test] + public void AppendChildren() { + List<Element> l = new List<Element>(); + l.Add(new Element("foo")); + l.Add(new Element("foo")); + XPathContext ctx = new XPathContext(); + ctx.SetChildren(l); + l = new List<Element>(); + l.Add(new Element("bar")); + l.Add(new Element("foo")); + ctx.AppendChildren(l); + ctx.NavigateToChild(0); + Assert.AreEqual("/foo[1]", ctx.XPath); + ctx.NavigateToParent(); + ctx.NavigateToChild(1); + Assert.AreEqual("/foo[2]", ctx.XPath); + ctx.NavigateToParent(); + ctx.NavigateToChild(2); + Assert.AreEqual("/bar[1]", ctx.XPath); + ctx.NavigateToParent(); + ctx.NavigateToChild(3); + Assert.AreEqual("/foo[3]", ctx.XPath); + } + + [Test] public void OneLevelOfElements() { List<Element> l = new List<Element>(); l.Add(new Element("foo")); @@ -34,7 +58,7 @@ l.Add(new Element("bar")); l.Add(new Element("foo")); XPathContext ctx = new XPathContext(); - ctx.RegisterChildren(l); + ctx.SetChildren(l); ctx.NavigateToChild(0); Assert.AreEqual("/foo[1]", ctx.XPath); ctx.NavigateToParent(); @@ -56,10 +80,10 @@ l.Add(new Element("bar")); l.Add(new Element("foo")); XPathContext ctx = new XPathContext(); - ctx.RegisterChildren(l); + ctx.SetChildren(l); ctx.NavigateToChild(0); Assert.AreEqual("/foo[1]", ctx.XPath); - ctx.RegisterChildren(l); + ctx.SetChildren(l); ctx.NavigateToChild(3); Assert.AreEqual("/foo[1]/foo[3]", ctx.XPath); ctx.NavigateToParent(); @@ -72,11 +96,11 @@ [Test] public void Attributes() { XPathContext ctx = new XPathContext(); - ctx.RegisterChildren(Linqy.Singleton(new Element("foo"))); + ctx.SetChildren(Linqy.Singleton(new Element("foo"))); ctx.NavigateToChild(0); List<XmlQualifiedName> l = new List<XmlQualifiedName>(); l.Add(new XmlQualifiedName("bar")); - ctx.RegisterAttributes(l); + ctx.AddAttributes(l); ctx.NavigateToAttribute(new XmlQualifiedName("bar")); Assert.AreEqual("/foo[1]/@bar", ctx.XPath); } @@ -93,7 +117,7 @@ l.Add(new PI()); l.Add(new Text()); XPathContext ctx = new XPathContext(); - ctx.RegisterChildren(l); + ctx.SetChildren(l); ctx.NavigateToChild(0); Assert.AreEqual("/text()[1]", ctx.XPath); ctx.NavigateToParent(); @@ -128,7 +152,7 @@ Dictionary<string, string> m = new Dictionary<string, string>(); m["urn:foo:bar"] = "bar"; XPathContext ctx = new XPathContext(m); - ctx.RegisterChildren(l); + ctx.SetChildren(l); ctx.NavigateToChild(0); Assert.AreEqual("/foo[1]", ctx.XPath); ctx.NavigateToParent(); @@ -144,13 +168,13 @@ Dictionary<string, string> m = new Dictionary<string, string>(); m["urn:foo:bar"] = "bar"; XPathContext ctx = new XPathContext(m); - ctx.RegisterChildren(Linqy.Singleton(new Element("foo", + ctx.SetChildren(Linqy.Singleton(new Element("foo", "urn:foo:bar"))); ctx.NavigateToChild(0); List<XmlQualifiedName> l = new List<XmlQualifiedName>(); l.Add(new XmlQualifiedName("baz")); l.Add(new XmlQualifiedName("baz", "urn:foo:bar")); - ctx.RegisterAttributes(l); + ctx.AddAttributes(l); ctx.NavigateToAttribute(new XmlQualifiedName("baz")); Assert.AreEqual("/bar:foo[1]/@baz", ctx.XPath); ctx.NavigateToParent(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |