From: <bo...@us...> - 2010-09-14 06:55:57
|
Revision: 473 http://xmlunit.svn.sourceforge.net/xmlunit/?rev=473&view=rev Author: bodewig Date: 2010-09-14 06:55:50 +0000 (Tue, 14 Sep 2010) Log Message: ----------- make the whole process of selecting nodes to match pluggable via a strategy Modified Paths: -------------- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/AbstractDifferenceEngine.java trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DOMDifferenceEngine.java.xml trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DifferenceEngine.java trunk/xmlunit/src/main/java-legacy/org/custommonkey/xmlunit/NewDifferenceEngine.java trunk/xmlunit/src/main/net-core/diff/AbstractDifferenceEngine.cs trunk/xmlunit/src/main/net-core/diff/DOMDifferenceEngine.xml trunk/xmlunit/src/main/net-core/diff/IDifferenceEngine.cs trunk/xmlunit/src/tests/java-core/net/sf/xmlunit/diff/DOMDifferenceEngineTest.java trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_DetailedDiff.java trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_NewDifferenceEngine.java trunk/xmlunit/src/tests/net-core/diff/DOMDifferenceEngineTest.cs Added Paths: ----------- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DefaultNodeMatcher.java trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/NodeMatcher.java trunk/xmlunit/src/main/net-core/diff/DefaultNodeMatcher.cs trunk/xmlunit/src/main/net-core/diff/INodeMatcher.cs Modified: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/AbstractDifferenceEngine.java =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/AbstractDifferenceEngine.java 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/AbstractDifferenceEngine.java 2010-09-14 06:55:50 UTC (rev 473) @@ -23,7 +23,7 @@ public abstract class AbstractDifferenceEngine implements DifferenceEngine { private final ComparisonListenerSupport listeners = new ComparisonListenerSupport(); - private ElementSelector elementSelector = ElementSelectors.Default; + private NodeMatcher nodeMatcher = new DefaultNodeMatcher(); private DifferenceEvaluator diffEvaluator = DifferenceEvaluators.Default; private Map<String, String> uri2Prefix = Collections.emptyMap(); @@ -48,16 +48,16 @@ listeners.addDifferenceListener(l); } - public void setElementSelector(ElementSelector s) { - if (s == null) { - throw new IllegalArgumentException("element selector must" + public void setNodeMatcher(NodeMatcher n) { + if (n == null) { + throw new IllegalArgumentException("node matcher must" + " not be null"); } - elementSelector = s; + nodeMatcher = n; } - public ElementSelector getElementSelector() { - return elementSelector; + public NodeMatcher getNodeMatcher() { + return nodeMatcher; } public void setDifferenceEvaluator(DifferenceEvaluator e) { Modified: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DOMDifferenceEngine.java.xml =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DOMDifferenceEngine.java.xml 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DOMDifferenceEngine.java.xml 2010-09-14 06:55:50 UTC (rev 473) @@ -20,6 +20,7 @@ <import reference="java.util.HashSet"/> <import reference="java.util.LinkedList"/> <import reference="java.util.List"/> + <import reference="java.util.Map"/> <import reference="java.util.Set"/> <import reference="java.util.TreeSet"/> <import reference="javax.xml.XMLConstants"/> @@ -315,47 +316,46 @@ XPathContext controlContext, Iterable<Node> testSeq, XPathContext testContext) { - List<Node> controlList = Linqy.asList(controlSeq); - List<Node> testList = Linqy.asList(testSeq); - final int testSize = testList.size(); - Set<Integer> unmatchedTestIndexes = new TreeSet<Integer>(); - for (int i = 0; i < testSize; i++) { - unmatchedTestIndexes.add(Integer.valueOf(i)); - } ]]></literal> <lastResultDef/> <literal><![CDATA[ // if there are no children on either Node, the result is equal lastResult = ComparisonResult.EQUAL; - final int controlSize = controlList.size(); - Match lastMatch = new Match(null, -1); - for (int i = 0; i < controlSize; i++) { - controlContext.navigateToChild(i); + + Iterable<Map.Entry<Node, Node>> matches = + getNodeMatcher().match(controlSeq, testSeq); + List<Node> controlList = Linqy.asList(controlSeq); + List<Node> testList = Linqy.asList(testSeq); + Set<Node> seen = new HashSet<Node>(); + for (Map.Entry<Node, Node> pair : matches) { + Node control = pair.getKey(); + seen.add(control); + Node test = pair.getValue(); + seen.add(test); + int controlIndex = controlList.indexOf(control); + int testIndex = testList.indexOf(test); + controlContext.navigateToChild(controlIndex); + testContext.navigateToChild(testIndex); try { - Match testMatch = findMatchingNode(controlList.get(i), testList, - lastMatch.index, - unmatchedTestIndexes); - if (testMatch != null) { ]]></literal> - testContext.navigateToChild(testMatch.index); - try { - Node control = controlList.get(i); - Node test = testMatch.node; - Integer testIndex = Integer.valueOf(testMatch.index); <compareExpr type="CHILD_NODELIST_SEQUENCE" - controlExpr="Integer.valueOf(i)" - testExpr="testIndex" + controlExpr="Integer.valueOf(controlIndex)" + testExpr="Integer.valueOf(testIndex)" /> <compareMethodExpr method="compareNodes" controlExpr="control" testExpr="test"/> <literal><![CDATA[ - unmatchedTestIndexes.remove(testIndex); - lastMatch = testMatch; - } finally { - testContext.navigateToParent(); - } - } else { + } finally { + testContext.navigateToParent(); + controlContext.navigateToParent(); + } + } + final int controlSize = controlList.size(); + for (int i = 0; i < controlSize; i++) { + if (!seen.contains(controlList.get(i))) { + controlContext.navigateToChild(i); + try { lastResult = compare(new Comparison(ComparisonType.CHILD_LOOKUP, controlList.get(i), @@ -365,26 +365,28 @@ ]]></literal> <if-return-boilerplate/> <literal><![CDATA[ + } finally { + controlContext.navigateToParent(); } - } finally { - controlContext.navigateToParent(); } } - for (Integer I : unmatchedTestIndexes) { - int i = I.intValue(); - testContext.navigateToChild(i); - try { - lastResult = - compare(new Comparison(ComparisonType.CHILD_LOOKUP, - null, null, null, - testList.get(i), - getXPath(testContext), - testList.get(i))); + final int testSize = testList.size(); + for (int i = 0; i < testSize; i++) { + if (!seen.contains(testList.get(i))) { + testContext.navigateToChild(i); + try { + lastResult = + compare(new Comparison(ComparisonType.CHILD_LOOKUP, + null, null, null, + testList.get(i), + getXPath(testContext), + testList.get(i))); ]]></literal> <if-return-boilerplate/> <literal><![CDATA[ - } finally { - testContext.navigateToParent(); + } finally { + testContext.navigateToParent(); + } } } return lastResult; @@ -459,51 +461,6 @@ return null; } - private Match findMatchingNode(final Node searchFor, - final List<Node> searchIn, - final int indexOfLastMatch, - final Set<Integer> availableIndexes) { - final int searchSize = searchIn.size(); - for (int i = indexOfLastMatch + 1; i < searchSize; i++) { - if (!availableIndexes.contains(Integer.valueOf(i))) { - continue; - } - if (nodesMatch(searchFor, searchIn.get(i))) { - return new Match(searchIn.get(i), i); - } - } - for (int i = 0; i < indexOfLastMatch; i++) { - if (!availableIndexes.contains(Integer.valueOf(i))) { - continue; - } - if (nodesMatch(searchFor, searchIn.get(i))) { - return new Match(searchIn.get(i), i); - } - } - return null; - } - - private boolean nodesMatch(final Node n1, final Node n2) { - if (n1 instanceof Element && n2 instanceof Element) { - return getElementSelector() - .canBeCompared((Element) n1, (Element) n2); - } - ComparisonResult r = - compareDontFire(new Comparison(ComparisonType.NODE_TYPE, - n1, null, n1.getNodeType(), - n2, null, n2.getNodeType())); - return r != ComparisonResult.CRITICAL && r != ComparisonResult.DIFFERENT; - } - - private class Match { - private final Node node; - private final int index; - private Match(Node match, int index) { - this.node = match; - this.index = index; - } - } - private static final Linqy.Mapper<Node, QName> QNAME_MAPPER = new Linqy.Mapper<Node, QName>() { public QName map(Node n) { return Nodes.getQName(n); } Added: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DefaultNodeMatcher.java =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DefaultNodeMatcher.java (rev 0) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DefaultNodeMatcher.java 2010-09-14 06:55:50 UTC (rev 473) @@ -0,0 +1,125 @@ +/* + This file is licensed to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package net.sf.xmlunit.diff; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import net.sf.xmlunit.util.Linqy; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * Strategy that matches control and tests nodes for comparison. + */ +public class DefaultNodeMatcher implements NodeMatcher { + private final ElementSelector elementSelector; + private final NodeTypeMatcher nodeTypeMatcher; + + public DefaultNodeMatcher() { + this(ElementSelectors.Default); + } + + public DefaultNodeMatcher(ElementSelector es) { + this(es, new DefaultNodeTypeMatcher()); + } + + public DefaultNodeMatcher(ElementSelector es, NodeTypeMatcher ntm) { + elementSelector = es; + nodeTypeMatcher = ntm; + } + + public Iterable<Map.Entry<Node, Node>> match(Iterable<Node> controlNodes, + Iterable<Node> testNodes) { + Map<Node, Node> matches = new LinkedHashMap<Node, Node>(); + List<Node> controlList = Linqy.asList(controlNodes); + List<Node> testList = Linqy.asList(testNodes); + final int testSize = testList.size(); + Set<Integer> unmatchedTestIndexes = new TreeSet<Integer>(); + for (int i = 0; i < testSize; i++) { + unmatchedTestIndexes.add(Integer.valueOf(i)); + } + final int controlSize = controlList.size(); + Match lastMatch = new Match(null, -1); + for (int i = 0; i < controlSize; i++) { + Node control = controlList.get(i); + Match testMatch = findMatchingNode(control, testList, + lastMatch.index, + unmatchedTestIndexes); + if (testMatch != null) { + unmatchedTestIndexes.remove(testMatch.index); + matches.put(control, testMatch.node); + } + } + return matches.entrySet(); + } + + private Match findMatchingNode(final Node searchFor, + final List<Node> searchIn, + final int indexOfLastMatch, + final Set<Integer> availableIndexes) { + final int searchSize = searchIn.size(); + for (int i = indexOfLastMatch + 1; i < searchSize; i++) { + if (!availableIndexes.contains(Integer.valueOf(i))) { + continue; + } + if (nodesMatch(searchFor, searchIn.get(i))) { + return new Match(searchIn.get(i), i); + } + } + for (int i = 0; i < indexOfLastMatch; i++) { + if (!availableIndexes.contains(Integer.valueOf(i))) { + continue; + } + if (nodesMatch(searchFor, searchIn.get(i))) { + return new Match(searchIn.get(i), i); + } + } + return null; + } + + private boolean nodesMatch(final Node n1, final Node n2) { + if (n1 instanceof Element && n2 instanceof Element) { + return elementSelector.canBeCompared((Element) n1, (Element) n2); + } + return nodeTypeMatcher.canBeCompared(n1.getNodeType(), + n2.getNodeType()); + } + + private class Match { + private final Node node; + private final int index; + private Match(Node match, int index) { + this.node = match; + this.index = index; + } + } + + public interface NodeTypeMatcher { + boolean canBeCompared(short controlType, short testType); + } + + private static final short CDATA = Node.TEXT_NODE; + private static final short TEXT = Node.CDATA_SECTION_NODE; + + public static class DefaultNodeTypeMatcher implements NodeTypeMatcher { + public boolean canBeCompared(short controlType, short testType) { + return controlType == testType + || (controlType == CDATA && testType == TEXT) + || (controlType == TEXT && testType == CDATA); + } + } +} Property changes on: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DefaultNodeMatcher.java ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DifferenceEngine.java =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DifferenceEngine.java 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DifferenceEngine.java 2010-09-14 06:55:50 UTC (rev 473) @@ -38,9 +38,9 @@ void addDifferenceListener(ComparisonListener l); /** - * Sets the strategy for selecting elements to compare. + * Sets the strategy for selecting nodes to compare. */ - void setElementSelector(ElementSelector s); + void setNodeMatcher(NodeMatcher n); /** * Determines whether the comparison should stop after given Added: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/NodeMatcher.java =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/NodeMatcher.java (rev 0) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/NodeMatcher.java 2010-09-14 06:55:50 UTC (rev 473) @@ -0,0 +1,30 @@ +/* + This file is licensed to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package net.sf.xmlunit.diff; + +import java.util.Map; +import org.w3c.dom.Node; + +/** + * Strategy that matches control and tests nodes for comparison. + */ +public interface NodeMatcher { + + /** + * Matches control and test nodes against each other, returns the + * matching pairs. + */ + Iterable<Map.Entry<Node, Node>> match(Iterable<Node> controlNodes, + Iterable<Node> testNodes); +} Property changes on: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/NodeMatcher.java ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/xmlunit/src/main/java-legacy/org/custommonkey/xmlunit/NewDifferenceEngine.java =================================================================== --- trunk/xmlunit/src/main/java-legacy/org/custommonkey/xmlunit/NewDifferenceEngine.java 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/main/java-legacy/org/custommonkey/xmlunit/NewDifferenceEngine.java 2010-09-14 06:55:50 UTC (rev 473) @@ -45,6 +45,7 @@ import net.sf.xmlunit.diff.ComparisonResult; import net.sf.xmlunit.diff.ComparisonType; import net.sf.xmlunit.diff.DOMDifferenceEngine; +import net.sf.xmlunit.diff.DefaultNodeMatcher; import net.sf.xmlunit.diff.DifferenceEvaluators; import net.sf.xmlunit.diff.ElementSelector; import net.sf.xmlunit.input.CommentLessSource; @@ -170,8 +171,7 @@ }); if (elementQualifier != null) { - engine - .setElementSelector(new ElementQualifier2ElementSelector(elementQualifier)); + engine.setNodeMatcher(new DefaultNodeMatcher(new ElementQualifier2ElementSelector(elementQualifier))); } Input.Builder ctrlBuilder = Input.fromNode(control); @@ -349,11 +349,12 @@ if ((comparison.getType() == ComparisonType.CHILD_NODELIST_LENGTH && comparison.getControlDetails().getTarget() instanceof Document) || - (comparison.getType() == ComparisonType.CHILD_LOOKUP - && comparison.getTestDetails() != null - && comparison.getTestDetails().getTarget() instanceof Node - && ((Node) comparison.getTestDetails().getTarget()).getParentNode() - instanceof Document) + ( + comparison.getType() == ComparisonType.CHILD_LOOKUP + && + (isNonElementDocumentChild(comparison.getControlDetails()) + || isNonElementDocumentChild(comparison.getTestDetails())) + ) || checkPrelude.shouldSkip() ) { return true; @@ -374,6 +375,12 @@ return false; } + private static boolean isNonElementDocumentChild(Comparison.Detail detail) { + return detail != null && detail.getTarget() instanceof Node + && !(detail.getTarget() instanceof Element) + && ((Node) detail.getTarget()).getParentNode() instanceof Document; + } + public static class ComparisonController2DifferenceEvaluator implements net.sf.xmlunit.diff.DifferenceEvaluator { private final ComparisonController cc; Modified: trunk/xmlunit/src/main/net-core/diff/AbstractDifferenceEngine.cs =================================================================== --- trunk/xmlunit/src/main/net-core/diff/AbstractDifferenceEngine.cs 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/main/net-core/diff/AbstractDifferenceEngine.cs 2010-09-14 06:55:50 UTC (rev 473) @@ -26,16 +26,17 @@ public event ComparisonListener MatchListener; public event ComparisonListener DifferenceListener; - private ElementSelector elementSelector = ElementSelectors.Default; - public virtual ElementSelector ElementSelector { + private INodeMatcher nodeMatcher = + new DefaultNodeMatcher(ElementSelectors.Default); + public virtual INodeMatcher NodeMatcher { set { if (value == null) { - throw new ArgumentNullException("element selector"); + throw new ArgumentNullException("node matcher"); } - elementSelector = value; + nodeMatcher = value; } get { - return elementSelector; + return nodeMatcher; } } Modified: trunk/xmlunit/src/main/net-core/diff/DOMDifferenceEngine.xml =================================================================== --- trunk/xmlunit/src/main/net-core/diff/DOMDifferenceEngine.xml 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/main/net-core/diff/DOMDifferenceEngine.xml 2010-09-14 06:55:50 UTC (rev 473) @@ -353,45 +353,48 @@ XPathContext controlContext, IEnumerable<XmlNode> testSeq, XPathContext testContext) { - IList<XmlNode> controlList = new List<XmlNode>(controlSeq); - IList<XmlNode> testList = new List<XmlNode>(testSeq); - IDictionary<int, object> unmatchedTestIndexes = - new SortedDictionary<int, object>(); - for (int i = 0; i < testList.Count; i++) { - unmatchedTestIndexes.Add(i, DUMMY); - } ]]></literal> <lastResultDef/> <literal><![CDATA[ // if there are no children on either Node, the result is equal lastResult = ComparisonResult.EQUAL; - Match lastMatch = new Match(null, -1); - for (int i = 0; i < controlList.Count; i++) { - Match testMatch = FindMatchingNode(controlList[i], testList, - lastMatch.Index, - unmatchedTestIndexes); - controlContext.NavigateToChild(i); + + IEnumerable<KeyValuePair<XmlNode, XmlNode>> matches = + NodeMatcher.Match(controlSeq, testSeq); + IList<XmlNode> controlList = new List<XmlNode>(controlSeq); + IList<XmlNode> testList = new List<XmlNode>(testSeq); + IDictionary<XmlNode, object> seen = + new Dictionary<XmlNode, object>(); + foreach (KeyValuePair<XmlNode, XmlNode> pair in matches) { + XmlNode control = pair.Key; + seen[control] = DUMMY; + XmlNode test = pair.Value; + seen[test] = DUMMY; + int controlIndex = controlList.IndexOf(control); + int testIndex = testList.IndexOf(test); + controlContext.NavigateToChild(controlIndex); + testContext.NavigateToChild(testIndex); try { - if (testMatch != null) { - XmlNode control = controlList[i]; - XmlNode test = testMatch.Node; - testContext.NavigateToChild(testMatch.Index); - try { ]]></literal> <compareExpr type="CHILD_NODELIST_SEQUENCE" - controlExpr="i" - testExpr="testMatch.Index" + controlExpr="controlIndex" + testExpr="testIndex" /> <compareMethodExpr method="CompareNodes" controlExpr="control" testExpr="test"/> <literal><![CDATA[ - unmatchedTestIndexes.Remove(testMatch.Index); - lastMatch = testMatch; - } finally { - testContext.NavigateToParent(); - } - } else { + } finally { + testContext.NavigateToParent(); + controlContext.NavigateToParent(); + } + } + + int controlSize = controlList.Count; + for (int i = 0; i < controlSize; i++) { + if (!seen.ContainsKey(controlList[i])) { + controlContext.NavigateToChild(i); + try { lastResult = Compare(new Comparison(ComparisonType.CHILD_LOOKUP, controlList[i], @@ -399,26 +402,30 @@ controlList[i], null, null, null)); ]]></literal> - <if-return-boilerplate/> - <literal><![CDATA[ + <if-return-boilerplate/> + <literal><![CDATA[ + } finally { + controlContext.NavigateToParent(); } - } finally { - controlContext.NavigateToParent(); } } - foreach (int i in unmatchedTestIndexes.Keys) { - testContext.NavigateToChild(i); - try { - lastResult = - Compare(new Comparison(ComparisonType.CHILD_LOOKUP, - null, null, null, - testList[i], GetXPath(testContext), - testList[i])); + int testSize = testList.Count; + for (int i = 0; i < testSize; i++) { + if (!seen.ContainsKey(testList[i])) { + testContext.NavigateToChild(i); + try { + lastResult = + Compare(new Comparison(ComparisonType.CHILD_LOOKUP, + null, null, null, + testList[i], + GetXPath(testContext), + testList[i])); ]]></literal> <if-return-boilerplate/> <literal><![CDATA[ - } finally { - testContext.NavigateToParent(); + } finally { + testContext.NavigateToParent(); + } } } return lastResult; @@ -491,52 +498,6 @@ return null; } - private Match FindMatchingNode(XmlNode searchFor, - IList<XmlNode> searchIn, - int indexOfLastMatch, - IDictionary<int, object> - availableIndexes) { - int searchSize = searchIn.Count; - for (int i = indexOfLastMatch + 1; i < searchSize; i++) { - if (!availableIndexes.ContainsKey(i)) { - continue; - } - if (NodesMatch(searchFor, searchIn[i])) { - return new Match(searchIn[i], i); - } - } - for (int i = 0; i < indexOfLastMatch; i++) { - if (!availableIndexes.ContainsKey(i)) { - continue; - } - if (NodesMatch(searchFor, searchIn[i])) { - return new Match(searchIn[i], i); - } - } - return null; - } - - private bool NodesMatch(XmlNode n1, XmlNode n2) { - if (n1 is XmlElement && n2 is XmlElement) { - return ElementSelector(n1 as XmlElement, n2 as XmlElement); - } - ComparisonResult r = - CompareDontFire(new Comparison(ComparisonType.NODE_TYPE, - n1, null, n1.NodeType, - n2, null, n2.NodeType)); - return r != ComparisonResult.CRITICAL - && r != ComparisonResult.DIFFERENT; - } - - internal class Match { - internal readonly XmlNode Node; - internal readonly int Index; - internal Match(XmlNode match, int index) { - Node = match; - Index = index; - } - } - private static XPathContext.INodeInfo TO_NODE_INFO(XmlNode n) { return new XPathContext.DOMNodeInfo(n); } Added: trunk/xmlunit/src/main/net-core/diff/DefaultNodeMatcher.cs =================================================================== --- trunk/xmlunit/src/main/net-core/diff/DefaultNodeMatcher.cs (rev 0) +++ trunk/xmlunit/src/main/net-core/diff/DefaultNodeMatcher.cs 2010-09-14 06:55:50 UTC (rev 473) @@ -0,0 +1,122 @@ +/* + This file is licensed to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Collections.Generic; +using System.Xml; + +namespace net.sf.xmlunit.diff { + + /// <summary> + /// Strategy that matches control and tests nodes for comparison. + /// </summary> + public class DefaultNodeMatcher : INodeMatcher { + private static readonly object DUMMY = new object(); + + private readonly ElementSelector elementSelector; + private readonly NodeTypeMatcher nodeTypeMatcher; + + public DefaultNodeMatcher() : this(ElementSelectors.Default) { + } + + public DefaultNodeMatcher(ElementSelector es) : + this(es, DefaultNodeTypeMatcher) { + } + + public DefaultNodeMatcher(ElementSelector es, NodeTypeMatcher ntm) { + elementSelector = es; + nodeTypeMatcher = ntm; + } + + public IEnumerable<KeyValuePair<XmlNode, XmlNode>> + Match(IEnumerable<XmlNode> controlNodes, + IEnumerable<XmlNode> testNodes) { + LinkedList<KeyValuePair<XmlNode, XmlNode>> matches = + new LinkedList<KeyValuePair<XmlNode, XmlNode>>(); + IList<XmlNode> controlList = new List<XmlNode>(controlNodes); + IList<XmlNode> testList = new List<XmlNode>(testNodes); + IDictionary<int, object> unmatchedTestIndexes = + new Dictionary<int, object>(); + for (int i = 0; i < testList.Count; i++) { + unmatchedTestIndexes.Add(i, DUMMY); + } + int controlSize = controlList.Count; + MatchInfo lastMatch = new MatchInfo(null, -1); + for (int i = 0; i < controlSize; i++) { + XmlNode control = controlList[i]; + MatchInfo testMatch = FindMatchingNode(control, testList, + lastMatch.Index, + unmatchedTestIndexes); + if (testMatch != null) { + unmatchedTestIndexes.Remove(testMatch.Index); + matches.AddLast(new KeyValuePair<XmlNode, + XmlNode>(control, testMatch.Node)); + } + } + return matches; + } + + private MatchInfo FindMatchingNode(XmlNode searchFor, + IList<XmlNode> searchIn, + int indexOfLastMatch, + IDictionary<int, object> + availableIndexes) { + int searchSize = searchIn.Count; + for (int i = indexOfLastMatch + 1; i < searchSize; i++) { + if (!availableIndexes.ContainsKey(i)) { + continue; + } + if (NodesMatch(searchFor, searchIn[i])) { + return new MatchInfo(searchIn[i], i); + } + } + for (int i = 0; i < indexOfLastMatch; i++) { + if (!availableIndexes.ContainsKey(i)) { + continue; + } + if (NodesMatch(searchFor, searchIn[i])) { + return new MatchInfo(searchIn[i], i); + } + } + return null; + } + + private bool NodesMatch(XmlNode n1, XmlNode n2) { + if (n1 is XmlElement && n2 is XmlElement) { + return elementSelector(n1 as XmlElement, n2 as XmlElement); + } + return nodeTypeMatcher(n1.NodeType, n2.NodeType); + } + + internal class MatchInfo { + internal readonly XmlNode Node; + internal readonly int Index; + internal MatchInfo(XmlNode match, int index) { + Node = match; + Index = index; + } + } + + public delegate bool NodeTypeMatcher(XmlNodeType controlType, + XmlNodeType testType); + + public static bool DefaultNodeTypeMatcher(XmlNodeType controlType, + XmlNodeType testType) { + return controlType == testType + || (controlType == XmlNodeType.CDATA + && testType == XmlNodeType.Text) + || (controlType == XmlNodeType.Text + && testType == XmlNodeType.CDATA); + } + } +} \ No newline at end of file Property changes on: trunk/xmlunit/src/main/net-core/diff/DefaultNodeMatcher.cs ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/xmlunit/src/main/net-core/diff/IDifferenceEngine.cs =================================================================== --- trunk/xmlunit/src/main/net-core/diff/IDifferenceEngine.cs 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/main/net-core/diff/IDifferenceEngine.cs 2010-09-14 06:55:50 UTC (rev 473) @@ -38,9 +38,9 @@ event ComparisonListener DifferenceListener; /// <summary> - /// Sets the strategy for selecting elements to compare. + /// Sets the strategy for selecting nodes to compare. /// </summary> - ElementSelector ElementSelector { set; } + INodeMatcher NodeMatcher { set; } /// <summary> /// Determines whether the comparison should stop after given Added: trunk/xmlunit/src/main/net-core/diff/INodeMatcher.cs =================================================================== --- trunk/xmlunit/src/main/net-core/diff/INodeMatcher.cs (rev 0) +++ trunk/xmlunit/src/main/net-core/diff/INodeMatcher.cs 2010-09-14 06:55:50 UTC (rev 473) @@ -0,0 +1,32 @@ +/* + This file is licensed to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Collections.Generic; +using System.Xml; + +namespace net.sf.xmlunit.diff { + + /// <summary> + /// Strategy that matches control and tests nodes for comparison. + /// </summary> + public interface INodeMatcher { + /// <summary> + /// Matches control and test nodes against each other, returns + /// the matching pairs. + /// </summary> + IEnumerable<KeyValuePair<XmlNode, XmlNode>> + Match(IEnumerable<XmlNode> controlNodes, + IEnumerable<XmlNode> testNodes); + } +} Property changes on: trunk/xmlunit/src/main/net-core/diff/INodeMatcher.cs ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/xmlunit/src/tests/java-core/net/sf/xmlunit/diff/DOMDifferenceEngineTest.java =================================================================== --- trunk/xmlunit/src/tests/java-core/net/sf/xmlunit/diff/DOMDifferenceEngineTest.java 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/tests/java-core/net/sf/xmlunit/diff/DOMDifferenceEngineTest.java 2010-09-14 06:55:50 UTC (rev 473) @@ -634,7 +634,7 @@ assertEquals(1, ex.invoked); d = new DOMDifferenceEngine(); - d.setElementSelector(ElementSelectors.byName); + d.setNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName)); ex = new DiffExpecter(ComparisonType.CHILD_LOOKUP, "/bar[1]", null); d.addDifferenceListener(ex); d.setDifferenceEvaluator(DifferenceEvaluators.DefaultStopWhenDifferent); @@ -751,7 +751,7 @@ } }; d.setDifferenceEvaluator(ev); - d.setElementSelector(ElementSelectors.byName); + d.setNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byName)); assertEquals(ComparisonResult.CRITICAL, d.compareNodes(e1, new XPathContext(), Modified: trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_DetailedDiff.java =================================================================== --- trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_DetailedDiff.java 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_DetailedDiff.java 2010-09-14 06:55:50 UTC (rev 473) @@ -418,7 +418,7 @@ diff.getTestNodeDetail().getXpathLocation()); // didn't find the second Apple element - diff = (Difference) l.get(1); + diff = (Difference) l.get(3); assertEquals(DifferenceConstants.CHILD_NODE_NOT_FOUND_ID, diff.getId()); assertEquals("Apple", diff.getControlNodeDetail().getValue()); @@ -429,7 +429,7 @@ diff.getTestNodeDetail().getXpathLocation()); // Banana's size attribute doesn't match - diff = (Difference) l.get(3); + diff = (Difference) l.get(2); assertEquals(DifferenceConstants.ATTR_VALUE_ID, diff.getId()); assertEquals("10", diff.getControlNodeDetail().getValue()); @@ -440,7 +440,7 @@ diff.getTestNodeDetail().getXpathLocation()); // Banana is the third child in control but the second one in test - diff = (Difference) l.get(2); + diff = (Difference) l.get(1); assertEquals("2", diff.getControlNodeDetail().getValue()); assertEquals("1", diff.getTestNodeDetail().getValue()); assertEquals("/Fruits[1]/Banana[1]", Modified: trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_NewDifferenceEngine.java =================================================================== --- trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_NewDifferenceEngine.java 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_NewDifferenceEngine.java 2010-09-14 06:55:50 UTC (rev 473) @@ -487,6 +487,10 @@ assertFalse("unexpected difference: " + listener.comparingWhat, listener.different); resetListener(); + listenToDifferences(test, control); + assertFalse("unexpected difference: " + listener.comparingWhat, + listener.different); + resetListener(); control = "<?xml version = \"1.0\" encoding = \"UTF-8\"?>" + "<!-- some comment -->" @@ -500,6 +504,10 @@ listenToDifferences(control, test); assertFalse("unexpected difference: " + listener.comparingWhat, listener.different); + resetListener(); + listenToDifferences(test, control); + assertFalse("unexpected difference: " + listener.comparingWhat, + listener.different); } private void listenToDifferences(String control, String test) Modified: trunk/xmlunit/src/tests/net-core/diff/DOMDifferenceEngineTest.cs =================================================================== --- trunk/xmlunit/src/tests/net-core/diff/DOMDifferenceEngineTest.cs 2010-09-13 11:49:10 UTC (rev 472) +++ trunk/xmlunit/src/tests/net-core/diff/DOMDifferenceEngineTest.cs 2010-09-14 06:55:50 UTC (rev 473) @@ -627,7 +627,7 @@ Assert.AreEqual(1, ex.invoked); d = new DOMDifferenceEngine(); - d.ElementSelector = ElementSelectors.ByName; + d.NodeMatcher = new DefaultNodeMatcher(ElementSelectors.ByName); ex = new DiffExpecter(ComparisonType.CHILD_LOOKUP); d.DifferenceListener += ex.ComparisonPerformed; d.DifferenceEvaluator = @@ -743,7 +743,7 @@ outcome); }; d.DifferenceEvaluator = ev; - d.ElementSelector = ElementSelectors.ByName; + d.NodeMatcher = new DefaultNodeMatcher(ElementSelectors.ByName); Assert.AreEqual(ComparisonResult.CRITICAL, d.CompareNodes(e1, new XPathContext(), This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |