WmlComparer.Public.Methods.GetRevisions.cs 9.6 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;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Xml.Linq;
  9. using DocumentFormat.OpenXml.Packaging;
  10. using OpenXmlPowerTools.Previous;
  11. namespace OpenXmlPowerTools
  12. {
  13. public static partial class WmlComparer
  14. {
  15. // the following gets a flattened list of ComparisonUnitAtoms, with status indicated in each ComparisonUnitAtom: Deleted, Inserted, or Equal
  16. // for any deleted or inserted rows, we go into the w:trPr properties, and add the appropriate w:ins or w:del element, and therefore
  17. // when generating the document, the appropriate row will be marked as deleted or inserted.
  18. public static List<WmlComparerRevision> GetRevisions(WmlDocument source, WmlComparerSettings settings)
  19. {
  20. using (var ms = new MemoryStream())
  21. {
  22. ms.Write(source.DocumentByteArray, 0, source.DocumentByteArray.Length);
  23. using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true))
  24. {
  25. TestForInvalidContent(wDoc);
  26. RemoveExistingPowerToolsMarkup(wDoc);
  27. XElement contentParent = wDoc.MainDocumentPart.GetXDocument().Root?.Element(W.body);
  28. ComparisonUnitAtom[] atomList =
  29. CreateComparisonUnitAtomList(wDoc.MainDocumentPart, contentParent, settings).ToArray();
  30. if (False)
  31. {
  32. var sb = new StringBuilder();
  33. foreach (ComparisonUnitAtom item in atomList)
  34. sb.Append(item + Environment.NewLine);
  35. string sbs = sb.ToString();
  36. TestUtil.NotePad(sbs);
  37. }
  38. List<IGrouping<string, ComparisonUnitAtom>> grouped = atomList
  39. .GroupAdjacent(a =>
  40. {
  41. string key = a.CorrelationStatus.ToString();
  42. if (a.CorrelationStatus != CorrelationStatus.Equal)
  43. {
  44. var rt = new XElement(a.RevTrackElement.Name,
  45. new XAttribute(XNamespace.Xmlns + "w",
  46. "http://schemas.openxmlformats.org/wordprocessingml/2006/main"),
  47. a.RevTrackElement.Attributes().Where(a2 => a2.Name != W.id && a2.Name != PtOpenXml.Unid));
  48. key += rt.ToString(SaveOptions.DisableFormatting);
  49. }
  50. return key;
  51. })
  52. .ToList();
  53. List<IGrouping<string, ComparisonUnitAtom>> revisions = grouped
  54. .Where(k => k.Key != "Equal")
  55. .ToList();
  56. if (False)
  57. {
  58. var sb = new StringBuilder();
  59. foreach (IGrouping<string, ComparisonUnitAtom> item in revisions)
  60. {
  61. sb.Append(item.Key + Environment.NewLine);
  62. }
  63. string sbs = sb.ToString();
  64. TestUtil.NotePad(sbs);
  65. }
  66. List<WmlComparerRevision> mainDocPartRevisionList = revisions
  67. .Select(rg =>
  68. {
  69. var rev = new WmlComparerRevision();
  70. if (rg.Key.StartsWith("Inserted"))
  71. {
  72. rev.RevisionType = WmlComparerRevisionType.Inserted;
  73. }
  74. else if (rg.Key.StartsWith("Deleted"))
  75. {
  76. rev.RevisionType = WmlComparerRevisionType.Deleted;
  77. }
  78. XElement revTrackElement = rg.First().RevTrackElement;
  79. rev.RevisionXElement = revTrackElement;
  80. rev.Author = (string) revTrackElement.Attribute(W.author);
  81. rev.ContentXElement = rg.First().ContentElement;
  82. rev.Date = (string) revTrackElement.Attribute(W.date);
  83. rev.PartUri = wDoc.MainDocumentPart.Uri;
  84. rev.PartContentType = wDoc.MainDocumentPart.ContentType;
  85. if (!RevElementsWithNoText.Contains(rev.ContentXElement.Name))
  86. {
  87. rev.Text = rg
  88. .Select(rgc => rgc.ContentElement.Name == W.pPr ? NewLine : rgc.ContentElement.Value)
  89. .StringConcatenate();
  90. }
  91. return rev;
  92. })
  93. .ToList();
  94. IEnumerable<WmlComparerRevision> footnotesRevisionList =
  95. GetFootnoteEndnoteRevisionList(wDoc.MainDocumentPart.FootnotesPart, W.footnote, settings);
  96. IEnumerable<WmlComparerRevision> endnotesRevisionList =
  97. GetFootnoteEndnoteRevisionList(wDoc.MainDocumentPart.EndnotesPart, W.endnote, settings);
  98. List<WmlComparerRevision> finalRevisionList = mainDocPartRevisionList
  99. .Concat(footnotesRevisionList)
  100. .Concat(endnotesRevisionList)
  101. .ToList();
  102. return finalRevisionList;
  103. }
  104. }
  105. }
  106. private static IEnumerable<WmlComparerRevision> GetFootnoteEndnoteRevisionList(
  107. OpenXmlPart footnotesEndnotesPart,
  108. XName footnoteEndnoteElementName,
  109. WmlComparerSettings settings)
  110. {
  111. if (footnotesEndnotesPart == null)
  112. {
  113. return Enumerable.Empty<WmlComparerRevision>();
  114. }
  115. XDocument xDoc = footnotesEndnotesPart.GetXDocument();
  116. IEnumerable<XElement> footnotesEndnotes =
  117. xDoc.Root?.Elements(footnoteEndnoteElementName) ?? throw new OpenXmlPowerToolsException("Invalid document.");
  118. var revisionsForPart = new List<WmlComparerRevision>();
  119. foreach (XElement fn in footnotesEndnotes)
  120. {
  121. ComparisonUnitAtom[] atomList = CreateComparisonUnitAtomList(footnotesEndnotesPart, fn, settings).ToArray();
  122. if (False)
  123. {
  124. var sb = new StringBuilder();
  125. foreach (ComparisonUnitAtom item in atomList)
  126. {
  127. sb.Append(item + Environment.NewLine);
  128. }
  129. string sbs = sb.ToString();
  130. TestUtil.NotePad(sbs);
  131. }
  132. List<IGrouping<string, ComparisonUnitAtom>> grouped = atomList
  133. .GroupAdjacent(a =>
  134. {
  135. string key = a.CorrelationStatus.ToString();
  136. if (a.CorrelationStatus != CorrelationStatus.Equal)
  137. {
  138. var rt = new XElement(a.RevTrackElement.Name,
  139. new XAttribute(XNamespace.Xmlns + "w",
  140. "http://schemas.openxmlformats.org/wordprocessingml/2006/main"),
  141. a.RevTrackElement.Attributes().Where(a2 => a2.Name != W.id && a2.Name != PtOpenXml.Unid));
  142. key += rt.ToString(SaveOptions.DisableFormatting);
  143. }
  144. return key;
  145. })
  146. .ToList();
  147. List<IGrouping<string, ComparisonUnitAtom>> revisions = grouped
  148. .Where(k => k.Key != "Equal")
  149. .ToList();
  150. IEnumerable<WmlComparerRevision> thisNoteRevisionList = revisions
  151. .Select(rg =>
  152. {
  153. var rev = new WmlComparerRevision();
  154. if (rg.Key.StartsWith("Inserted"))
  155. {
  156. rev.RevisionType = WmlComparerRevisionType.Inserted;
  157. }
  158. else if (rg.Key.StartsWith("Deleted"))
  159. {
  160. rev.RevisionType = WmlComparerRevisionType.Deleted;
  161. }
  162. XElement revTrackElement = rg.First().RevTrackElement;
  163. rev.RevisionXElement = revTrackElement;
  164. rev.Author = (string) revTrackElement.Attribute(W.author);
  165. rev.ContentXElement = rg.First().ContentElement;
  166. rev.Date = (string) revTrackElement.Attribute(W.date);
  167. rev.PartUri = footnotesEndnotesPart.Uri;
  168. rev.PartContentType = footnotesEndnotesPart.ContentType;
  169. if (!RevElementsWithNoText.Contains(rev.ContentXElement.Name))
  170. {
  171. rev.Text = rg
  172. .Select(rgc => rgc.ContentElement.Name == W.pPr ? NewLine : rgc.ContentElement.Value)
  173. .StringConcatenate();
  174. }
  175. return rev;
  176. });
  177. revisionsForPart.AddRange(thisNoteRevisionList);
  178. }
  179. return revisionsForPart;
  180. }
  181. }
  182. }