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

Mutations

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

39

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

51

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

67

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

91

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

94

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

102

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

106

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

117

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

130

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

147

1.1
Location : findSmallestCommonParent
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:#14]
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

148

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

149

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

150

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

165

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

166

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

168

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

171

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

172

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

173

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

177

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

181

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

Active mutators

Tests examined


Report generated by PIT 1.17.1