ComparisonUnitAtom.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // Copyright (c) Microsoft. All rights reserved.
  2. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Xml.Linq;
  7. using DocumentFormat.OpenXml.Packaging;
  8. namespace OpenXmlPowerTools
  9. {
  10. public class ComparisonUnitAtom : ComparisonUnit
  11. {
  12. public ComparisonUnitAtom(
  13. XElement contentElement,
  14. XElement[] ancestorElements,
  15. OpenXmlPart part,
  16. WmlComparerSettings settings)
  17. {
  18. ContentElement = contentElement;
  19. AncestorElements = ancestorElements;
  20. Part = part;
  21. RevTrackElement = GetRevisionTrackingElementFromAncestors(contentElement, AncestorElements);
  22. if (RevTrackElement == null)
  23. {
  24. CorrelationStatus = CorrelationStatus.Equal;
  25. }
  26. else
  27. {
  28. if (RevTrackElement.Name == W.del)
  29. {
  30. CorrelationStatus = CorrelationStatus.Deleted;
  31. }
  32. else if (RevTrackElement.Name == W.ins)
  33. {
  34. CorrelationStatus = CorrelationStatus.Inserted;
  35. }
  36. }
  37. var sha1Hash = (string) contentElement.Attribute(PtOpenXml.SHA1Hash);
  38. if (sha1Hash != null)
  39. {
  40. SHA1Hash = sha1Hash;
  41. }
  42. else
  43. {
  44. string shaHashString = GetSha1HashStringForElement(ContentElement, settings);
  45. SHA1Hash = WmlComparerUtil.SHA1HashStringForUTF8String(shaHashString);
  46. }
  47. }
  48. // AncestorElements are kept in order from the body to the leaf, because this is the order in which we need to access in order
  49. // to reassemble the document. However, in many places in the code, it is necessary to find the nearest ancestor, i.e. cell
  50. // so it is necessary to reverse the order when looking for it, i.e. look from the leaf back to the body element.
  51. public XElement[] AncestorElements { get; }
  52. public XElement ContentElement { get; }
  53. public XElement RevTrackElement { get; }
  54. public string[] AncestorUnids { get; set; }
  55. public ComparisonUnitAtom ComparisonUnitAtomBefore { get; set; }
  56. public XElement ContentElementBefore { get; set; }
  57. public OpenXmlPart Part { get; }
  58. private static string GetSha1HashStringForElement(XElement contentElement, WmlComparerSettings settings)
  59. {
  60. string text = contentElement.Value;
  61. if (settings.CaseInsensitive)
  62. {
  63. text = text.ToUpper(settings.CultureInfo);
  64. }
  65. return contentElement.Name.LocalName + text;
  66. }
  67. private static XElement GetRevisionTrackingElementFromAncestors(
  68. XElement contentElement,
  69. IEnumerable<XElement> ancestors)
  70. {
  71. return contentElement.Name == W.pPr
  72. ? contentElement.Elements(W.rPr).Elements().FirstOrDefault(e => e.Name == W.del || e.Name == W.ins)
  73. : ancestors.FirstOrDefault(a => a.Name == W.del || a.Name == W.ins);
  74. }
  75. public override string ToString()
  76. {
  77. return ToString(0);
  78. }
  79. public override string ToString(int indent)
  80. {
  81. const int xNamePad = 16;
  82. string indentString = "".PadRight(indent);
  83. var sb = new StringBuilder();
  84. sb.Append(indentString);
  85. var correlationStatus = "";
  86. if (CorrelationStatus != CorrelationStatus.Nil)
  87. {
  88. correlationStatus = $"[{CorrelationStatus.ToString().PadRight(8)}] ";
  89. }
  90. if (ContentElement.Name == W.t || ContentElement.Name == W.delText)
  91. {
  92. sb.AppendFormat(
  93. "Atom {0}: {1} {2} SHA1:{3} ",
  94. PadLocalName(xNamePad, this),
  95. ContentElement.Value,
  96. correlationStatus,
  97. SHA1Hash.Substring(0, 8));
  98. AppendAncestorsDump(sb, this);
  99. }
  100. else
  101. {
  102. sb.AppendFormat(
  103. "Atom {0}: {1} SHA1:{2} ",
  104. PadLocalName(xNamePad, this),
  105. correlationStatus,
  106. SHA1Hash.Substring(0, 8));
  107. AppendAncestorsDump(sb, this);
  108. }
  109. return sb.ToString();
  110. }
  111. public string ToStringAncestorUnids()
  112. {
  113. return ToStringAncestorUnids(0);
  114. }
  115. private string ToStringAncestorUnids(int indent)
  116. {
  117. const int xNamePad = 16;
  118. string indentString = "".PadRight(indent);
  119. var sb = new StringBuilder();
  120. sb.Append(indentString);
  121. var correlationStatus = "";
  122. if (CorrelationStatus != CorrelationStatus.Nil)
  123. {
  124. correlationStatus = $"[{CorrelationStatus.ToString().PadRight(8)}] ";
  125. }
  126. if (ContentElement.Name == W.t || ContentElement.Name == W.delText)
  127. {
  128. sb.AppendFormat(
  129. "Atom {0}: {1} {2} SHA1:{3} ",
  130. PadLocalName(xNamePad, this),
  131. ContentElement.Value,
  132. correlationStatus,
  133. SHA1Hash.Substring(0, 8));
  134. AppendAncestorsUnidsDump(sb, this);
  135. }
  136. else
  137. {
  138. sb.AppendFormat(
  139. "Atom {0}: {1} SHA1:{2} ",
  140. PadLocalName(xNamePad, this),
  141. correlationStatus,
  142. SHA1Hash.Substring(0, 8));
  143. AppendAncestorsUnidsDump(sb, this);
  144. }
  145. return sb.ToString();
  146. }
  147. private static string PadLocalName(int xNamePad, ComparisonUnitAtom item)
  148. {
  149. return (item.ContentElement.Name.LocalName + " ").PadRight(xNamePad, '-') + " ";
  150. }
  151. private static void AppendAncestorsDump(StringBuilder sb, ComparisonUnitAtom sr)
  152. {
  153. string s = sr
  154. .AncestorElements.Select(p => p.Name.LocalName + GetUnid(p) + "/")
  155. .StringConcatenate()
  156. .TrimEnd('/');
  157. sb.Append("Ancestors:" + s);
  158. }
  159. private static void AppendAncestorsUnidsDump(StringBuilder sb, ComparisonUnitAtom sr)
  160. {
  161. var zipped = sr.AncestorElements.Zip(sr.AncestorUnids, (a, u) => new
  162. {
  163. AncestorElement = a,
  164. AncestorUnid = u
  165. });
  166. string s = zipped
  167. .Select(p => p.AncestorElement.Name.LocalName + "[" + p.AncestorUnid.Substring(0, 8) + "]/")
  168. .StringConcatenate().TrimEnd('/');
  169. sb.Append("Ancestors:" + s);
  170. }
  171. private static string GetUnid(XElement p)
  172. {
  173. var unid = (string) p.Attribute(PtOpenXml.Unid);
  174. return unid == null ? "" : "[" + unid.Substring(0, 8) + "]";
  175. }
  176. }
  177. }