ComparisonUnitAtom.cs 7.1 KB

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