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

Mutations

57

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

59

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

61

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

69

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:#2]
replaced return value with Optional.empty for pro/verron/officestamper/core/CommentUtil::getCommentsPart → KILLED

79

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:#2]
replaced return value with null for pro/verron/officestamper/core/CommentUtil::extractContent → KILLED

87

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

88

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

107

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

108

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

111

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

135

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

2.2
Location : comment
Killed by : none
negated conditional → SURVIVED
Covering tests

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:#2]
negated conditional → KILLED

137

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:#2]
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:#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:#2]
negated conditional → KILLED

140

1.1
Location : comment
Killed by : none
negated conditional → SURVIVED
Covering tests

2.2
Location : comment
Killed by : none
negated conditional → SURVIVED Covering tests

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:#2]
negated conditional → KILLED

146

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:#2]
negated conditional → KILLED

152

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:#2]
replaced return value with null for pro/verron/officestamper/core/CommentUtil::comment → 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:#2]
replaced return value with null for pro/verron/officestamper/core/CommentUtil::comment → KILLED

Active mutators

Tests examined


Report generated by PIT 1.25.5 support