CommentUtil.java

1
package pro.verron.officestamper.core;
2
3
import org.docx4j.openpackaging.exceptions.Docx4JException;
4
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
5
import org.docx4j.openpackaging.packages.OpcPackage;
6
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
7
import org.docx4j.openpackaging.parts.PartName;
8
import org.docx4j.openpackaging.parts.Parts;
9
import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart;
10
import org.docx4j.wml.*;
11
import org.docx4j.wml.R.CommentReference;
12
import pro.verron.officestamper.api.Comment;
13
import pro.verron.officestamper.api.DocxPart;
14
import pro.verron.officestamper.api.OfficeStamperException;
15
import pro.verron.officestamper.utils.wml.DocxIterator;
16
17
import java.math.BigInteger;
18
import java.util.*;
19
20
import static java.util.function.Function.identity;
21
import static java.util.stream.Collectors.toMap;
22
23
/// Utility class for working with comments in a DOCX document.
24
///
25
/// @author Joseph Verron
26
/// @author Tom Hombergs
27
/// @version ${version}
28
/// @since 1.0.0
29
public class CommentUtil {
30
    private static final PartName WORD_COMMENTS_PART_NAME;
31
32
    static {
33
        try {
34
            WORD_COMMENTS_PART_NAME = new PartName("/word/comments.xml");
35
        } catch (InvalidFormatException e) {
36
            throw new OfficeStamperException(e);
37
        }
38
    }
39
40
    private CommentUtil() {
41
        throw new OfficeStamperException("Utility class shouldn't be instantiated");
42
    }
43
44
    /// Retrieves the comment associated with a given paragraph content within a WordprocessingMLPackage document.
45
    ///
46
    /// @param contentAccessor the content accessor to search for comments.
47
    /// @param document        the WordprocessingMLPackage document containing the paragraph and its comments.
48
    /// @return a collection of found comments.
49
    public static Collection<Comments.Comment> getCommentFor(ContentAccessor contentAccessor, OpcPackage document) {
50
        var comments = getCommentsPart(document.getParts()).map(CommentUtil::extractContent)
51
                                                           .map(Comments::getComment)
52
                                                           .stream()
53
                                                           .flatMap(Collection::stream)
54
                                                           .toList();
55
56
        var result = new ArrayList<Comments.Comment>();
57
        var commentIterator = new DocxIterator(contentAccessor).selectClass(CommentRangeStart.class);
58 1 1. getCommentFor : negated conditional → NO_COVERAGE
        while (commentIterator.hasNext()) {
59
            var crs = commentIterator.next();
60 1 1. getCommentFor : removed call to java/util/Optional::ifPresent → NO_COVERAGE
            findCommentById(comments, crs.getId()).ifPresent(result::add);
61
        }
62 1 1. getCommentFor : replaced return value with Collections.emptyList for pro/verron/officestamper/core/CommentUtil::getCommentFor → NO_COVERAGE
        return result;
63
    }
64
65
    /// Retrieves the CommentsPart from the given Parts object.
66
    ///
67
    /// @param parts the Parts object containing the various parts of the document.
68
    ///
69
    /// @return an Optional containing the CommentsPart if found, or an empty Optional if not found.
70
    public static Optional<CommentsPart> getCommentsPart(Parts parts) {
71 1 1. getCommentsPart : replaced return value with Optional.empty for pro/verron/officestamper/core/CommentUtil::getCommentsPart → KILLED
        return Optional.ofNullable((CommentsPart) parts.get(WORD_COMMENTS_PART_NAME));
72
    }
73
74
    /// Extracts the contents of a given [CommentsPart].
75
    ///
76
    /// @param commentsPart the [CommentsPart] from which content will be extracted
77
    ///
78
    /// @return the [Comments] instance containing the content of the provided comments part
79
    ///
80
    /// @throws OfficeStamperException if an error occurs while retrieving the content
81
    public static Comments extractContent(CommentsPart commentsPart) {
82
        try {
83 1 1. extractContent : replaced return value with null for pro/verron/officestamper/core/CommentUtil::extractContent → KILLED
            return commentsPart.getContents();
84
        } catch (Docx4JException e) {
85
            throw new OfficeStamperException("Error while searching comment.", e);
86
        }
87
    }
88
89
    private static Optional<Comments.Comment> findCommentById(List<Comments.Comment> comments, BigInteger id) {
90
        for (Comments.Comment comment : comments) {
91 1 1. findCommentById : negated conditional → NO_COVERAGE
            if (id.equals(comment.getId())) {
92 1 1. findCommentById : replaced return value with Optional.empty for pro/verron/officestamper/core/CommentUtil::findCommentById → NO_COVERAGE
                return Optional.of(comment);
93
            }
94
        }
95
        return Optional.empty();
96
    }
97
98
    /// Returns the string value of the specified comment object.
99
    ///
100
    /// @param comment a [Comment] object
101
    public static void deleteComment(Comment comment) {
102
        CommentRangeEnd end = comment.getCommentRangeEnd();
103
        ContentAccessor endParent = (ContentAccessor) end.getParent();
104
        endParent.getContent()
105
                 .remove(end);
106
        CommentRangeStart start = comment.getCommentRangeStart();
107
        var parent = start.getParent();
108
        ContentAccessor startParent = (ContentAccessor) parent;
109
        startParent.getContent()
110
                   .remove(start);
111 1 1. deleteComment : negated conditional → KILLED
        if (startParent instanceof CTSmartTagRun tag && tag.getContent()
112 1 1. deleteComment : negated conditional → KILLED
                                                           .isEmpty()) ((ContentAccessor) tag.getParent()).getContent()
113
                                                                                                          .remove(tag);
114
        CommentReference reference = comment.getCommentReference();
115 1 1. deleteComment : negated conditional → KILLED
        if (reference != null) {
116
            ContentAccessor referenceParent = (ContentAccessor) reference.getParent();
117
            referenceParent.getContent()
118
                           .remove(reference);
119
        }
120
    }
121
122
    /// Creates a [Comment] object.
123
    ///
124
    /// @param docxPart the document part.
125
    /// @param crs the comment range start.
126
    /// @param document the document.
127
    /// @param contentAccessor the content accessor.
128
    ///
129
    /// @return the comment.
130
    public static Comment comment(
131
            DocxPart docxPart,
132
            CommentRangeStart crs,
133
            WordprocessingMLPackage document,
134
            ContentAccessor contentAccessor
135
    ) {
136
        var iterator = new DocxIterator(contentAccessor).slice(crs, null);
137
        CommentRangeEnd cre = null;
138
        CommentReference cr = null;
139
        var commentId = crs.getId();
140 3 1. comment : negated conditional → TIMED_OUT
2. comment : negated conditional → KILLED
3. comment : negated conditional → KILLED
        while (iterator.hasNext() && (cr == null || cre == null)) {
141
            var element = iterator.next();
142 3 1. comment : negated conditional → KILLED
2. comment : negated conditional → KILLED
3. comment : negated conditional → KILLED
            if (element instanceof CommentRangeEnd found && cre == null && Objects.equals(found.getId(), commentId)) {
143
                cre = found;
144
            }
145 3 1. comment : negated conditional → KILLED
2. comment : negated conditional → KILLED
3. comment : negated conditional → KILLED
            else if (element instanceof CommentReference found && cr == null && Objects.equals(found.getId(),
146
                    commentId)) {
147
                cr = found;
148
            }
149
        }
150
151 1 1. comment : negated conditional → KILLED
        if (cre == null) throw new IllegalStateException("Could not find comment range end or reference");
152
153
        var comment = comment(document, commentId);
154
155
156 1 1. comment : replaced return value with null for pro/verron/officestamper/core/CommentUtil::comment → KILLED
        return new StandardComment(docxPart, (CTSmartTagRun) crs.getParent(), crs, cre, comment, cr);
157
    }
158
159
    private static Comments.Comment comment(WordprocessingMLPackage document, BigInteger commentId) {
160 1 1. comment : replaced return value with null for pro/verron/officestamper/core/CommentUtil::comment → KILLED
        return getCommentsPart(document.getParts()).map(CommentUtil::extractContent)
161
                                                   .map(Comments::getComment)
162
                                                   .stream()
163
                                                   .flatMap(Collection::stream)
164
                                                   .collect(toMap(Comments.Comment::getId, identity()))
165
                                                   .get(commentId);
166
    }
167
}

Mutations

58

1.1
Location : getCommentFor
Killed by : none
negated conditional → NO_COVERAGE

60

1.1
Location : getCommentFor
Killed by : none
removed call to java/util/Optional::ifPresent → NO_COVERAGE

62

1.1
Location : getCommentFor
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/core/CommentUtil::getCommentFor → NO_COVERAGE

71

1.1
Location : getCommentsPart
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
replaced return value with Optional.empty for pro/verron/officestamper/core/CommentUtil::getCommentsPart → KILLED

83

1.1
Location : extractContent
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
replaced return value with null for pro/verron/officestamper/core/CommentUtil::extractContent → KILLED

91

1.1
Location : findCommentById
Killed by : none
negated conditional → NO_COVERAGE

92

1.1
Location : findCommentById
Killed by : none
replaced return value with Optional.empty for pro/verron/officestamper/core/CommentUtil::findCommentById → NO_COVERAGE

111

1.1
Location : deleteComment
Killed by : pro.verron.officestamper.test.ProcessorReplaceWithTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorReplaceWithTest]/[method:notWorking1()]
negated conditional → KILLED

112

1.1
Location : deleteComment
Killed by : pro.verron.officestamper.test.ProcessorReplaceWithTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorReplaceWithTest]/[method:notWorking1()]
negated conditional → KILLED

115

1.1
Location : deleteComment
Killed by : pro.verron.officestamper.test.RegressionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.RegressionTests]/[test-template:test52(pro.verron.officestamper.test.RegressionTests$Conditions, java.lang.String)]/[test-template-invocation:#2]
negated conditional → KILLED

140

1.1
Location : comment
Killed by : none
negated conditional → TIMED_OUT

2.2
Location : comment
Killed by : pro.verron.officestamper.test.RegressionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.RegressionTests]/[test-template:test52(pro.verron.officestamper.test.RegressionTests$Conditions, java.lang.String)]/[test-template-invocation:#2]
negated conditional → KILLED

3.3
Location : comment
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
negated conditional → KILLED

142

1.1
Location : comment
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
negated conditional → KILLED

2.2
Location : comment
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
negated conditional → KILLED

3.3
Location : comment
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
negated conditional → KILLED

145

1.1
Location : comment
Killed by : pro.verron.officestamper.test.RegressionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.RegressionTests]/[test-template:test52(pro.verron.officestamper.test.RegressionTests$Conditions, java.lang.String)]/[test-template-invocation:#2]
negated conditional → KILLED

2.2
Location : comment
Killed by : pro.verron.officestamper.test.RegressionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.RegressionTests]/[test-template:test52(pro.verron.officestamper.test.RegressionTests$Conditions, java.lang.String)]/[test-template-invocation:#2]
negated conditional → KILLED

3.3
Location : comment
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
negated conditional → KILLED

151

1.1
Location : comment
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
negated conditional → KILLED

156

1.1
Location : comment
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
replaced return value with null for pro/verron/officestamper/core/CommentUtil::comment → KILLED

160

1.1
Location : comment
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
replaced return value with null for pro/verron/officestamper/core/CommentUtil::comment → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.0