WmlUtils.java

1
package pro.verron.officestamper.utils;
2
3
import jakarta.xml.bind.JAXBElement;
4
import org.docx4j.TraversalUtil;
5
import org.docx4j.XmlUtils;
6
import org.docx4j.finders.CommentFinder;
7
import org.docx4j.openpackaging.exceptions.Docx4JException;
8
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
9
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
10
import org.docx4j.openpackaging.parts.PartName;
11
import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart;
12
import org.docx4j.wml.*;
13
import org.jvnet.jaxb2_commons.ppp.Child;
14
import pro.verron.officestamper.api.OfficeStamperException;
15
import pro.verron.officestamper.core.TableCellUtil;
16
17
import java.math.BigInteger;
18
import java.util.List;
19
import java.util.Objects;
20
import java.util.Optional;
21
import java.util.function.Predicate;
22
23
/**
24
 * Utility class with methods to help in the interaction with WordprocessingMLPackage documents
25
 * and their elements, such as comments, parents, and child elements.
26
 */
27
public final class WmlUtils {
28
    private WmlUtils() {
29
        throw new OfficeStamperException("Utility class shouldn't be instantiated");
30
    }
31
32
    /**
33
     * Attempts to find the first parent of a given child element that is an instance of the specified class within
34
     * the defined search depth.
35
     *
36
     * @param child the child element from which the search for a parent begins.
37
     * @param clazz the class type to match for the parent
38
     * @param depth the maximum amount levels to traverse up the parent hierarchy
39
     * @param <T>   the type of the parent class to search for
40
     *
41
     * @return an Optional containing the first parent matching the specified class, or an empty Optional if no match
42
     * found.
43
     */
44
    public static <T> Optional<T> getFirstParentWithClass(Child child, Class<T> clazz, int depth) {
45
        var parent = child.getParent();
46
        var currentDepth = 0;
47 2 1. getFirstParentWithClass : changed conditional boundary → SURVIVED
2. getFirstParentWithClass : negated conditional → KILLED
        while (currentDepth <= depth) {
48 1 1. getFirstParentWithClass : Changed increment from 1 to -1 → SURVIVED
            currentDepth++;
49 1 1. getFirstParentWithClass : negated conditional → KILLED
            if (parent == null) return Optional.empty();
50 2 1. getFirstParentWithClass : negated conditional → KILLED
2. getFirstParentWithClass : replaced return value with Optional.empty for pro/verron/officestamper/utils/WmlUtils::getFirstParentWithClass → KILLED
            if (clazz.isInstance(parent)) return Optional.of(clazz.cast(parent));
51 1 1. getFirstParentWithClass : negated conditional → TIMED_OUT
            if (parent instanceof Child next) parent = next.getParent();
52
        }
53
        return Optional.empty();
54
    }
55
56
    /**
57
     * Extracts a list of comment elements from the specified WordprocessingMLPackage document.
58
     *
59
     * @param document the WordprocessingMLPackage document from which to extract comment elements
60
     *
61
     * @return a list of Child objects representing the extracted comment elements
62
     */
63
    public static List<Child> extractCommentElements(WordprocessingMLPackage document) {
64
        var commentFinder = new CommentFinder();
65 1 1. extractCommentElements : removed call to org/docx4j/TraversalUtil::visit → KILLED
        TraversalUtil.visit(document, true, commentFinder);
66 1 1. extractCommentElements : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/WmlUtils::extractCommentElements → KILLED
        return commentFinder.getCommentElements();
67
    }
68
69
    /// Finds a comment with the given ID in the specified WordprocessingMLPackage document.
70
    ///
71
    /// @param document the WordprocessingMLPackage document to search for the comment
72
    /// @param id       the ID of the comment to find
73
    ///
74
    /// @return an Optional containing the Comment if found, or an empty Optional if not found.
75
    public static Optional<Comments.Comment> findComment(WordprocessingMLPackage document, BigInteger id) {
76
        var name = getPartName("/word/comments.xml");
77
        var parts = document.getParts();
78
        var wordComments = (CommentsPart) parts.get(name);
79
        var comments = getComments(wordComments);
80 1 1. findComment : replaced return value with Optional.empty for pro/verron/officestamper/utils/WmlUtils::findComment → KILLED
        return comments.getComment()
81
                       .stream()
82
                       .filter(idEqual(id))
83
                       .findFirst();
84
    }
85
86
    private static PartName getPartName(String partName) {
87
        try {
88 1 1. getPartName : replaced return value with null for pro/verron/officestamper/utils/WmlUtils::getPartName → KILLED
            return new PartName(partName);
89
        } catch (InvalidFormatException e) {
90
            throw new OfficeStamperException(e);
91
        }
92
    }
93
94
    private static Comments getComments(CommentsPart wordComments) {
95
        try {
96 1 1. getComments : replaced return value with null for pro/verron/officestamper/utils/WmlUtils::getComments → KILLED
            return wordComments.getContents();
97
        } catch (Docx4JException e) {
98
            throw new OfficeStamperException(e);
99
        }
100
    }
101
102
    private static Predicate<Comments.Comment> idEqual(BigInteger id) {
103 1 1. idEqual : replaced return value with null for pro/verron/officestamper/utils/WmlUtils::idEqual → KILLED
        return comment -> {
104
            var commentId = comment.getId();
105 2 1. lambda$idEqual$0 : replaced boolean return with false for pro/verron/officestamper/utils/WmlUtils::lambda$idEqual$0 → KILLED
2. lambda$idEqual$0 : replaced boolean return with true for pro/verron/officestamper/utils/WmlUtils::lambda$idEqual$0 → KILLED
            return commentId.equals(id);
106
        };
107
    }
108
109
110
    /**
111
     * Removes the specified child element from its parent container.
112
     * Depending on the type of the parent element, the removal process
113
     * is delegated to the appropriate helper method. If the child is
114
     * contained within a table cell and the cell is empty after removal,
115
     * an empty paragraph is added to the cell.
116
     *
117
     * @param child the child element to be removed
118
     *
119
     * @throws OfficeStamperException if the parent of the child element is of an unexpected type
120
     */
121
    public static void remove(Child child) {
122
        switch (child.getParent()) {
123 1 1. remove : removed call to pro/verron/officestamper/utils/WmlUtils::remove → KILLED
            case ContentAccessor parent -> remove(parent, child);
124 1 1. remove : removed call to pro/verron/officestamper/utils/WmlUtils::remove → KILLED
            case CTFootnotes parent -> remove(parent, child);
125 1 1. remove : removed call to pro/verron/officestamper/utils/WmlUtils::remove → KILLED
            case CTEndnotes parent -> remove(parent, child);
126 1 1. remove : removed call to pro/verron/officestamper/utils/WmlUtils::remove → NO_COVERAGE
            case SdtRun parent -> remove(parent, child);
127
            default -> throw new OfficeStamperException("Unexpected value: " + child.getParent());
128
        }
129 2 1. remove : negated conditional → SURVIVED
2. remove : negated conditional → KILLED
        if (child.getParent() instanceof Tc cell && TableCellUtil.hasNoParagraphOrTable(cell)) {
130 1 1. remove : removed call to pro/verron/officestamper/core/TableCellUtil::addEmptyParagraph → SURVIVED
            TableCellUtil.addEmptyParagraph(cell);
131
        }
132
    }
133
134
    private static void remove(ContentAccessor parent, Child child) {
135
        var siblings = parent.getContent();
136
        var iterator = siblings.listIterator();
137 1 1. remove : negated conditional → KILLED
        while (iterator.hasNext()) {
138 1 1. remove : negated conditional → KILLED
            if (equals(iterator.next(), child)) {
139 1 1. remove : removed call to java/util/ListIterator::remove → KILLED
                iterator.remove();
140
                break;
141
            }
142
        }
143
    }
144
145
    @SuppressWarnings("SuspiciousMethodCalls")
146
    private static void remove(CTFootnotes parent, Child child) {
147
        parent.getFootnote()
148
              .remove(child);
149
    }
150
151
    @SuppressWarnings("SuspiciousMethodCalls")
152
    private static void remove(CTEndnotes parent, Child child) {
153
        parent.getEndnote()
154
              .remove(child);
155
    }
156
157
    private static void remove(SdtRun parent, Child child) {
158
        parent.getSdtContent()
159
              .getContent()
160
              .remove(child);
161
    }
162
163
    private static boolean equals(Object o1, Object o2) {
164 1 1. equals : negated conditional → KILLED
        if (o1 instanceof JAXBElement<?> e1) o1 = e1.getValue();
165 1 1. equals : negated conditional → KILLED
        if (o2 instanceof JAXBElement<?> e2) o2 = e2.getValue();
166 2 1. equals : replaced boolean return with false for pro/verron/officestamper/utils/WmlUtils::equals → KILLED
2. equals : replaced boolean return with true for pro/verron/officestamper/utils/WmlUtils::equals → KILLED
        return Objects.equals(o1, o2);
167
    }
168
169
    /**
170
     * Checks if the given object is serializable to XML.
171
     *
172
     * @param object the object to be checked for XML serialization
173
     *
174
     * @return true if the object can be serialized to XML, false otherwise
175
     */
176
    public static boolean serializable(Object object) {
177
        try {
178
            XmlUtils.marshaltoString(object);
179 1 1. serializable : replaced boolean return with false for pro/verron/officestamper/utils/WmlUtils::serializable → KILLED
            return true;
180
        } catch (Exception e) {
181 1 1. serializable : replaced boolean return with true for pro/verron/officestamper/utils/WmlUtils::serializable → NO_COVERAGE
            return false;
182
        }
183
    }
184
}

Mutations

47

1.1
Location : getFirstParentWithClass
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

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

48

1.1
Location : getFirstParentWithClass
Killed by : none
Changed increment from 1 to -1 → SURVIVED
Covering tests

49

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

50

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

2.2
Location : getFirstParentWithClass
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
replaced return value with Optional.empty for pro/verron/officestamper/utils/WmlUtils::getFirstParentWithClass → KILLED

51

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

65

1.1
Location : extractCommentElements
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to org/docx4j/TraversalUtil::visit → KILLED

66

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

80

1.1
Location : findComment
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#28]
replaced return value with Optional.empty for pro/verron/officestamper/utils/WmlUtils::findComment → KILLED

88

1.1
Location : getPartName
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#28]
replaced return value with null for pro/verron/officestamper/utils/WmlUtils::getPartName → KILLED

96

1.1
Location : getComments
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#28]
replaced return value with null for pro/verron/officestamper/utils/WmlUtils::getComments → KILLED

103

1.1
Location : idEqual
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#28]
replaced return value with null for pro/verron/officestamper/utils/WmlUtils::idEqual → KILLED

105

1.1
Location : lambda$idEqual$0
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#28]
replaced boolean return with false for pro/verron/officestamper/utils/WmlUtils::lambda$idEqual$0 → KILLED

2.2
Location : lambda$idEqual$0
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#28]
replaced boolean return with true for pro/verron/officestamper/utils/WmlUtils::lambda$idEqual$0 → KILLED

123

1.1
Location : remove
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to pro/verron/officestamper/utils/WmlUtils::remove → KILLED

124

1.1
Location : remove
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfFootnotes(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#2]
removed call to pro/verron/officestamper/utils/WmlUtils::remove → KILLED

125

1.1
Location : remove
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfEndnotes(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to pro/verron/officestamper/utils/WmlUtils::remove → KILLED

126

1.1
Location : remove
Killed by : none
removed call to pro/verron/officestamper/utils/WmlUtils::remove → NO_COVERAGE

129

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

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

130

1.1
Location : remove
Killed by : none
removed call to pro/verron/officestamper/core/TableCellUtil::addEmptyParagraph → SURVIVED
Covering tests

137

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

138

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

139

1.1
Location : remove
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to java/util/ListIterator::remove → KILLED

164

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

165

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

166

1.1
Location : equals
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
replaced boolean return with false for pro/verron/officestamper/utils/WmlUtils::equals → KILLED

2.2
Location : equals
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
replaced boolean return with true for pro/verron/officestamper/utils/WmlUtils::equals → KILLED

179

1.1
Location : serializable
Killed by : pro.verron.officestamper.test.ResolutionTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ResolutionTest]/[test-template:testCustomResolution(java.lang.String, boolean, boolean, boolean, boolean, boolean, java.lang.String, boolean, java.lang.String)]/[test-template-invocation:#28]
replaced boolean return with false for pro/verron/officestamper/utils/WmlUtils::serializable → KILLED

181

1.1
Location : serializable
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/WmlUtils::serializable → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.20.0