DocumentUtil.java

1
package pro.verron.officestamper.core;
2
3
import jakarta.xml.bind.JAXBElement;
4
import org.docx4j.TraversalUtil;
5
import org.docx4j.XmlUtils;
6
import org.docx4j.finders.ClassFinder;
7
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
8
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
9
import org.docx4j.wml.*;
10
import org.jvnet.jaxb2_commons.ppp.Child;
11
import pro.verron.officestamper.api.DocxPart;
12
import pro.verron.officestamper.api.OfficeStamperException;
13
14
import java.util.*;
15
import java.util.stream.Stream;
16
17
/**
18
 * Utility class to retrieve elements from a document.
19
 *
20
 * @author Joseph Verron
21
 * @author DallanMC
22
 * @version ${version}
23
 * @since 1.4.7
24
 */
25
public class DocumentUtil {
26
27
    private DocumentUtil() {
28
        throw new OfficeStamperException("Utility classes shouldn't be instantiated");
29
    }
30
31
32
    public static <T> Stream<T> streamObjectElements(
33
            DocxPart source,
34
            Class<T> elementClass
35
    ) {
36
        ClassFinder finder = new ClassFinder(elementClass);
37 1 1. streamObjectElements : removed call to org/docx4j/TraversalUtil::visit → KILLED
        TraversalUtil.visit(source.part(), finder);
38 1 1. streamObjectElements : replaced return value with Stream.empty for pro/verron/officestamper/core/DocumentUtil::streamObjectElements → KILLED
        return finder.results.stream()
39
                             .map(elementClass::cast);
40
    }
41
42
    /**
43
     * Retrieve the first element from an object.
44
     *
45
     * @param subDocument the object to get the first element from
46
     *
47
     * @return the first element
48
     */
49
    public static Object lastElement(WordprocessingMLPackage subDocument) {
50
        var mainDocumentPart = subDocument.getMainDocumentPart();
51
        var mainDocumentPartContent = mainDocumentPart.getContent();
52 2 1. lastElement : Replaced integer subtraction with addition → KILLED
2. lastElement : replaced return value with null for pro/verron/officestamper/core/DocumentUtil::lastElement → KILLED
        return mainDocumentPartContent.get(mainDocumentPartContent.size() - 1);
53
    }
54
55
    /**
56
     * Retrieve the last element from an object.
57
     *
58
     * @param subDocument the object to get the last element from
59
     *
60
     * @return the last element
61
     */
62
    public static List<Object> allElements(WordprocessingMLPackage subDocument) {
63 1 1. allElements : replaced return value with Collections.emptyList for pro/verron/officestamper/core/DocumentUtil::allElements → KILLED
        return subDocument.getMainDocumentPart()
64
                          .getContent();
65
    }
66
67
    /**
68
     * Recursively walk through a source to find embedded images and import them in the target document.
69
     *
70
     * @param source source document containing image files.
71
     * @param target target document to add image files to.
72
     *
73
     * @return a {@link Map} object
74
     */
75
    public static Map<R, R> walkObjectsAndImportImages(
76
            WordprocessingMLPackage source,
77
            WordprocessingMLPackage target
78
    ) {
79 1 1. walkObjectsAndImportImages : replaced return value with Collections.emptyMap for pro/verron/officestamper/core/DocumentUtil::walkObjectsAndImportImages → KILLED
        return walkObjectsAndImportImages(source.getMainDocumentPart(),
80
                source,
81
                target);
82
    }
83
84
    /**
85
     * Recursively walk through source accessor to find embedded images and import the target document.
86
     *
87
     * @param container source container to walk.
88
     * @param source    source document containing image files.
89
     * @param target    target document to add image files to.
90
     *
91
     * @return a {@link Map} object
92
     */
93
    public static Map<R, R> walkObjectsAndImportImages(
94
            ContentAccessor container,
95
            WordprocessingMLPackage source,
96
            WordprocessingMLPackage target
97
    ) {
98
        Map<R, R> replacements = new HashMap<>();
99
        for (Object obj : container.getContent()) {
100
            Queue<Object> queue = new ArrayDeque<>();
101
            queue.add(obj);
102
103 1 1. walkObjectsAndImportImages : negated conditional → KILLED
            while (!queue.isEmpty()) {
104
                Object currentObj = queue.remove();
105
106 2 1. walkObjectsAndImportImages : negated conditional → KILLED
2. walkObjectsAndImportImages : negated conditional → KILLED
                if (currentObj instanceof R currentR && isImageRun(currentR)) {
107
                    var docxImageExtractor = new DocxImageExtractor(source);
108
                    var imageData = docxImageExtractor.getRunDrawingData(currentR);
109
                    var maxWidth = docxImageExtractor.getRunDrawingMaxWidth(currentR);
110
                    var imagePart = tryCreateImagePart(target, imageData);
111
                    var runWithImage = RunUtil.createRunWithImage(maxWidth, imagePart);
112
                    replacements.put(currentR, runWithImage);
113
                }
114 1 1. walkObjectsAndImportImages : negated conditional → KILLED
                else if (currentObj instanceof ContentAccessor contentAccessor)
115
                    queue.addAll(contentAccessor.getContent());
116
            }
117
        }
118 1 1. walkObjectsAndImportImages : replaced return value with Collections.emptyMap for pro/verron/officestamper/core/DocumentUtil::walkObjectsAndImportImages → KILLED
        return replacements;
119
    }
120
121
    /**
122
     * Check if a run contains an embedded image.
123
     *
124
     * @param run the run to analyze
125
     *
126
     * @return true if the run contains an image, false otherwise.
127
     */
128
    private static boolean isImageRun(R run) {
129 2 1. isImageRun : replaced boolean return with false for pro/verron/officestamper/core/DocumentUtil::isImageRun → KILLED
2. isImageRun : replaced boolean return with true for pro/verron/officestamper/core/DocumentUtil::isImageRun → KILLED
        return run.getContent()
130
                  .stream()
131
                  .filter(JAXBElement.class::isInstance)
132
                  .map(JAXBElement.class::cast)
133
                  .map(JAXBElement::getValue)
134
                  .anyMatch(Drawing.class::isInstance);
135
    }
136
137
    private static BinaryPartAbstractImage tryCreateImagePart(
138
            WordprocessingMLPackage destDocument,
139
            byte[] imageData
140
    ) {
141
        try {
142 1 1. tryCreateImagePart : replaced return value with null for pro/verron/officestamper/core/DocumentUtil::tryCreateImagePart → KILLED
            return BinaryPartAbstractImage.createImagePart(destDocument, imageData);
143
        } catch (Exception e) {
144
            throw new OfficeStamperException(e);
145
        }
146
    }
147
148
    /**
149
     * Finds the smallest common parent between two objects.
150
     *
151
     * @param o1 the first object
152
     * @param o2 the second object
153
     *
154
     * @return the smallest common parent of the two objects
155
     *
156
     * @throws OfficeStamperException if there is an error finding the common parent
157
     */
158
    public static ContentAccessor findSmallestCommonParent(Object o1, Object o2) {
159 2 1. findSmallestCommonParent : negated conditional → KILLED
2. findSmallestCommonParent : negated conditional → KILLED
        if (depthElementSearch(o1, o2) && o2 instanceof ContentAccessor contentAccessor)
160 1 1. findSmallestCommonParent : replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findSmallestCommonParent → KILLED
            return findInsertableParent(contentAccessor);
161 1 1. findSmallestCommonParent : negated conditional → KILLED
        else if (o2 instanceof Child child)
162 1 1. findSmallestCommonParent : replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findSmallestCommonParent → KILLED
            return findSmallestCommonParent(o1, child.getParent());
163
        else
164
            throw new OfficeStamperException();
165
    }
166
167
    /**
168
     * Recursively searches for an element in a content tree.
169
     *
170
     * @param searchTarget the element to search for
171
     * @param content      the content tree to search in
172
     *
173
     * @return true if the element is found, false otherwise
174
     */
175
    public static boolean depthElementSearch(Object searchTarget, Object content) {
176
        content = XmlUtils.unwrap(content);
177 1 1. depthElementSearch : negated conditional → KILLED
        if (searchTarget.equals(content)) {
178 1 1. depthElementSearch : replaced boolean return with false for pro/verron/officestamper/core/DocumentUtil::depthElementSearch → KILLED
            return true;
179
        }
180 1 1. depthElementSearch : negated conditional → KILLED
        else if (content instanceof ContentAccessor contentAccessor) {
181
            for (Object object : contentAccessor.getContent()) {
182
                Object unwrappedObject = XmlUtils.unwrap(object);
183 1 1. depthElementSearch : negated conditional → KILLED
                if (searchTarget.equals(unwrappedObject)
184 1 1. depthElementSearch : negated conditional → KILLED
                    || depthElementSearch(searchTarget, unwrappedObject)) {
185 1 1. depthElementSearch : replaced boolean return with false for pro/verron/officestamper/core/DocumentUtil::depthElementSearch → KILLED
                    return true;
186
                }
187
            }
188
        }
189 1 1. depthElementSearch : replaced boolean return with true for pro/verron/officestamper/core/DocumentUtil::depthElementSearch → KILLED
        return false;
190
    }
191
192
    private static ContentAccessor findInsertableParent(Object searchFrom) {
193 1 1. findInsertableParent : negated conditional → KILLED
        if (searchFrom instanceof Tc tc)
194 1 1. findInsertableParent : replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findInsertableParent → NO_COVERAGE
            return tc;
195 1 1. findInsertableParent : negated conditional → KILLED
        else if (searchFrom instanceof Body body)
196 1 1. findInsertableParent : replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findInsertableParent → KILLED
            return body;
197 1 1. findInsertableParent : negated conditional → KILLED
        else if (searchFrom instanceof Child child)
198 1 1. findInsertableParent : replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findInsertableParent → KILLED
            return findInsertableParent(child.getParent());
199
        else
200
            throw new OfficeStamperException();
201
    }
202
}

Mutations

37

1.1
Location : streamObjectElements
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:#17]
removed call to org/docx4j/TraversalUtil::visit → KILLED

38

1.1
Location : streamObjectElements
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:#17]
replaced return value with Stream.empty for pro/verron/officestamper/core/DocumentUtil::streamObjectElements → KILLED

52

1.1
Location : lastElement
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:#15]
Replaced integer subtraction with addition → KILLED

2.2
Location : lastElement
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:#15]
replaced return value with null for pro/verron/officestamper/core/DocumentUtil::lastElement → KILLED

63

1.1
Location : allElements
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:#3]
replaced return value with Collections.emptyList for pro/verron/officestamper/core/DocumentUtil::allElements → KILLED

79

1.1
Location : walkObjectsAndImportImages
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:#9]
replaced return value with Collections.emptyMap for pro/verron/officestamper/core/DocumentUtil::walkObjectsAndImportImages → KILLED

103

1.1
Location : walkObjectsAndImportImages
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:#9]
negated conditional → KILLED

106

1.1
Location : walkObjectsAndImportImages
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:#1]
negated conditional → KILLED

2.2
Location : walkObjectsAndImportImages
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:#1]
negated conditional → KILLED

114

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

118

1.1
Location : walkObjectsAndImportImages
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:#9]
replaced return value with Collections.emptyMap for pro/verron/officestamper/core/DocumentUtil::walkObjectsAndImportImages → KILLED

129

1.1
Location : isImageRun
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:#9]
replaced boolean return with false for pro/verron/officestamper/core/DocumentUtil::isImageRun → KILLED

2.2
Location : isImageRun
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:#1]
replaced boolean return with true for pro/verron/officestamper/core/DocumentUtil::isImageRun → KILLED

142

1.1
Location : tryCreateImagePart
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:#9]
replaced return value with null for pro/verron/officestamper/core/DocumentUtil::tryCreateImagePart → KILLED

159

1.1
Location : findSmallestCommonParent
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:#1]
negated conditional → KILLED

2.2
Location : findSmallestCommonParent
Killed by : pro.verron.officestamper.test.RepeatDocPartBadPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.RepeatDocPartBadPlaceholderTest]/[method:testBadExpressionShouldNotBlockCallerThread()]
negated conditional → KILLED

160

1.1
Location : findSmallestCommonParent
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:#1]
replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findSmallestCommonParent → KILLED

161

1.1
Location : findSmallestCommonParent
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:#1]
negated conditional → KILLED

162

1.1
Location : findSmallestCommonParent
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:#1]
replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findSmallestCommonParent → KILLED

177

1.1
Location : depthElementSearch
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:#1]
negated conditional → KILLED

178

1.1
Location : depthElementSearch
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:#17]
replaced boolean return with false for pro/verron/officestamper/core/DocumentUtil::depthElementSearch → KILLED

180

1.1
Location : depthElementSearch
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:#1]
negated conditional → KILLED

183

1.1
Location : depthElementSearch
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:#1]
negated conditional → KILLED

184

1.1
Location : depthElementSearch
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:#1]
negated conditional → KILLED

185

1.1
Location : depthElementSearch
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:#1]
replaced boolean return with false for pro/verron/officestamper/core/DocumentUtil::depthElementSearch → KILLED

189

1.1
Location : depthElementSearch
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:#1]
replaced boolean return with true for pro/verron/officestamper/core/DocumentUtil::depthElementSearch → KILLED

193

1.1
Location : findInsertableParent
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:#1]
negated conditional → KILLED

194

1.1
Location : findInsertableParent
Killed by : none
replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findInsertableParent → NO_COVERAGE

195

1.1
Location : findInsertableParent
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:#1]
negated conditional → KILLED

196

1.1
Location : findInsertableParent
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:#1]
replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findInsertableParent → KILLED

197

1.1
Location : findInsertableParent
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:#8]
negated conditional → KILLED

198

1.1
Location : findInsertableParent
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:#8]
replaced return value with null for pro/verron/officestamper/core/DocumentUtil::findInsertableParent → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0