/// <summary> /// Compares properties of XML declaration. /// </summary> private ComparisonState CompareDeclarations(XmlDeclaration control, XPathContext controlContext, XmlDeclaration test, XPathContext testContext) { string controlVersion = control == null ? "1.0" : control.Version; string testVersion = test == null ? "1.0" : test.Version; string controlStandalone = control == null ? string.Empty : control.Standalone; string testStandalone = test == null ? string.Empty : test.Standalone; string controlEncoding = control != null ? control.Encoding : string.Empty; string testEncoding = test != null ? test.Encoding : string.Empty; return(Compare(new Comparison(ComparisonType.XML_VERSION, control, GetXPath(controlContext), controlVersion, GetParentXPath(controlContext), test, GetXPath(testContext), testVersion, GetParentXPath(testContext))) .AndThen(new Comparison(ComparisonType.XML_STANDALONE, control, GetXPath(controlContext), controlStandalone, GetParentXPath(controlContext), test, GetXPath(testContext), testStandalone, GetParentXPath(testContext))) .AndThen(new Comparison(ComparisonType.XML_ENCODING, control, GetXPath(controlContext), controlEncoding, GetParentXPath(controlContext), test, GetXPath(testContext), testEncoding, GetParentXPath(testContext)))); }
/// <summary> /// Creates an instance of ChildNodeXPathContextProvider. /// </summary> /// <param name="parentContext">parentContext context of the /// parent of all Nodes ever expected to be passed in as /// arguments to Map. This XPathContext must be "positioned /// at" the parent element and already know about all its /// children.</param> /// <param name="children">all child nodes of the parent in /// the same order they are known to the XPathContext.</param> internal ChildNodeXPathContextProvider(XPathContext parentContext, IEnumerable <XmlNode> children) { this.xpathContext = (XPathContext)parentContext.Clone(); childIndex = children.Select((n, i) => new { Node = n, Index = i }) .ToDictionary(t => t.Node, t => t.Index); }
internal XPathContext Map(XmlNode n) { XPathContext ctx = (XPathContext)xpathContext.Clone(); ctx.NavigateToChild(childIndex[n]); return(ctx); }
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); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(1); Assert.AreEqual("/foo[2]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(2); Assert.AreEqual("/bar[1]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(3); Assert.AreEqual("/foo[3]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); }
public void ShouldReturnACopyOfOriginalXPathContext() { ChildNodeXPathContextProvider p = new ChildNodeXPathContextProvider(ctx, elements); XPathContext provided = p.Map(elements[0]); Assert.AreNotSame(ctx, provided); }
public void ShouldCreateCopyOnClone() { List <Element> l = new List <Element>(); l.Add(new Element("foo")); l.Add(new Element("foo")); l.Add(new Element("bar")); XPathContext ctx = new XPathContext(); ctx.SetChildren(l); ctx.NavigateToChild(1); Assert.AreEqual("/foo[2]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); XPathContext clone = (XPathContext)ctx.Clone(); Assert.AreEqual("/foo[2]", clone.XPath); Assert.AreEqual("/", clone.ParentXPath); Assert.AreNotSame(ctx, clone); clone.NavigateToParent(); clone.NavigateToChild(2); Assert.AreEqual("/bar[1]", clone.XPath); Assert.AreEqual("/", clone.ParentXPath); Assert.AreEqual("/foo[2]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); }
public void ShouldFindCorrectChildIndex() { ChildNodeXPathContextProvider p = new ChildNodeXPathContextProvider(ctx, elements); XPathContext provided = p.Map(elements[1]); Assert.AreEqual("/foo[2]", provided.XPath); }
public void ElementsAndNs() { List <Element> l = new List <Element>(); l.Add(new Element("foo", "urn:foo:foo")); l.Add(new Element("foo")); l.Add(new Element("foo", "urn:foo:bar")); Dictionary <string, string> m = new Dictionary <string, string>(); m["bar"] = "urn:foo:bar"; XPathContext ctx = new XPathContext(m); ctx.SetChildren(l); ctx.NavigateToChild(0); Assert.AreEqual("/foo[1]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(1); Assert.AreEqual("/foo[2]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(2); Assert.AreEqual("/bar:foo[1]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); }
private Func <ComparisonState> UnmatchedTestNodes(IList <XmlNode> testListForXpath, IList <XmlNode> testList, XPathContext testContext, ICollection <XmlNode> seen, XPathContext controlContext) { return(() => { ComparisonState chain = new OngoingComparisonState(this); int testSize = testList.Count; for (int i = 0; i < testSize; i++) { if (!seen.Contains(testList[i])) { testContext.NavigateToChild(testListForXpath.IndexOf(testList[i])); try { chain = chain .AndThen(new Comparison(ComparisonType.CHILD_LOOKUP, null, null, null, GetXPath(controlContext), testList[i], GetXPath(testContext), testList[i].GetQName(), GetParentXPath(testContext))); } finally { testContext.NavigateToParent(); } } } return chain; }); }
/// <summary> /// Compares xsi:type attribute values /// </summary> private ComparisonState CompareXsiType(XmlAttribute control, XPathContext controlContext, XmlAttribute test, XPathContext testContext) { bool mustChangeControlContext = control != null; bool mustChangeTestContext = test != null; if (!mustChangeControlContext && !mustChangeTestContext) { return(new OngoingComparisonState(this)); } bool attributePresentOnBothSides = mustChangeControlContext && mustChangeTestContext; try { XmlQualifiedName controlAttrName = null; if (mustChangeControlContext) { controlAttrName = control.GetQName(); controlContext.AddAttribute(controlAttrName); controlContext.NavigateToAttribute(controlAttrName); } XmlQualifiedName testAttrName = null; if (mustChangeTestContext) { testAttrName = test.GetQName(); testContext.AddAttribute(testAttrName); testContext.NavigateToAttribute(testAttrName); } return(Compare(new Comparison(ComparisonType.ATTR_NAME_LOOKUP, control, GetXPath(controlContext), controlAttrName, GetParentXPath(controlContext), test, GetXPath(testContext), testAttrName, GetParentXPath(testContext))) .AndIfTrueThen(attributePresentOnBothSides, () => CompareAttributeExplicitness(control, controlContext, test, testContext)) .AndIfTrueThen(attributePresentOnBothSides, new Comparison(ComparisonType.ATTR_VALUE, control, GetXPath(controlContext), ValueAsQName(control), GetParentXPath(controlContext), test, GetXPath(testContext), ValueAsQName(test), GetParentXPath(testContext)))); } finally { if (mustChangeControlContext) { controlContext.NavigateToParent(); } if (mustChangeTestContext) { testContext.NavigateToParent(); } } }
private XPathContext GetXPathForAttribute(XmlAttribute a) { XPathContext elementContext = GetXPathForNonAttribute(a.OwnerElement); XmlQualifiedName q = Nodes.GetQName(a); elementContext.AddAttribute(q); elementContext.NavigateToAttribute(q); return(elementContext); }
/// <summary> /// Compares whether two attributes are specified explicitly. /// </summary> private ComparisonState CompareAttributeExplicitness(XmlAttribute control, XPathContext controlContext, XmlAttribute test, XPathContext testContext) { return(Compare(new Comparison(ComparisonType.ATTR_VALUE_EXPLICITLY_SPECIFIED, control, GetXPath(controlContext), control.Specified, GetParentXPath(controlContext), test, GetXPath(testContext), test.Specified, GetParentXPath(testContext)))); }
/// <summary> /// Compares textual content. /// </summary> private ComparisonState CompareCharacterData(XmlCharacterData control, XPathContext controlContext, XmlCharacterData test, XPathContext testContext) { return(Compare(new Comparison(ComparisonType.TEXT_VALUE, control, GetXPath(controlContext), control.Data, GetParentXPath(controlContext), test, GetXPath(testContext), test.Data, GetParentXPath(testContext)))); }
public void Init() { doc = new XmlDocument(); elements = new List <XmlNode>(); elements.Add(doc.CreateElement("foo")); elements.Add(doc.CreateElement("foo")); elements.Add(doc.CreateElement("bar")); elements.Add(doc.CreateElement("foo")); ctx = new XPathContext(); ctx.SetChildren(elements.Select(ElementSelectors.TO_NODE_INFO)); }
/// <summary> /// Creates a deep copy of this XPathContext. /// </summary> public object Clone() { XPathContext c = (XPathContext)MemberwiseClone(); c.path = new LinkedList <Level>(); foreach (Level l in path) { c.path.AddLast((Level)l.Clone()); } return(c); }
public void singleAttribute() { XPathContext ctx = new XPathContext(); ctx.SetChildren(Linqy.Singleton(new Element("foo"))); ctx.NavigateToChild(0); ctx.AddAttribute(new XmlQualifiedName("bar")); ctx.NavigateToAttribute(new XmlQualifiedName("bar")); Assert.AreEqual("/foo[1]/@bar", ctx.XPath); Assert.AreEqual("/foo[1]", ctx.ParentXPath); }
/// <summary> /// Compares element's node properties, in particular the /// element's name and its attributes. /// </summary> private ComparisonState CompareElements(XmlElement control, XPathContext controlContext, XmlElement test, XPathContext testContext) { return(Compare(new Comparison(ComparisonType.ELEMENT_TAG_NAME, control, GetXPath(controlContext), Nodes.GetQName(control).Name, GetParentXPath(controlContext), test, GetXPath(testContext), Nodes.GetQName(test).Name, GetParentXPath(testContext))) .AndThen(() => CompareElementAttributes(control, controlContext, test, testContext))); }
/// <summary> /// Compares properties of an attribute. /// </summary> private ComparisonState CompareAttributes(XmlAttribute control, XPathContext controlContext, XmlAttribute test, XPathContext testContext) { return(CompareAttributeExplicitness(control, controlContext, test, testContext) .AndThen(new Comparison(ComparisonType.ATTR_VALUE, control, GetXPath(controlContext), control.Value, GetParentXPath(controlContext), test, GetXPath(testContext), test.Value, GetParentXPath(testContext)))); }
/// <summary> /// Matches nodes of two node lists and invokes compareNode on /// each pair. /// </summary> /// <remarks> /// Also performs CHILD_LOOKUP comparisons for each node that /// couldn't be matched to one of the "other" list. /// </remarks> private ComparisonState CompareNodeLists(IEnumerable <XmlNode> allControlChildren, IEnumerable <XmlNode> controlSeq, XPathContext controlContext, IEnumerable <XmlNode> allTestChildren, IEnumerable <XmlNode> testSeq, XPathContext testContext) { ComparisonState chain = new OngoingComparisonState(this); IEnumerable <KeyValuePair <XmlNode, XmlNode> > matches = NodeMatcher.Match(controlSeq, testSeq); IList <XmlNode> controlListForXpath = new List <XmlNode>(allControlChildren); IList <XmlNode> testListForXpath = new List <XmlNode>(allTestChildren); IList <XmlNode> controlList = new List <XmlNode>(controlSeq); IList <XmlNode> testList = new List <XmlNode>(testSeq); ICollection <XmlNode> seen = new HashSet <XmlNode>(); foreach (KeyValuePair <XmlNode, XmlNode> pair in matches) { XmlNode control = pair.Key; seen.Add(control); XmlNode test = pair.Value; seen.Add(test); int controlIndexForXpath = controlListForXpath.IndexOf(control); int testIndexForXpath = testListForXpath.IndexOf(test); int controlIndex = controlList.IndexOf(control); int testIndex = testList.IndexOf(test); controlContext.NavigateToChild(controlIndexForXpath); testContext.NavigateToChild(testIndexForXpath); try { chain = chain.AndThen(new Comparison(ComparisonType.CHILD_NODELIST_SEQUENCE, control, GetXPath(controlContext), controlIndex, GetParentXPath(controlContext), test, GetXPath(testContext), testIndex, GetParentXPath(testContext))) .AndThen(() => CompareNodes(control, controlContext, test, testContext)); } finally { testContext.NavigateToParent(); controlContext.NavigateToParent(); } } return(chain .AndThen(UnmatchedControlNodes(controlListForXpath, controlList, controlContext, seen, testContext)) .AndThen(UnmatchedTestNodes(testListForXpath, testList, testContext, seen, controlContext))); }
/// <summary> /// Compares element's attributes. /// </summary> private ComparisonState CompareElementAttributes(XmlElement control, XPathContext controlContext, XmlElement test, XPathContext testContext) { Attributes controlAttributes = SplitAttributes(control.Attributes); controlContext .AddAttributes(controlAttributes.RemainingAttributes .Select <XmlAttribute, XmlQualifiedName>(Nodes.GetQName)); Attributes testAttributes = SplitAttributes(test.Attributes); testContext .AddAttributes(testAttributes.RemainingAttributes .Select <XmlAttribute, XmlQualifiedName>(Nodes.GetQName)); return(Compare(new Comparison(ComparisonType.ELEMENT_NUM_ATTRIBUTES, control, GetXPath(controlContext), controlAttributes.RemainingAttributes.Count, GetParentXPath(controlContext), test, GetXPath(testContext), testAttributes.RemainingAttributes.Count, GetParentXPath(testContext))) .AndThen(() => CompareXsiType(controlAttributes.Type, controlContext, testAttributes.Type, testContext)) .AndThen(new Comparison(ComparisonType.SCHEMA_LOCATION, control, GetXPath(controlContext), controlAttributes.SchemaLocation != null ? controlAttributes.SchemaLocation.Value : null, GetParentXPath(controlContext), test, GetXPath(testContext), testAttributes.SchemaLocation != null ? testAttributes.SchemaLocation.Value : null, GetParentXPath(testContext))) .AndThen(new Comparison(ComparisonType.NO_NAMESPACE_SCHEMA_LOCATION, control, GetXPath(controlContext), controlAttributes.NoNamespaceSchemaLocation != null ? controlAttributes.NoNamespaceSchemaLocation.Value : null, GetParentXPath(controlContext), test, GetXPath(testContext), testAttributes.NoNamespaceSchemaLocation != null ? testAttributes.NoNamespaceSchemaLocation.Value : null, GetParentXPath(testContext))) .AndThen(NormalAttributeComparer(control, controlContext, controlAttributes, test, testContext, testAttributes))); }
private Func <ComparisonState> CompareChildren(XPathContext controlContext, IEnumerable <XmlNode> controlChildren, XPathContext testContext, IEnumerable <XmlNode> testChildren) { return(() => { controlContext .SetChildren(controlChildren.Select <XmlNode, XPathContext.INodeInfo> (ElementSelectors.TO_NODE_INFO)); testContext .SetChildren(testChildren.Select <XmlNode, XPathContext.INodeInfo> (ElementSelectors.TO_NODE_INFO)); return CompareNodeLists(controlChildren, controlContext, testChildren, testContext); }); }
/// <summary> /// Compares properties of a processing instruction. /// </summary> private ComparisonState CompareProcessingInstructions(XmlProcessingInstruction control, XPathContext controlContext, XmlProcessingInstruction test, XPathContext testContext) { return(Compare(new Comparison(ComparisonType.PROCESSING_INSTRUCTION_TARGET, control, GetXPath(controlContext), control.Target, GetParentXPath(controlContext), test, GetXPath(testContext), test.Target, GetParentXPath(testContext))) .AndThen(new Comparison(ComparisonType.PROCESSING_INSTRUCTION_DATA, control, GetXPath(controlContext), control.Data, GetParentXPath(controlContext), test, GetXPath(testContext), test.Data, GetParentXPath(testContext)))); }
public void Mixed() { List <XPathContext.INodeInfo> l = new List <XPathContext.INodeInfo>(); l.Add(new Text()); l.Add(new Comment()); l.Add(new CDATA()); l.Add(new PI()); l.Add(new CDATA()); l.Add(new Comment()); l.Add(new PI()); l.Add(new Text()); XPathContext ctx = new XPathContext(); ctx.SetChildren(l); ctx.NavigateToChild(0); Assert.AreEqual("/text()[1]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(1); Assert.AreEqual("/comment()[1]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(2); Assert.AreEqual("/text()[2]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(3); Assert.AreEqual("/processing-instruction()[1]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(4); Assert.AreEqual("/text()[3]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(5); Assert.AreEqual("/comment()[2]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(6); Assert.AreEqual("/processing-instruction()[2]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToChild(7); Assert.AreEqual("/text()[4]", ctx.XPath); Assert.AreEqual("/", ctx.ParentXPath); }
private XPathContext GetXPathForNonAttribute(XmlNode n) { XmlNode parent = n.ParentNode; if (parent == null || parent is XmlDocument) { return(new XPathContext(prefix2uri, n)); } XPathContext parentContext = GetXPathForNonAttribute(parent); IEnumerable <XmlNode> children = parent.ChildNodes.Cast <XmlNode>(); parentContext.SetChildren(children.Select <XmlNode, XPathContext.INodeInfo>(ElementSelectors.TO_NODE_INFO)); ChildNodeXPathContextProvider cn = new ChildNodeXPathContextProvider(parentContext, children); return(cn.Map(n)); }
/// <summary> /// Recursively compares two XML nodes. /// </summary> /// <remarks> /// Performs comparisons common to all node types, then performs /// the node type specific comparisons and finally recurses into /// the node's child lists. /// /// Stops as soon as any comparison returns ComparisonResult.CRITICAL. /// </remarks> internal ComparisonState CompareNodes(XmlNode control, XPathContext controlContext, XmlNode test, XPathContext testContext) { IEnumerable <XmlNode> allControlChildren = control.ChildNodes.Cast <XmlNode>(); IEnumerable <XmlNode> controlChildren = allControlChildren.Where(n => NodeFilter(n)); IEnumerable <XmlNode> allTestChildren = test.ChildNodes.Cast <XmlNode>(); IEnumerable <XmlNode> testChildren = allTestChildren.Where(n => NodeFilter(n)); return(Compare(new Comparison(ComparisonType.NODE_TYPE, control, GetXPath(controlContext), control.NodeType, GetParentXPath(controlContext), test, GetXPath(testContext), test.NodeType, GetParentXPath(testContext))) .AndThen(new Comparison(ComparisonType.NAMESPACE_URI, control, GetXPath(controlContext), control.NamespaceURI, GetParentXPath(controlContext), test, GetXPath(testContext), test.NamespaceURI, GetParentXPath(testContext))) .AndThen(new Comparison(ComparisonType.NAMESPACE_PREFIX, control, GetXPath(controlContext), control.Prefix, GetParentXPath(controlContext), test, GetXPath(testContext), test.Prefix, GetParentXPath(testContext))) .AndIfTrueThen(control.NodeType != XmlNodeType.Attribute, new Comparison(ComparisonType.CHILD_NODELIST_LENGTH, control, GetXPath(controlContext), controlChildren.Count(), GetParentXPath(controlContext), test, GetXPath(testContext), testChildren.Count(), GetParentXPath(testContext))) .AndThen(() => NodeTypeSpecificComparison(control, controlContext, test, testContext)) // and finally recurse into children .AndIfTrueThen(control.NodeType != XmlNodeType.Attribute, CompareChildren(controlContext, allControlChildren, controlChildren, testContext, allTestChildren, testChildren))); }
/// <summary> /// Compares document node, doctype and XML declaration properties /// </summary> private ComparisonState CompareDocuments(XmlDocument control, XPathContext controlContext, XmlDocument test, XPathContext testContext) { XmlDocumentType controlDt = FilterNode(control.DocumentType); XmlDocumentType testDt = FilterNode(test.DocumentType); return(Compare(new Comparison(ComparisonType.HAS_DOCTYPE_DECLARATION, control, GetXPath(controlContext), controlDt != null, GetParentXPath(controlContext), test, GetXPath(testContext), testDt != null, GetParentXPath(testContext))) .AndIfTrueThen(controlDt != null && testDt != null, () => CompareNodes(controlDt, controlContext, testDt, testContext)) .AndThen(() => CompareDeclarations(control.FirstChild as XmlDeclaration, controlContext, test.FirstChild as XmlDeclaration, testContext))); }
/// <summary> /// Compares properties of the doctype declaration. /// </summary> private ComparisonState CompareDocTypes(XmlDocumentType control, XPathContext controlContext, XmlDocumentType test, XPathContext testContext) { return(Compare(new Comparison(ComparisonType.DOCTYPE_NAME, control, GetXPath(controlContext), control.Name, GetParentXPath(controlContext), test, GetXPath(testContext), test.Name, GetParentXPath(testContext))) .AndThen(new Comparison(ComparisonType.DOCTYPE_PUBLIC_ID, control, GetXPath(controlContext), control.PublicId, GetParentXPath(controlContext), test, GetXPath(testContext), test.PublicId, GetParentXPath(testContext))) .AndThen(new Comparison(ComparisonType.DOCTYPE_SYSTEM_ID, control, GetXPath(controlContext), control.SystemId, GetParentXPath(controlContext), test, GetXPath(testContext), test.SystemId, GetParentXPath(testContext)))); }
public void AttributesAndNs() { Dictionary <string, string> m = new Dictionary <string, string>(); m["bar"] = "urn:foo:bar"; XPathContext ctx = new XPathContext(m); 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.AddAttributes(l); ctx.NavigateToAttribute(new XmlQualifiedName("baz")); Assert.AreEqual("/bar:foo[1]/@baz", ctx.XPath); Assert.AreEqual("/bar:foo[1]", ctx.ParentXPath); ctx.NavigateToParent(); ctx.NavigateToAttribute(new XmlQualifiedName("baz", "urn:foo:bar")); Assert.AreEqual("/bar:foo[1]/@bar:baz", ctx.XPath); Assert.AreEqual("/bar:foo[1]", ctx.ParentXPath); ctx.NavigateToParent(); }
/// <summary> /// Returns a string representation of the given XPathContext's parent context. /// </summary> /// <param name="ctx">the XPath to evaluate</param> /// <returns>the stringified XPath of the parent or null if /// the XPathContext was null</returns> protected static string GetParentXPath(XPathContext ctx) { return(ctx == null ? null : ctx.ParentXPath); }
private Func <ComparisonState> NormalAttributeComparer(XmlElement control, XPathContext controlContext, Attributes controlAttributes, XmlElement test, XPathContext testContext, Attributes testAttributes) { return(() => { ComparisonState chain = new OngoingComparisonState(this); ICollection <XmlAttribute> foundTestAttributes = new HashSet <XmlAttribute>(); foreach (XmlAttribute controlAttr in controlAttributes.RemainingAttributes) { XmlQualifiedName controlAttrName = controlAttr.GetQName(); XmlAttribute testAttr = FindMatchingAttr(testAttributes.RemainingAttributes, controlAttr); XmlQualifiedName testAttrName = testAttr != null ? testAttr.GetQName() : null; controlContext.NavigateToAttribute(controlAttrName); try { chain = chain.AndThen(new Comparison(ComparisonType.ATTR_NAME_LOOKUP, control, GetXPath(controlContext), controlAttrName, GetParentXPath(controlContext), test, GetXPath(testContext), testAttrName, GetParentXPath(testContext))); if (testAttr != null) { testContext.NavigateToAttribute(testAttrName); try { chain = chain.AndThen(() => CompareNodes(controlAttr, controlContext, testAttr, testContext)); foundTestAttributes.Add(testAttr); } finally { testContext.NavigateToParent(); } } } finally { controlContext.NavigateToParent(); } } return chain.AndThen(() => { ComparisonState secondChain = new OngoingComparisonState(this); foreach (XmlAttribute testAttr in testAttributes.RemainingAttributes) { if (!foundTestAttributes.Contains(testAttr)) { XmlQualifiedName testAttrName = testAttr.GetQName(); testContext.NavigateToAttribute(testAttrName); try { secondChain = secondChain .AndThen(new Comparison(ComparisonType.ATTR_NAME_LOOKUP, control, GetXPath(controlContext), null, GetParentXPath(controlContext), test, GetXPath(testContext), testAttrName, GetParentXPath(testContext))); } finally { testContext.NavigateToParent(); } } } return secondChain; }); }); }