/// <summary> /// Compares two comments using the indicated comparison options. /// </summary> /// <param name="c1">The first comment to compare.</param> /// <param name="c2">The second comment to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the comments are equal, false otherwise.</returns> public static bool DeepEquals(this XComment c1, XComment c2, XNodeComparisonOptions options) { if ((c1 ?? c2) == null) return true; if ((c1 == null) || (c2 == null)) return false; // They are not both null, so if either is, then the other isn't return c1.Value == c2.Value; }
/// <summary> /// Compares two processing instructions using the indicated comparison options. /// </summary> /// <param name="p1">The first processing instruction to compare.</param> /// <param name="p2">The second processing instruction to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the processing instructions are equal, false otherwise.</returns> public static bool DeepEquals(this XProcessingInstruction p1, XProcessingInstruction p2, XNodeComparisonOptions options) { if ((p1 ?? p2) == null) return true; if ((p1 == null) || (p2 == null)) return false; // They are not both null, so if either is, then the other isn't return ((p1.Target == p2.Target) && (p1.Data == p1.Data)); }
/// <summary> /// Checks if two elements have equal content. /// </summary> /// <param name="e1">The first element to compare.</param> /// <param name="e2">The second element to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the element contents are equal, false otherwise.</returns> private static bool ContentsEqual(XElement e1, XElement e2, XNodeComparisonOptions options) { if (e1.Value != e2.Value) return false; if ((e1.IsEmpty != e2.IsEmpty) && ((options & XNodeComparisonOptions.EmptyTagStyle) == XNodeComparisonOptions.EmptyTagStyle)) return false; if (e1.IsEmpty && e2.IsEmpty) return true; var nodes1 = e1.Nodes(); var nodes2 = e2.Nodes(); // Exclude comments and notations if ((options & XNodeComparisonOptions.CommentsAndNotations) != XNodeComparisonOptions.CommentsAndNotations) { nodes1 = nodes1.Where(x => x.NodeType != XmlNodeType.Comment && x.NodeType != XmlNodeType.Notation); nodes2 = nodes2.Where(x => x.NodeType != XmlNodeType.Comment && x.NodeType != XmlNodeType.Notation); } // Order the nodes if required if ((options & XNodeComparisonOptions.ElementOrdering) != XNodeComparisonOptions.ElementOrdering) { nodes1 = nodes1.OrderBy(x => x.ToString()); nodes2 = nodes2.OrderBy(x => x.ToString()); } var enum1 = nodes1.GetEnumerator(); var enum2 = nodes2.GetEnumerator(); var next1 = enum1.MoveNext(); var next2 = enum2.MoveNext(); while (next1 && next2) { if (!DeepEquals(enum1.Current, enum2.Current, options)) return false; next1 = enum1.MoveNext(); next2 = enum2.MoveNext(); } // They have the same number of elements if these are equal return next1 == next2; }
/// <summary> /// Checks if two elements have equal attributes. /// </summary> /// <param name="e1">The first element to compare.</param> /// <param name="e2">The second element to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the element attributes are equal, false otherwise.</returns> private static bool AttributesEqual(XElement e1, XElement e2, XNodeComparisonOptions options) { // They must both have or not have attributes, and if they don't we're good and can exit early if (e1.HasAttributes != e2.HasAttributes) return false; if (!e1.HasAttributes) return true; var attributes1 = e1.Attributes(); var attributes2 = e2.Attributes(); // We will ignore attribute ordering if ((options & XNodeComparisonOptions.AttributeOrdering) != XNodeComparisonOptions.AttributeOrdering) { attributes1 = attributes1.OrderBy(x => x.Name.Namespace.NamespaceName + x.Name.LocalName); attributes2 = attributes2.OrderBy(x => x.Name.Namespace.NamespaceName + x.Name.LocalName); } var enum1 = attributes1.GetEnumerator(); var enum2 = attributes2.GetEnumerator(); var next1 = enum1.MoveNext(); var next2 = enum2.MoveNext(); while (next1 && next2) { if (enum1.Current.IsNamespaceDeclaration != enum2.Current.IsNamespaceDeclaration) return false; if (!enum1.Current.IsNamespaceDeclaration || ((options & XNodeComparisonOptions.NamespacePrefix) == XNodeComparisonOptions.NamespacePrefix)) if (!DeepEquals(enum1.Current, enum2.Current, options)) return false; next1 = enum1.MoveNext(); next2 = enum2.MoveNext(); } // They have the same number of elements if these are equal return next1 == next2; }
/// <summary> /// Compares two names. /// </summary> /// <param name="n1">The first name to compare.</param> /// <param name="n2">The second name to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the elements are equal, false otherwise.</returns> private static bool CompareNames(XName n1, XName n2, XNodeComparisonOptions options) { // Compare the name if ((n1.LocalName != n2.LocalName) || (n1.Namespace != n2.Namespace)) return false; if ((options & XNodeComparisonOptions.NamespacePrefix) == XNodeComparisonOptions.NamespacePrefix && n1.NamespaceName != n2.NamespaceName) return false; return true; }
/// <summary> /// Compares two nodes using the indicated comparison options. /// </summary> /// <param name="n1">The first node to compare.</param> /// <param name="n2">The second node to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the nodes are equal, false otherwise.</returns> public static bool DeepEquals(this XNode n1, XNode n2, XNodeComparisonOptions options) { if ((n1 ?? n2) == null) return true; if ((n1 == null) || (n2 == null)) return false; // They are not both null, so if either is, then the other isn't if ((n1 is XElement) && (n2 is XElement)) return DeepEquals((XElement)n1, (XElement)n2, options); if ((n1 is XComment) && (n2 is XComment)) return DeepEquals((XComment)n1, (XComment)n2, options); if ((n1 is XText) && (n2 is XText)) return DeepEquals((XText)n1, (XText)n2, options); if ((n1 is XProcessingInstruction) && (n2 is XProcessingInstruction)) return DeepEquals((XProcessingInstruction)n1, (XProcessingInstruction)n2, options); if (!(n1.GetType().IsAssignableFrom(n2.GetType()) || n2.GetType().IsAssignableFrom(n1.GetType()))) return false; throw new NotImplementedException(); }
/// <summary> /// Compares two attributes using the indicated comparison options. /// </summary> /// <param name="a1">The first attribute to compare.</param> /// <param name="a2">The second attribute to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the attributes are equal, false otherwise.</returns> public static bool DeepEquals(this XAttribute a1, XAttribute a2, XNodeComparisonOptions options) { if ((a1 ?? a2) == null) return true; if ((a1 == null) || (a2 == null)) return false; // They are not both null, so if either is, then the other isn't // Compare the name if (!CompareNames(a1.Name, a2.Name, options)) return false; return a1.Value == a2.Value; }
/// <summary> /// Compares two documents using the indicated comparison options. /// </summary> /// <param name="d1">The first document to compare.</param> /// <param name="d2">The second document to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the documents are equal, false otherwise.</returns> public static bool DeepEquals(this XDocument d1, XDocument d2, XNodeComparisonOptions options) { if ((d1 ?? d2) == null) return true; if ((d1 == null) || (d2 == null)) return false; // They are not both null, so if either is, then the other isn't return DeepEquals(d1.Root, d2.Root, options); }
/// <summary> /// Compares two elements using the indicated comparison options. /// </summary> /// <param name="e1">The first element to compare.</param> /// <param name="e2">The second element to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the elements are equal, false otherwise.</returns> public static bool DeepEquals(this XElement e1, XElement e2, XNodeComparisonOptions options) { if ((e1 ?? e2) == null) return true; if ((e1 == null) || (e2 == null)) return false; // They are not both null, so if either is, then the other isn't // Compare the name if (!CompareNames(e1.Name, e2.Name, options)) return false; // Performance tweak: compare attributes first, because they will often be faster to find a difference return AttributesEqual(e1, e2, options) && ContentsEqual(e1, e2, options); }
/// <summary> /// Compares two texts using the indicated comparison options. /// </summary> /// <param name="t1">The first text to compare.</param> /// <param name="t2">The second text to compare.</param> /// <param name="options">The options to use in the comparison.</param> /// <returns>true if the texts are equal, false otherwise.</returns> public static bool DeepEquals(this XText t1, XText t2, XNodeComparisonOptions options) { if ((t1 ?? t2) == null) return true; if ((t1 == null) || (t2 == null)) return false; // They are not both null, so if either is, then the other isn't return ((t1.NodeType == t2.NodeType) && (t1.Value == t1.Value)); }