Word 2007 has a feature where you can lock a document, preventing any changes to content, yet allowing the user to add comments. CommentMerger is a small method (~400 lines of code) that merges comments from two documents into a single document, provided the two contain the same content. This code is part of the PowerTools for Open XML project. In the future, I’m going to write a cmdlet that will use this code to merge comments.
I’ve updated the CommentMerger class (actually completely rewritten it). The new code (again, written in a functional style) is much more robust. It now merges comments on math formulas. The code (and example program to use it) is available in the ‘downloads’ tab at codeplex.com/powertools.
There have been two good side effects of this effort – I’ve learned more about the Open XML formats, and I’ve refined my approach for doing recursive pure functional transforms.
One of the biggest differences in the code is the approach that I took to selecting elements for transform. Previously, I was looking for paragraph nodes, then recursively processing the descendants. But this became more complicated in certain scenarios – paragraphs can contain runs which contain other content which can contain paragraphs. In the new version, I simply select the elements for transform based on their contents. For example, the following code selects a node for transform based on if the node contains any child w:commentRangeStart, w:commentRangeEnd, or runs (w:r) that contain w:commentReference elements:
// If fromElement contains comments, then do special clone that merges comments.
.Where(e => e.Name == W.commentRangeStart ||
e.Name == W.commentRangeEnd ||
((e.Name == W.r || e.Name == M.r) &&
// Get new, cloned element with merged comments.
XElement newToElement = CloneElementWithComments(toElement, fromElement,
I used this approach throughout the transform. I had a question about whether this approach would perform well enough. I believe it does for two reasons – first, the Any extension method ‘short-circuits’ any positive matches, reducing the work to be done. Second, I think that CPU caching may be effective in these situations. In any case, the code seems to be more than fast enough.