WmlUtils.java

1
package pro.verron.officestamper.utils.wml;
2
3
import jakarta.xml.bind.JAXBElement;
4
import org.docx4j.TraversalUtil;
5
import org.docx4j.finders.CommentFinder;
6
import org.docx4j.mce.AlternateContent;
7
import org.docx4j.model.structure.HeaderFooterPolicy;
8
import org.docx4j.model.structure.SectionWrapper;
9
import org.docx4j.model.styles.StyleUtil;
10
import org.docx4j.openpackaging.exceptions.Docx4JException;
11
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
12
import org.docx4j.openpackaging.parts.JaxbXmlPart;
13
import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart;
14
import org.docx4j.utils.TraversalUtilVisitor;
15
import org.docx4j.vml.CTShadow;
16
import org.docx4j.vml.CTTextbox;
17
import org.docx4j.vml.VmlShapeElements;
18
import org.docx4j.wml.*;
19
import org.docx4j.wml.Comments.Comment;
20
import org.jspecify.annotations.Nullable;
21
import org.jvnet.jaxb2_commons.ppp.Child;
22
import org.slf4j.Logger;
23
import org.slf4j.LoggerFactory;
24
import pro.verron.officestamper.utils.UtilsException;
25
import pro.verron.officestamper.utils.openpackaging.OpenpackagingFactory;
26
27
import java.math.BigInteger;
28
import java.util.*;
29
import java.util.function.Consumer;
30
import java.util.function.Predicate;
31
import java.util.stream.Stream;
32
33
import static java.util.Collections.emptyList;
34
import static java.util.Optional.empty;
35
import static java.util.Optional.ofNullable;
36
import static java.util.stream.Collectors.joining;
37
import static org.docx4j.XmlUtils.unwrap;
38
import static pro.verron.officestamper.utils.wml.WmlFactory.*;
39
40
/// Utility class with methods to help in the interaction with
41
/// [WordprocessingMLPackage] documents and their elements,
42
/// such as comments, parents, and child elements.
43
public final class WmlUtils {
44
45
    private static final String PRESERVE = "preserve";
46
    private static final Logger log = LoggerFactory.getLogger(WmlUtils.class);
47
48
    private WmlUtils() {
49
        throw new UtilsException("Utility class shouldn't be instantiated");
50
    }
51
52
    /// Attempts to find the first parent of a given child element that is an
53
    /// instance of the specified class within the
54
    /// defined search depth.
55
    ///
56
    /// @param child the [Child] element from which the search for a parent
57
    /// begins.
58
    /// @param clazz the [Class] type to match for the parent
59
    /// @param depth the maximum amount levels to traverse up the parent
60
    /// hierarchy
61
    /// @param <T>   the type of the parent class to search for
62
    ///
63
    /// @return an [Optional] containing the first parent matching the specified
64
    /// class, or an empty [Optional] if no
65
    ///         match found.
66
    public static <T> Optional<T> getFirstParentWithClass(
67
            Child child,
68
            Class<T> clazz,
69
            int depth
70
    ) {
71
        var parent = child.getParent();
72
        var currentDepth = 0;
73 2 1. getFirstParentWithClass : changed conditional boundary → NO_COVERAGE
2. getFirstParentWithClass : negated conditional → NO_COVERAGE
        while (currentDepth <= depth) {
74 1 1. getFirstParentWithClass : Changed increment from 1 to -1 → NO_COVERAGE
            currentDepth++;
75 1 1. getFirstParentWithClass : negated conditional → NO_COVERAGE
            if (parent == null) return empty();
76 1 1. getFirstParentWithClass : negated conditional → NO_COVERAGE
            if (clazz.isInstance(parent))
77 1 1. getFirstParentWithClass : replaced return value with Optional.empty for pro/verron/officestamper/utils/wml/WmlUtils::getFirstParentWithClass → NO_COVERAGE
                return Optional.of(clazz.cast(parent));
78 1 1. getFirstParentWithClass : negated conditional → NO_COVERAGE
            if (parent instanceof Child next) parent = next.getParent();
79
        }
80
        return empty();
81
    }
82
83
    /// Extracts a list of comment elements from the specified
84
    /// [WordprocessingMLPackage] document.
85
    ///
86
    /// @param document the [WordprocessingMLPackage] document from which to
87
    /// extract comment elements
88
    ///
89
    /// @return a list of [Child] objects representing the extracted comment
90
    /// elements
91
    public static List<Child> extractCommentElements(WordprocessingMLPackage document) {
92
        var commentFinder = new CommentFinder();
93 1 1. extractCommentElements : removed call to org/docx4j/TraversalUtil::visit → NO_COVERAGE
        TraversalUtil.visit(document, true, commentFinder);
94 1 1. extractCommentElements : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::extractCommentElements → NO_COVERAGE
        return commentFinder.getCommentElements();
95
    }
96
97
    /// Finds a comment with the given ID in the specified
98
    /// [WordprocessingMLPackage] document.
99
    ///
100
    /// @param document the [WordprocessingMLPackage] document to search for
101
    /// the comment
102
    /// @param id       the ID of the comment to find
103
    ///
104
    /// @return an [Optional] containing the [Comment] if found, or an empty
105
    /// [Optional] if not found.
106
    public static Optional<Comment> findComment(
107
            WordprocessingMLPackage document,
108
            BigInteger id
109
    ) {
110
        var name = OpenpackagingFactory.newPartName("/word/comments.xml");
111
        var parts = document.getParts();
112
        var wordComments = (CommentsPart) parts.get(name);
113
        var comments = getComments(wordComments);
114 1 1. findComment : replaced return value with Optional.empty for pro/verron/officestamper/utils/wml/WmlUtils::findComment → NO_COVERAGE
        return comments.getComment()
115
                       .stream()
116
                       .filter(idEqual(id))
117
                       .findFirst();
118
    }
119
120
    private static Comments getComments(CommentsPart wordComments) {
121
        try {
122 1 1. getComments : replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::getComments → NO_COVERAGE
            return wordComments.getContents();
123
        } catch (Docx4JException e) {
124
            throw new UtilsException(e);
125
        }
126
    }
127
128
    private static Predicate<Comment> idEqual(BigInteger id) {
129 1 1. idEqual : replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::idEqual → NO_COVERAGE
        return comment -> {
130
            var commentId = comment.getId();
131 2 1. lambda$idEqual$0 : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::lambda$idEqual$0 → NO_COVERAGE
2. lambda$idEqual$0 : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::lambda$idEqual$0 → NO_COVERAGE
            return commentId.equals(id);
132
        };
133
    }
134
135
136
    /// Removes the specified child element from its parent container. Depending
137
    /// on the type of the parent element, the
138
    /// removal process is delegated to the appropriate helper method. If the
139
    /// child is contained within a table cell and
140
    /// the cell is empty after removal, an empty paragraph is added to the
141
    /// cell.
142
    ///
143
    /// @param child the [Child] element to be removed
144
    ///
145
    /// @throws UtilsException if the parent of the child element is of an
146
    /// unexpected type
147
    public static void remove(Child child) {
148
        switch (child.getParent()) {
149 1 1. remove : removed call to pro/verron/officestamper/utils/wml/WmlUtils::remove → NO_COVERAGE
            case ContentAccessor parent -> remove(parent, child);
150 1 1. remove : removed call to pro/verron/officestamper/utils/wml/WmlUtils::remove → NO_COVERAGE
            case CTFootnotes parent -> remove(parent, child);
151 1 1. remove : removed call to pro/verron/officestamper/utils/wml/WmlUtils::remove → NO_COVERAGE
            case CTEndnotes parent -> remove(parent, child);
152 1 1. remove : removed call to pro/verron/officestamper/utils/wml/WmlUtils::remove → NO_COVERAGE
            case SdtRun parent -> remove(parent, child);
153
            default -> throw new UtilsException(
154
                    "Unexpected value: " + child.getParent());
155
        }
156 2 1. remove : negated conditional → NO_COVERAGE
2. remove : removed call to pro/verron/officestamper/utils/wml/WmlUtils::ensureValidity → NO_COVERAGE
        if (child.getParent() instanceof Tc cell) ensureValidity(cell);
157
    }
158
159
    private static void remove(ContentAccessor parent, Child child) {
160
        var siblings = parent.getContent();
161
        var iterator = siblings.listIterator();
162 1 1. remove : negated conditional → NO_COVERAGE
        while (iterator.hasNext()) {
163 1 1. remove : negated conditional → NO_COVERAGE
            if (equals(iterator.next(), child)) {
164 1 1. remove : removed call to java/util/ListIterator::remove → NO_COVERAGE
                iterator.remove();
165
                break;
166
            }
167
        }
168
    }
169
170
    @SuppressWarnings("SuspiciousMethodCalls")
171
    private static void remove(CTFootnotes parent, Child child) {
172
        parent.getFootnote()
173
              .remove(child);
174
    }
175
176
    @SuppressWarnings("SuspiciousMethodCalls")
177
    private static void remove(CTEndnotes parent, Child child) {
178
        parent.getEndnote()
179
              .remove(child);
180
    }
181
182
    private static void remove(SdtRun parent, Child child) {
183
        parent.getSdtContent()
184
              .getContent()
185
              .remove(child);
186
    }
187
188
    /// Utility method to ensure the validity of a table cell by adding an empty
189
    /// paragraph if necessary.
190
    ///
191
    /// @param cell the [Tc] to be checked and updated.
192
    public static void ensureValidity(Tc cell) {
193 1 1. ensureValidity : negated conditional → NO_COVERAGE
        if (!containsAnElementOfAnyClasses(cell.getContent(),
194
                P.class,
195
                Tbl.class)) {
196 1 1. ensureValidity : removed call to pro/verron/officestamper/utils/wml/WmlUtils::addEmptyParagraph → NO_COVERAGE
            addEmptyParagraph(cell);
197
        }
198
    }
199
200
    private static boolean equals(Object o1, Object o2) {
201 1 1. equals : negated conditional → NO_COVERAGE
        if (o1 instanceof JAXBElement<?> e1) o1 = e1.getValue();
202 1 1. equals : negated conditional → NO_COVERAGE
        if (o2 instanceof JAXBElement<?> e2) o2 = e2.getValue();
203 2 1. equals : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::equals → NO_COVERAGE
2. equals : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::equals → NO_COVERAGE
        return Objects.equals(o1, o2);
204
    }
205
206
    private static boolean containsAnElementOfAnyClasses(
207
            Collection<Object> collection,
208
            Class<?>... classes
209
    ) {
210 2 1. containsAnElementOfAnyClasses : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::containsAnElementOfAnyClasses → NO_COVERAGE
2. containsAnElementOfAnyClasses : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::containsAnElementOfAnyClasses → NO_COVERAGE
        return collection.stream()
211 2 1. lambda$containsAnElementOfAnyClasses$0 : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::lambda$containsAnElementOfAnyClasses$0 → NO_COVERAGE
2. lambda$containsAnElementOfAnyClasses$0 : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::lambda$containsAnElementOfAnyClasses$0 → NO_COVERAGE
                         .anyMatch(element -> isAnElementOfAnyClasses(element,
212
                                 classes));
213
    }
214
215
    private static void addEmptyParagraph(Tc cell) {
216
        var emptyParagraph = WmlFactory.newParagraph();
217
        var cellContent = cell.getContent();
218
        cellContent.add(emptyParagraph);
219
    }
220
221
    private static boolean isAnElementOfAnyClasses(
222
            Object element,
223
            Class<?>... classes
224
    ) {
225
        for (var clazz : classes) {
226 2 1. isAnElementOfAnyClasses : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::isAnElementOfAnyClasses → NO_COVERAGE
2. isAnElementOfAnyClasses : negated conditional → NO_COVERAGE
            if (clazz.isInstance(unwrapJAXBElement(element))) return true;
227
        }
228 1 1. isAnElementOfAnyClasses : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::isAnElementOfAnyClasses → NO_COVERAGE
        return false;
229
    }
230
231
    private static Object unwrapJAXBElement(Object element) {
232 2 1. unwrapJAXBElement : negated conditional → NO_COVERAGE
2. unwrapJAXBElement : replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::unwrapJAXBElement → NO_COVERAGE
        return element instanceof JAXBElement<?> jaxbElement
233
                ? jaxbElement.getValue()
234
                : element;
235
    }
236
237
    /// Extracts textual content from a given object, handling various object
238
    /// types, such as runs, text elements, and
239
    /// other specific constructs. The method accounts for different cases, such
240
    /// as run breaks, hyphens, and other
241
    /// document-specific constructs, and converts them into corresponding
242
    /// string representations.
243
    ///
244
    /// @param content the object from which text content is to be extracted.
245
    /// This could be of various types
246
    ///         such as [R], [JAXBElement], [Text] or specific document
247
    /// elements.
248
    ///
249
    /// @return a string representation of the extracted textual content. If the
250
    /// object's type is not handled, an empty
251
    ///         string is returned.
252
    public static String asString(Object content) {
253 1 1. asString : replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils::asString → NO_COVERAGE
        return switch (content) {
254
            case P paragraph -> asString(paragraph.getContent());
255
            case R run -> asString(run.getContent());
256
            case JAXBElement<?> jaxbElement when jaxbElement.getName()
257
                                                            .getLocalPart()
258 1 1. asString : negated conditional → NO_COVERAGE
                                                            .equals("instrText") ->
259
                    "<instrText>";
260
            case JAXBElement<?> jaxbElement when !jaxbElement.getName()
261
                                                             .getLocalPart()
262 1 1. asString : negated conditional → NO_COVERAGE
                                                             .equals("instrText") ->
263
                    asString(jaxbElement.getValue());
264
            case Text text -> asString(text);
265
            case R.Tab _ -> "\t";
266
            case R.Cr _ -> "\n";
267 1 1. asString : negated conditional → NO_COVERAGE
            case Br br when br.getType() == null -> "\n";
268 1 1. asString : negated conditional → NO_COVERAGE
            case Br br when br.getType() == STBrType.PAGE -> "\n";
269 1 1. asString : negated conditional → NO_COVERAGE
            case Br br when br.getType() == STBrType.COLUMN -> "\n";
270 1 1. asString : negated conditional → NO_COVERAGE
            case Br br when br.getType() == STBrType.TEXT_WRAPPING -> "\n";
271
272
            case R.NoBreakHyphen _ -> "‑";
273
            case R.SoftHyphen _ -> "\u00AD";
274 4 1. asString : negated conditional → NO_COVERAGE
2. asString : negated conditional → NO_COVERAGE
3. asString : negated conditional → NO_COVERAGE
4. asString : negated conditional → NO_COVERAGE
            case R.LastRenderedPageBreak _, R.AnnotationRef _,
275
                 R.CommentReference _, Drawing _ -> "";
276
            case FldChar _ -> "<fldchar>";
277
            case CTFtnEdnRef ref -> "<ref(%s)>".formatted(ref.getId());
278
            case R.Sym sym ->
279
                    "<sym(%s, %s)>".formatted(sym.getFont(), sym.getChar());
280
            case List<?> list -> list.stream()
281
                                     .map(WmlUtils::asString)
282
                                     .collect(joining());
283 2 1. asString : negated conditional → NO_COVERAGE
2. asString : negated conditional → NO_COVERAGE
            case ProofErr _, CTShadow _ -> "";
284
            case SdtRun sdtRun -> asString(sdtRun.getSdtContent());
285
            case ContentAccessor contentAccessor ->
286
                    asString(contentAccessor.getContent());
287
            case Pict pict -> "<pict(%s)>".formatted(pict.getAnchorId());
288
            case VmlShapeElements vmlShapeElements ->
289
                    asString(vmlShapeElements.getEGShapeElements());
290
            case CTTextbox textbox -> asString(textbox.getTxbxContent());
291 2 1. asString : negated conditional → NO_COVERAGE
2. asString : negated conditional → NO_COVERAGE
            case CommentRangeStart _, CommentRangeEnd _ -> "";
292
            case AlternateContent ac -> {
293
                var choices = ac.getChoice();
294 1 1. asString : Replaced integer addition with subtraction → NO_COVERAGE
                yield "<alternateContent(%d)>".formatted(choices.size() + 1);
295
            }
296
            default -> {
297
                log.debug("Unhandled object type: {}", content.getClass());
298
                yield "";
299
            }
300
        };
301
    }
302
303
    private static String asString(Text text) {
304
        // According to specs, the 'space' value can be empty or 'preserve'.
305
        // In the first case, we are supposed to ignore spaces around the
306
        // 'text' value.
307
        var value = text.getValue();
308
        var space = text.getSpace();
309 2 1. asString : negated conditional → NO_COVERAGE
2. asString : replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils::asString → NO_COVERAGE
        return Objects.equals(space, PRESERVE) ? value : value.trim();
310
    }
311
312
    /// Inserts a smart tag with the specified element type into the given
313
    /// paragraph at the position of the expression.
314
    ///
315
    /// @param element    the element type for the smart tag
316
    /// @param paragraph  the [P] paragraph to insert the smart tag into
317
    /// @param expression the expression to replace with the smart tag
318
    /// @param start      the start index of the expression
319
    /// @param end        the end index of the expression
320
    ///
321
    /// @return a list of [Object] representing the updated content
322
    public static List<Object> insertSmartTag(
323
            String element,
324
            P paragraph,
325
            String expression,
326
            int start,
327
            int end
328
    ) {
329
        var run = newRun(expression);
330
        var smartTag = newSmartTag("officestamper",
331
                newCtAttr("type", element),
332
                run);
333 1 1. insertSmartTag : removed call to java/util/Optional::ifPresent → NO_COVERAGE
        findFirstAffectedRunPr(paragraph, start, end).ifPresent(run::setRPr);
334 1 1. insertSmartTag : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::insertSmartTag → NO_COVERAGE
        return replace(paragraph, List.of(smartTag), start, end);
335
    }
336
337
    /// Finds the first affected run properties within the specified range.
338
    ///
339
    /// @param contentAccessor the [ContentAccessor] to search in
340
    /// @param start           the start index of the range
341
    /// @param end             the end index of the range
342
    ///
343
    /// @return an [Optional] containing the [RPr] if found, or an empty
344
    /// [Optional] if not found
345
    public static Optional<RPr> findFirstAffectedRunPr(
346
            ContentAccessor contentAccessor,
347
            int start,
348
            int end
349
    ) {
350
        var iterator = new DocxIterator(contentAccessor).selectClass(R.class);
351
        var runs = StandardRun.wrap(iterator);
352
353
        var affectedRuns = runs.stream()
354 2 1. lambda$findFirstAffectedRunPr$0 : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::lambda$findFirstAffectedRunPr$0 → NO_COVERAGE
2. lambda$findFirstAffectedRunPr$0 : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::lambda$findFirstAffectedRunPr$0 → NO_COVERAGE
                               .filter(run -> run.isTouchedByRange(start, end))
355
                               .toList();
356
357
        var firstRun = affectedRuns.getFirst();
358
        var firstRunPr = firstRun.getPr();
359 1 1. findFirstAffectedRunPr : replaced return value with Optional.empty for pro/verron/officestamper/utils/wml/WmlUtils::findFirstAffectedRunPr → NO_COVERAGE
        return ofNullable(firstRunPr);
360
    }
361
362
    /// Replaces content within the specified range with the provided insert
363
    /// objects.
364
    ///
365
    /// @param contentAccessor the [ContentAccessor] in which to replace content
366
    /// @param insert          the list of objects to insert
367
    /// @param startIndex      the start index of the range to replace
368
    /// @param endIndex        the end index of the range to replace
369
    ///
370
    /// @return a list of [Object] representing the updated content
371
    public static List<Object> replace(
372
            ContentAccessor contentAccessor,
373
            List<Object> insert,
374
            int startIndex,
375
            int endIndex
376
    ) {
377
        var iterator = new DocxIterator(contentAccessor).selectClass(R.class);
378
        var runs = StandardRun.wrap(iterator);
379
        var affectedRuns = runs.stream()
380 2 1. lambda$replace$0 : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::lambda$replace$0 → NO_COVERAGE
2. lambda$replace$0 : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::lambda$replace$0 → NO_COVERAGE
                               .filter(run -> run.isTouchedByRange(startIndex,
381
                                       endIndex))
382
                               .toList();
383
384
        var firstRun = affectedRuns.getFirst();
385
        var firstR = firstRun.run();
386
        var firstSiblings = ((ContentAccessor) firstR.getParent()).getContent();
387
        var firstIndex = firstSiblings.indexOf(firstRun.run());
388
389 1 1. replace : negated conditional → NO_COVERAGE
        boolean singleRun = affectedRuns.size() == 1;
390 1 1. replace : negated conditional → NO_COVERAGE
        if (singleRun) {
391 1 1. replace : Replaced integer subtraction with addition → NO_COVERAGE
            boolean expressionSpansCompleteRun =
392 1 1. replace : negated conditional → NO_COVERAGE
                    endIndex - startIndex == firstRun.length();
393
            boolean expressionAtStartOfRun =
394 1 1. replace : negated conditional → NO_COVERAGE
                    startIndex == firstRun.startIndex();
395 1 1. replace : negated conditional → NO_COVERAGE
            boolean expressionAtEndOfRun = endIndex == firstRun.endIndex();
396 2 1. replace : changed conditional boundary → NO_COVERAGE
2. replace : negated conditional → NO_COVERAGE
            boolean expressionWithinRun = startIndex > firstRun.startIndex()
397 2 1. replace : changed conditional boundary → NO_COVERAGE
2. replace : negated conditional → NO_COVERAGE
                                          && endIndex <= firstRun.endIndex();
398
399 1 1. replace : negated conditional → NO_COVERAGE
            if (expressionSpansCompleteRun) {
400 1 1. replace : removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE
                firstRun.replace(startIndex, endIndex, "");
401
                firstSiblings.addAll(firstIndex, insert);
402
            }
403 1 1. replace : negated conditional → NO_COVERAGE
            else if (expressionAtStartOfRun) {
404 1 1. replace : removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE
                firstRun.replace(startIndex, endIndex, "");
405
                firstSiblings.addAll(firstIndex, insert);
406
            }
407 1 1. replace : negated conditional → NO_COVERAGE
            else if (expressionAtEndOfRun) {
408 1 1. replace : removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE
                firstRun.replace(startIndex, endIndex, "");
409 1 1. replace : Replaced integer addition with subtraction → NO_COVERAGE
                firstSiblings.addAll(firstIndex + 1, insert);
410
            }
411 1 1. replace : negated conditional → NO_COVERAGE
            else if (expressionWithinRun) {
412
                var originalRun = firstRun.run();
413
                var originalRPr = originalRun.getRPr();
414
                var newStartRun = create(firstRun.left(startIndex),
415
                        originalRPr);
416
                var newEndRun = create(firstRun.right(endIndex), originalRPr);
417
                firstSiblings.remove(firstIndex);
418
                firstSiblings.addAll(firstIndex,
419
                        wrap(newStartRun, insert, newEndRun));
420
            }
421
        }
422
        else {
423
            StandardRun lastRun = affectedRuns.getLast();
424 1 1. replace : removed call to pro/verron/officestamper/utils/wml/WmlUtils::removeExpression → NO_COVERAGE
            removeExpression(firstSiblings,
425
                    firstRun,
426
                    startIndex,
427
                    endIndex,
428
                    lastRun,
429
                    affectedRuns);
430
            // add replacement run between first and last run
431 1 1. replace : Replaced integer addition with subtraction → NO_COVERAGE
            firstSiblings.addAll(firstIndex + 1, insert);
432
        }
433 1 1. replace : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::replace → NO_COVERAGE
        return new ArrayList<>(contentAccessor.getContent());
434
    }
435
436
    /// Creates a new run with the specified text, and the specified run style.
437
    ///
438
    /// @param text the initial text of the [R].
439
    /// @param rPr  the [RPr] to apply to the run
440
    ///
441
    /// @return the newly created [R].
442
    public static R create(String text, RPr rPr) {
443
        R newStartRun = newRun(text);
444 1 1. create : removed call to org/docx4j/wml/R::setRPr → NO_COVERAGE
        newStartRun.setRPr(rPr);
445 1 1. create : replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::create → NO_COVERAGE
        return newStartRun;
446
    }
447
448
    private static Collection<?> wrap(
449
            R prefix,
450
            Collection<?> elements,
451
            R suffix
452
    ) {
453
        var merge = new ArrayList<>();
454
        merge.add(prefix);
455
        merge.addAll(elements);
456
        merge.add(suffix);
457 1 1. wrap : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::wrap → NO_COVERAGE
        return merge;
458
    }
459
460
    private static void removeExpression(
461
            List<Object> contents,
462
            StandardRun firstRun,
463
            int matchStartIndex,
464
            int matchEndIndex,
465
            StandardRun lastRun,
466
            List<StandardRun> affectedRuns
467
    ) {
468
        // remove the expression from the first run
469 1 1. removeExpression : removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE
        firstRun.replace(matchStartIndex, matchEndIndex, "");
470
        // remove all runs between first and last
471
        for (StandardRun run : affectedRuns) {
472 2 1. removeExpression : negated conditional → NO_COVERAGE
2. removeExpression : negated conditional → NO_COVERAGE
            if (!Objects.equals(run, firstRun) && !Objects.equals(run,
473
                    lastRun)) {
474
                contents.remove(run.run());
475
            }
476
        }
477
        // remove the expression from the last run
478 1 1. removeExpression : removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE
        lastRun.replace(matchStartIndex, matchEndIndex, "");
479
    }
480
481
    /// Creates a new run with the specified text and inherits the style of the
482
    /// parent paragraph.
483
    ///
484
    /// @param text        the initial text of the [R].
485
    /// @param paragraphPr the [PPr] to apply to the run
486
    ///
487
    /// @return the newly created [R].
488
    public static R create(String text, PPr paragraphPr) {
489
        R run = newRun(text);
490 1 1. create : removed call to pro/verron/officestamper/utils/wml/WmlUtils::applyParagraphStyle → NO_COVERAGE
        applyParagraphStyle(run, paragraphPr);
491 1 1. create : replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::create → NO_COVERAGE
        return run;
492
    }
493
494
    /// Applies the style of the given paragraph to the given content object (if
495
    /// the content object is a [R]).
496
    ///
497
    /// @param run         the [R] to which the style should be applied.
498
    /// @param paragraphPr the [PPr] containing the style to apply
499
    public static void applyParagraphStyle(R run, @Nullable PPr paragraphPr) {
500 1 1. applyParagraphStyle : negated conditional → NO_COVERAGE
        if (paragraphPr == null) return;
501
        var runPr = paragraphPr.getRPr();
502 1 1. applyParagraphStyle : negated conditional → NO_COVERAGE
        if (runPr == null) return;
503
        RPr runProperties = new RPr();
504
        StyleUtil.apply(runPr, runProperties);
505 1 1. applyParagraphStyle : removed call to org/docx4j/wml/R::setRPr → NO_COVERAGE
        run.setRPr(runProperties);
506
    }
507
508
    /// Sets the text of the given run to the given value.
509
    ///
510
    /// @param run  the [R] whose text to change.
511
    /// @param text the text to set.
512
    public static void setText(R run, String text) {
513
        run.getContent()
514 1 1. setText : removed call to java/util/List::clear → NO_COVERAGE
           .clear();
515
        Text textObj = newText(text);
516
        run.getContent()
517
           .add(textObj);
518
    }
519
520
    /// Replaces all occurrences of the specified expression with the provided
521
    /// run objects.
522
    ///
523
    /// @param contentAccessor the [ContentAccessor] in which to replace the
524
    /// expression
525
    /// @param expression      the expression to replace
526
    /// @param insert          the list of objects to insert
527
    /// @param onRPr           a consumer to handle [RPr] properties
528
    ///
529
    /// @return a list of [Object] representing the updated content
530
    public static List<Object> replaceExpressionWithRun(
531
            ContentAccessor contentAccessor,
532
            String expression,
533
            List<Object> insert,
534
            Consumer<RPr> onRPr
535
    ) {
536
        var text = asString(contentAccessor);
537
        int matchStartIndex = text.indexOf(expression);
538 1 1. replaceExpressionWithRun : negated conditional → NO_COVERAGE
        if (matchStartIndex == -1) /*nothing to replace*/
539 1 1. replaceExpressionWithRun : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::replaceExpressionWithRun → NO_COVERAGE
            return contentAccessor.getContent();
540 1 1. replaceExpressionWithRun : Replaced integer addition with subtraction → NO_COVERAGE
        int matchEndIndex = matchStartIndex + expression.length();
541
        findFirstAffectedRunPr(contentAccessor,
542
                matchStartIndex,
543 1 1. replaceExpressionWithRun : removed call to java/util/Optional::ifPresent → NO_COVERAGE
                matchEndIndex).ifPresent(onRPr);
544 1 1. replaceExpressionWithRun : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::replaceExpressionWithRun → NO_COVERAGE
        return replace(contentAccessor, insert, matchStartIndex, matchEndIndex);
545
    }
546
547
    /// Checks if the given [CTSmartTagRun] contains an element that matches the
548
    /// expected element.
549
    ///
550
    /// @param tag             the [CTSmartTagRun] object to be evaluated
551
    /// @param expectedElement the expected element to compare against
552
    ///
553
    /// @return true if the actual element of the given tag matches the expected
554
    /// element, false otherwise
555
    public static boolean isTagElement(
556
            CTSmartTagRun tag,
557
            String expectedElement
558
    ) {
559
        var actualElement = tag.getElement();
560 2 1. isTagElement : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::isTagElement → NO_COVERAGE
2. isTagElement : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::isTagElement → NO_COVERAGE
        return Objects.equals(expectedElement, actualElement);
561
    }
562
563
    /// Sets or updates an attribute for the specified smart tag. This method
564
    /// ensures that the provided attribute
565
    /// key-value pair is added to the smart tag's attribute list. If the
566
    /// attribute already exists, its value is
567
    /// updated. If the smart tag or its attribute metadata is null, they are
568
    /// initialized.
569
    ///
570
    /// @param smartTag       the smart tag object to modify
571
    /// @param attributeKey   the key of the attribute to set or update
572
    /// @param attributeValue the value to assign to the specified attribute key
573
    public static void setTagAttribute(
574
            CTSmartTagRun smartTag,
575
            String attributeKey,
576
            String attributeValue
577
    ) {
578
        var smartTagPr = smartTag.getSmartTagPr();
579 1 1. setTagAttribute : negated conditional → NO_COVERAGE
        if (smartTagPr == null) {
580
            smartTagPr = new CTSmartTagPr();
581 1 1. setTagAttribute : removed call to org/docx4j/wml/CTSmartTagRun::setSmartTagPr → NO_COVERAGE
            smartTag.setSmartTagPr(smartTagPr);
582
        }
583
        var smartTagPrAttr = smartTagPr.getAttr();
584 1 1. setTagAttribute : negated conditional → NO_COVERAGE
        if (smartTagPrAttr == null) {
585
            smartTagPrAttr = new ArrayList<>();
586 1 1. setTagAttribute : removed call to org/docx4j/wml/CTSmartTagRun::setSmartTagPr → NO_COVERAGE
            smartTag.setSmartTagPr(smartTagPr);
587
        }
588
        for (CTAttr attribute : smartTagPrAttr) {
589 1 1. setTagAttribute : negated conditional → NO_COVERAGE
            if (attributeKey.equals(attribute.getName())) {
590 1 1. setTagAttribute : removed call to org/docx4j/wml/CTAttr::setVal → NO_COVERAGE
                attribute.setVal(attributeValue);
591
                return;
592
            }
593
        }
594
        var ctAttr = newAttribute(attributeKey, attributeValue);
595
        smartTagPrAttr.add(ctAttr);
596
    }
597
598
    /// Creates a new attribute object with the specified key and value.
599
    ///
600
    /// @param attributeKey   the key for the new attribute
601
    /// @param attributeValue the value for the new attribute
602
    ///
603
    /// @return a CTAttr object representing the new attribute
604
    public static CTAttr newAttribute(
605
            String attributeKey,
606
            String attributeValue
607
    ) {
608 1 1. newAttribute : replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::newAttribute → NO_COVERAGE
        return newCtAttr(attributeKey, attributeValue);
609
    }
610
611
    /// Deletes all elements associated with the specified comment from the
612
    /// provided list of items.
613
    ///
614
    /// @param commentId the ID of the comment to be deleted
615
    /// @param items     the list of items from which elements associated with
616
    /// the comment will be deleted
617
    public static void deleteCommentFromElements(
618
            BigInteger commentId,
619
            List<Object> items
620
    ) {
621
        record DeletableItems(List<Object> container, List<Object> items) {
622
            static List<DeletableItems> findAll(
623
                    List<Object> items,
624
                    BigInteger commentId
625
            ) {
626 2 1. lambda$findAll$0 : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::lambda$findAll$0 → NO_COVERAGE
2. lambda$findAll$0 : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::lambda$findAll$0 → NO_COVERAGE
                Predicate<BigInteger> predicate = bi -> Objects.equals(bi,
627
                        commentId);
628
                List<DeletableItems> elementsToRemove = new ArrayList<>();
629 1 1. findAll : removed call to java/util/List::forEach → NO_COVERAGE
                items.forEach(item -> {
630
                    Object unwrapped = unwrap(item);
631
                    // Recursively finds deletable items associated with
632
                    // comment ID
633
                    elementsToRemove.addAll(switch (unwrapped) {
634
                        case CTSmartTagRun str when str.getContent()
635
                                                       .stream()
636 1 1. lambda$findAll$1 : negated conditional → NO_COVERAGE
                                                       .anyMatch(i ->
637
                                                               i instanceof CommentRangeStart crs
638 3 1. lambda$findAll$2 : negated conditional → NO_COVERAGE
2. lambda$findAll$2 : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::lambda$findAll$2 → NO_COVERAGE
3. lambda$findAll$2 : negated conditional → NO_COVERAGE
                                                               && predicate.test(
639
                                                                       crs.getId())) ->
640
                                from(items, item);
641 1 1. lambda$findAll$1 : negated conditional → NO_COVERAGE
                        case CommentRangeStart crs when predicate.test(crs.getId()) ->
642
                                from(items, item);
643 1 1. lambda$findAll$1 : negated conditional → NO_COVERAGE
                        case CommentRangeEnd cre when predicate.test(cre.getId()) ->
644
                                from(items, item);
645 1 1. lambda$findAll$1 : negated conditional → NO_COVERAGE
                        case R.CommentReference rcr when predicate.test(rcr.getId()) ->
646
                                from(items, item);
647
                        case ContentAccessor ca -> findAll(ca, commentId);
648
                        case SdtRun sdtRun -> findAll(sdtRun, commentId);
649
                        default -> emptyList();
650
                    });
651
                });
652 1 1. findAll : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::findAll → NO_COVERAGE
                return elementsToRemove;
653
            }
654
655
            private static Collection<DeletableItems> findAll(
656
                    SdtRun sdtRun,
657
                    BigInteger commentId
658
            ) {
659 1 1. findAll : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::findAll → NO_COVERAGE
                return findAll(sdtRun.getSdtContent(), commentId);
660
            }
661
662
            private static Collection<DeletableItems> findAll(
663
                    ContentAccessor ca,
664
                    BigInteger commentId
665
            ) {
666 1 1. findAll : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::findAll → NO_COVERAGE
                return findAll(ca.getContent(), commentId);
667
            }
668
669
            private static List<DeletableItems> from(
670
                    List<Object> items,
671
                    Object item
672
            ) {
673 1 1. from : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::from → NO_COVERAGE
                return Collections.singletonList(new DeletableItems(items,
674
                        List.of(item)));
675
            }
676
        }
677
        DeletableItems.findAll(items, commentId)
678 1 1. deleteCommentFromElements : removed call to java/util/List::forEach → NO_COVERAGE
                      .forEach(p -> p.container.removeAll(p.items));
679
    }
680
681
    /// Visits the document's main content, header, footer, footnotes, and
682
    /// endnotes using the specified visitor.
683
    ///
684
    /// @param document the WordprocessingMLPackage representing the document
685
    ///  to be visited
686
    /// @param visitor  the TraversalUtilVisitor to be applied to each relevant
687
    /// part of the document
688
    public static void visitDocument(
689
            WordprocessingMLPackage document,
690
            TraversalUtilVisitor<?> visitor
691
    ) {
692
        var mainDocumentPart = document.getMainDocumentPart();
693 1 1. visitDocument : removed call to org/docx4j/TraversalUtil::visit → NO_COVERAGE
        TraversalUtil.visit(mainDocumentPart, visitor);
694
        WmlUtils.streamHeaderFooterPart(document)
695 2 1. lambda$visitDocument$0 : removed call to org/docx4j/TraversalUtil::visit → NO_COVERAGE
2. visitDocument : removed call to java/util/stream/Stream::forEach → NO_COVERAGE
                .forEach(f -> TraversalUtil.visit(f, visitor));
696 1 1. visitDocument : removed call to pro/verron/officestamper/utils/wml/WmlUtils::visitPartIfExists → NO_COVERAGE
        WmlUtils.visitPartIfExists(visitor,
697
                mainDocumentPart.getFootnotesPart());
698 1 1. visitDocument : removed call to pro/verron/officestamper/utils/wml/WmlUtils::visitPartIfExists → NO_COVERAGE
        WmlUtils.visitPartIfExists(visitor, mainDocumentPart.getEndNotesPart());
699
    }
700
701
    private static Stream<Object> streamHeaderFooterPart(WordprocessingMLPackage document) {
702 1 1. streamHeaderFooterPart : replaced return value with Stream.empty for pro/verron/officestamper/utils/wml/WmlUtils::streamHeaderFooterPart → NO_COVERAGE
        return document.getDocumentModel()
703
                       .getSections()
704
                       .stream()
705
                       .map(SectionWrapper::getHeaderFooterPolicy)
706
                       .flatMap(WmlUtils::extractHeaderFooterParts);
707
    }
708
709
    private static void visitPartIfExists(
710
            TraversalUtilVisitor<?> visitor,
711
            @Nullable JaxbXmlPart<?> part
712
    ) {
713
        ofNullable(part).map(WmlUtils::extractContent)
714 2 1. lambda$visitPartIfExists$0 : removed call to org/docx4j/TraversalUtil::visit → NO_COVERAGE
2. visitPartIfExists : removed call to java/util/Optional::ifPresent → NO_COVERAGE
                        .ifPresent(c -> TraversalUtil.visit(c, visitor));
715
    }
716
717
    private static Stream<JaxbXmlPart<?>> extractHeaderFooterParts(
718
            HeaderFooterPolicy hfp
719
    ) {
720
        Stream.Builder<JaxbXmlPart<?>> builder = Stream.builder();
721 1 1. extractHeaderFooterParts : removed call to java/util/Optional::ifPresent → NO_COVERAGE
        ofNullable(hfp.getFirstHeader()).ifPresent(builder::add);
722 1 1. extractHeaderFooterParts : removed call to java/util/Optional::ifPresent → NO_COVERAGE
        ofNullable(hfp.getDefaultHeader()).ifPresent(builder::add);
723 1 1. extractHeaderFooterParts : removed call to java/util/Optional::ifPresent → NO_COVERAGE
        ofNullable(hfp.getEvenHeader()).ifPresent(builder::add);
724 1 1. extractHeaderFooterParts : removed call to java/util/Optional::ifPresent → NO_COVERAGE
        ofNullable(hfp.getFirstFooter()).ifPresent(builder::add);
725 1 1. extractHeaderFooterParts : removed call to java/util/Optional::ifPresent → NO_COVERAGE
        ofNullable(hfp.getDefaultFooter()).ifPresent(builder::add);
726 1 1. extractHeaderFooterParts : removed call to java/util/Optional::ifPresent → NO_COVERAGE
        ofNullable(hfp.getEvenFooter()).ifPresent(builder::add);
727 1 1. extractHeaderFooterParts : replaced return value with Stream.empty for pro/verron/officestamper/utils/wml/WmlUtils::extractHeaderFooterParts → NO_COVERAGE
        return builder.build();
728
    }
729
730
    private static Object extractContent(JaxbXmlPart<?> jaxbXmlPart) {
731
        try {
732 1 1. extractContent : replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::extractContent → NO_COVERAGE
            return jaxbXmlPart.getContents();
733
        } catch (Docx4JException e) {
734
            throw new UtilsException(e);
735
        }
736
    }
737
738
    /// Checks if the provided smart tag contains an attribute with the
739
    /// specified key and value.
740
    ///
741
    /// @param tag     the smart tag to search for the attribute
742
    /// @param attrKey the key of the attribute to search for
743
    /// @param attrVal the value of the attribute to match
744
    ///
745
    /// @return true if the smart tag contains an attribute with the specified
746
    /// key and value, false otherwise
747
    public static boolean hasTagAttribute(
748
            CTSmartTagRun tag,
749
            String attrKey,
750
            String attrVal
751
    ) {
752
        var smartTagPr = tag.getSmartTagPr();
753
        var smartTagPrAttr = smartTagPr.getAttr();
754
        for (CTAttr ctAttr : smartTagPrAttr)
755 1 1. hasTagAttribute : negated conditional → NO_COVERAGE
            if (Objects.equals(ctAttr.getName(), attrKey))
756 2 1. hasTagAttribute : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::hasTagAttribute → NO_COVERAGE
2. hasTagAttribute : replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::hasTagAttribute → NO_COVERAGE
                return Objects.equals(ctAttr.getVal(), attrVal);
757 1 1. hasTagAttribute : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::hasTagAttribute → NO_COVERAGE
        return false;
758
    }
759
760
    /// @param startIndex the start index of the run relative to the
761
    /// containing paragraph.
762
    /// @param run        the [R] run itself.
763
    private record StandardRun(int startIndex, R run) {
764
765
        /// Initializes a list of [StandardRun] objects based on the given
766
        /// iterator of [R] objects.
767
        ///
768
        /// @param iterator the iterator of [R] objects to be processed into
769
        /// [StandardRun] instances
770
        ///
771
        /// @return a list of [StandardRun] objects created from the given
772
        /// iterator
773
        public static List<StandardRun> wrap(Iterator<R> iterator) {
774
            var index = 0;
775
            var runList = new ArrayList<StandardRun>();
776 1 1. wrap : negated conditional → NO_COVERAGE
            while (iterator.hasNext()) {
777
                var run = iterator.next();
778
                var currentRun = new StandardRun(index, run);
779
                runList.add(currentRun);
780 1 1. wrap : Replaced integer addition with subtraction → NO_COVERAGE
                index += currentRun.length();
781
            }
782 1 1. wrap : replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::wrap → NO_COVERAGE
            return runList;
783
        }
784
785
        /// Calculates the length of the text content of this run.
786
        ///
787
        /// @return the length of the text in the current run.
788
        public int length() {
789 1 1. length : replaced int return with 0 for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::length → NO_COVERAGE
            return getText().length();
790
        }
791
792
        /// Returns the text string of a run.
793
        ///
794
        /// @return [String] representation of the run.
795
        public String getText() {
796 1 1. getText : replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::getText → NO_COVERAGE
            return asString(run);
797
        }
798
799
        /// Retrieves the properties associated with this run.
800
        ///
801
        /// @return the [RPr] object representing the properties of the run.
802
        public RPr getPr() {
803 1 1. getPr : replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::getPr → NO_COVERAGE
            return run.getRPr();
804
        }
805
806
        /// Determines whether the current run is affected by the specified
807
        /// range of global start and end indices. A run
808
        /// is considered "touched" if any part of it overlaps with the given
809
        /// range.
810
        ///
811
        /// @param globalStartIndex the global start index of the range.
812
        /// @param globalEndIndex   the global end index of the range.
813
        ///
814
        /// @return `true` if the current run is touched by the specified range;
815
        /// `false` otherwise.
816
        public boolean isTouchedByRange(
817
                int globalStartIndex,
818
                int globalEndIndex
819
        ) {
820 2 1. isTouchedByRange : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::isTouchedByRange → NO_COVERAGE
2. isTouchedByRange : negated conditional → NO_COVERAGE
            return startsInRange(globalStartIndex, globalEndIndex)
821 1 1. isTouchedByRange : negated conditional → NO_COVERAGE
                   || endsInRange(globalStartIndex, globalEndIndex)
822 1 1. isTouchedByRange : negated conditional → NO_COVERAGE
                   || englobesRange(globalStartIndex, globalEndIndex);
823
        }
824
825
        private boolean startsInRange(
826
                int globalStartIndex,
827
                int globalEndIndex
828
        ) {
829 5 1. startsInRange : negated conditional → NO_COVERAGE
2. startsInRange : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::startsInRange → NO_COVERAGE
3. startsInRange : changed conditional boundary → NO_COVERAGE
4. startsInRange : negated conditional → NO_COVERAGE
5. startsInRange : changed conditional boundary → NO_COVERAGE
            return globalStartIndex < startIndex
830
                   && startIndex <= globalEndIndex;
831
        }
832
833
        private boolean endsInRange(int globalStartIndex, int globalEndIndex) {
834 3 1. endsInRange : negated conditional → NO_COVERAGE
2. endsInRange : changed conditional boundary → NO_COVERAGE
3. endsInRange : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::endsInRange → NO_COVERAGE
            return globalStartIndex < endIndex()
835 2 1. endsInRange : changed conditional boundary → NO_COVERAGE
2. endsInRange : negated conditional → NO_COVERAGE
                   && endIndex() <= globalEndIndex;
836
        }
837
838
        private boolean englobesRange(
839
                int globalStartIndex,
840
                int globalEndIndex
841
        ) {
842 3 1. englobesRange : negated conditional → NO_COVERAGE
2. englobesRange : replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::englobesRange → NO_COVERAGE
3. englobesRange : changed conditional boundary → NO_COVERAGE
            return startIndex <= globalStartIndex
843 2 1. englobesRange : changed conditional boundary → NO_COVERAGE
2. englobesRange : negated conditional → NO_COVERAGE
                   && globalEndIndex <= endIndex();
844
        }
845
846
        /// Calculates the end index of the current run based on its start index
847
        /// and length.
848
        ///
849
        /// @return the end index of the run.
850
        public int endIndex() {
851 2 1. endIndex : Replaced integer addition with subtraction → NO_COVERAGE
2. endIndex : replaced int return with 0 for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::endIndex → NO_COVERAGE
            return startIndex + length();
852
        }
853
854
        /// Replaces the substring starting at the given index with the given
855
        /// replacement string.
856
        ///
857
        /// @param globalStartIndex the global index at which to start the
858
        /// replacement.
859
        /// @param globalEndIndex   the global index at which to end the
860
        /// replacement.
861
        /// @param replacement      the string to replace the substring at
862
        /// the specified global index.
863
        public void replace(
864
                int globalStartIndex,
865
                int globalEndIndex,
866
                String replacement
867
        ) {
868
            var text = left(globalStartIndex) + replacement + right(
869
                    globalEndIndex);
870 1 1. replace : removed call to pro/verron/officestamper/utils/wml/WmlUtils::setText → NO_COVERAGE
            setText(run, text);
871
        }
872
873
        /// Extracts a substring of the run's text, starting from the beginning
874
        /// and extending up to the localized index
875
        /// of the specified global end index.
876
        ///
877
        /// @param globalEndIndex the global end index used to determine the
878
        /// cutoff point for the extracted
879
        ///         substring.
880
        ///
881
        /// @return a substring of the run's text, starting at the beginning and
882
        /// ending at the specified localized
883
        ///         index.
884
        public String left(int globalEndIndex) {
885 1 1. left : replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::left → NO_COVERAGE
            return getText().substring(0, localize(globalEndIndex));
886
        }
887
888
        /// Extracts a substring of the run's text, starting from the localized
889
        /// index of the specified global start
890
        /// index to the end of the run's text.
891
        ///
892
        /// @param globalStartIndex the global index specifying the starting
893
        /// point for the substring in the
894
        ///         run's text.
895
        ///
896
        /// @return a substring of the run's text starting from the localized
897
        /// index corresponding to the provided global
898
        ///         start index.
899
        public String right(int globalStartIndex) {
900 1 1. right : replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::right → NO_COVERAGE
            return getText().substring(localize(globalStartIndex));
901
        }
902
903
        /// Converts a global index to a local index within the context of this
904
        /// run. (meaning the index relative to
905
        /// multiple aggregated runs)
906
        ///
907
        /// @param globalIndex the global index to convert.
908
        ///
909
        /// @return the local index corresponding to the given global index.
910
        private int localize(int globalIndex) {
911 2 1. localize : changed conditional boundary → NO_COVERAGE
2. localize : negated conditional → NO_COVERAGE
            if (globalIndex < startIndex) return 0;
912 3 1. localize : changed conditional boundary → NO_COVERAGE
2. localize : replaced int return with 0 for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::localize → NO_COVERAGE
3. localize : negated conditional → NO_COVERAGE
            else if (globalIndex > endIndex()) return length();
913 2 1. localize : replaced int return with 0 for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::localize → NO_COVERAGE
2. localize : Replaced integer subtraction with addition → NO_COVERAGE
            else return globalIndex - startIndex;
914
        }
915
916
        /// Gets the start index of this run.
917
        ///
918
        /// @return the start index of the run relative to the containing
919
        /// paragraph.
920
        @Override
921
        public int startIndex() {return startIndex;}
922
923
        /// Gets the underlying run object.
924
        ///
925
        /// @return the [R] run object.
926
        @Override
927
        public R run() {return run;}
928
    }
929
}

Mutations

73

1.1
Location : getFirstParentWithClass
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : getFirstParentWithClass
Killed by : none
negated conditional → NO_COVERAGE

74

1.1
Location : getFirstParentWithClass
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

75

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

76

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

77

1.1
Location : getFirstParentWithClass
Killed by : none
replaced return value with Optional.empty for pro/verron/officestamper/utils/wml/WmlUtils::getFirstParentWithClass → NO_COVERAGE

78

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

93

1.1
Location : extractCommentElements
Killed by : none
removed call to org/docx4j/TraversalUtil::visit → NO_COVERAGE

94

1.1
Location : extractCommentElements
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::extractCommentElements → NO_COVERAGE

114

1.1
Location : findComment
Killed by : none
replaced return value with Optional.empty for pro/verron/officestamper/utils/wml/WmlUtils::findComment → NO_COVERAGE

122

1.1
Location : getComments
Killed by : none
replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::getComments → NO_COVERAGE

129

1.1
Location : idEqual
Killed by : none
replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::idEqual → NO_COVERAGE

131

1.1
Location : lambda$idEqual$0
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::lambda$idEqual$0 → NO_COVERAGE

2.2
Location : lambda$idEqual$0
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::lambda$idEqual$0 → NO_COVERAGE

149

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

150

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

151

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

152

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

156

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

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

162

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

163

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

164

1.1
Location : remove
Killed by : none
removed call to java/util/ListIterator::remove → NO_COVERAGE

193

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

196

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

201

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

202

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

203

1.1
Location : equals
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::equals → NO_COVERAGE

2.2
Location : equals
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::equals → NO_COVERAGE

210

1.1
Location : containsAnElementOfAnyClasses
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::containsAnElementOfAnyClasses → NO_COVERAGE

2.2
Location : containsAnElementOfAnyClasses
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::containsAnElementOfAnyClasses → NO_COVERAGE

211

1.1
Location : lambda$containsAnElementOfAnyClasses$0
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::lambda$containsAnElementOfAnyClasses$0 → NO_COVERAGE

2.2
Location : lambda$containsAnElementOfAnyClasses$0
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::lambda$containsAnElementOfAnyClasses$0 → NO_COVERAGE

226

1.1
Location : isAnElementOfAnyClasses
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::isAnElementOfAnyClasses → NO_COVERAGE

2.2
Location : isAnElementOfAnyClasses
Killed by : none
negated conditional → NO_COVERAGE

228

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

232

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

2.2
Location : unwrapJAXBElement
Killed by : none
replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::unwrapJAXBElement → NO_COVERAGE

253

1.1
Location : asString
Killed by : none
replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils::asString → NO_COVERAGE

258

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

262

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

267

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

268

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

269

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

270

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

274

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

2.2
Location : asString
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : asString
Killed by : none
negated conditional → NO_COVERAGE

4.4
Location : asString
Killed by : none
negated conditional → NO_COVERAGE

283

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

2.2
Location : asString
Killed by : none
negated conditional → NO_COVERAGE

291

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

2.2
Location : asString
Killed by : none
negated conditional → NO_COVERAGE

294

1.1
Location : asString
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

309

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

2.2
Location : asString
Killed by : none
replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils::asString → NO_COVERAGE

333

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

334

1.1
Location : insertSmartTag
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::insertSmartTag → NO_COVERAGE

354

1.1
Location : lambda$findFirstAffectedRunPr$0
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::lambda$findFirstAffectedRunPr$0 → NO_COVERAGE

2.2
Location : lambda$findFirstAffectedRunPr$0
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::lambda$findFirstAffectedRunPr$0 → NO_COVERAGE

359

1.1
Location : findFirstAffectedRunPr
Killed by : none
replaced return value with Optional.empty for pro/verron/officestamper/utils/wml/WmlUtils::findFirstAffectedRunPr → NO_COVERAGE

380

1.1
Location : lambda$replace$0
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils::lambda$replace$0 → NO_COVERAGE

2.2
Location : lambda$replace$0
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::lambda$replace$0 → NO_COVERAGE

389

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

390

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

391

1.1
Location : replace
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

392

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

394

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

395

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

396

1.1
Location : replace
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : replace
Killed by : none
negated conditional → NO_COVERAGE

397

1.1
Location : replace
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : replace
Killed by : none
negated conditional → NO_COVERAGE

399

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

400

1.1
Location : replace
Killed by : none
removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE

403

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

404

1.1
Location : replace
Killed by : none
removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE

407

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

408

1.1
Location : replace
Killed by : none
removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE

409

1.1
Location : replace
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

411

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

424

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

431

1.1
Location : replace
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

433

1.1
Location : replace
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::replace → NO_COVERAGE

444

1.1
Location : create
Killed by : none
removed call to org/docx4j/wml/R::setRPr → NO_COVERAGE

445

1.1
Location : create
Killed by : none
replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::create → NO_COVERAGE

457

1.1
Location : wrap
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::wrap → NO_COVERAGE

469

1.1
Location : removeExpression
Killed by : none
removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE

472

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

2.2
Location : removeExpression
Killed by : none
negated conditional → NO_COVERAGE

478

1.1
Location : removeExpression
Killed by : none
removed call to pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::replace → NO_COVERAGE

490

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

491

1.1
Location : create
Killed by : none
replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::create → NO_COVERAGE

500

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

502

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

505

1.1
Location : applyParagraphStyle
Killed by : none
removed call to org/docx4j/wml/R::setRPr → NO_COVERAGE

514

1.1
Location : setText
Killed by : none
removed call to java/util/List::clear → NO_COVERAGE

538

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

539

1.1
Location : replaceExpressionWithRun
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::replaceExpressionWithRun → NO_COVERAGE

540

1.1
Location : replaceExpressionWithRun
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

543

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

544

1.1
Location : replaceExpressionWithRun
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils::replaceExpressionWithRun → NO_COVERAGE

560

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

2.2
Location : isTagElement
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::isTagElement → NO_COVERAGE

579

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

581

1.1
Location : setTagAttribute
Killed by : none
removed call to org/docx4j/wml/CTSmartTagRun::setSmartTagPr → NO_COVERAGE

584

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

586

1.1
Location : setTagAttribute
Killed by : none
removed call to org/docx4j/wml/CTSmartTagRun::setSmartTagPr → NO_COVERAGE

589

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

590

1.1
Location : setTagAttribute
Killed by : none
removed call to org/docx4j/wml/CTAttr::setVal → NO_COVERAGE

608

1.1
Location : newAttribute
Killed by : none
replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::newAttribute → NO_COVERAGE

626

1.1
Location : lambda$findAll$0
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::lambda$findAll$0 → NO_COVERAGE

2.2
Location : lambda$findAll$0
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::lambda$findAll$0 → NO_COVERAGE

629

1.1
Location : findAll
Killed by : none
removed call to java/util/List::forEach → NO_COVERAGE

636

1.1
Location : lambda$findAll$1
Killed by : none
negated conditional → NO_COVERAGE

638

1.1
Location : lambda$findAll$2
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : lambda$findAll$2
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::lambda$findAll$2 → NO_COVERAGE

3.3
Location : lambda$findAll$2
Killed by : none
negated conditional → NO_COVERAGE

641

1.1
Location : lambda$findAll$1
Killed by : none
negated conditional → NO_COVERAGE

643

1.1
Location : lambda$findAll$1
Killed by : none
negated conditional → NO_COVERAGE

645

1.1
Location : lambda$findAll$1
Killed by : none
negated conditional → NO_COVERAGE

652

1.1
Location : findAll
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::findAll → NO_COVERAGE

659

1.1
Location : findAll
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::findAll → NO_COVERAGE

666

1.1
Location : findAll
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::findAll → NO_COVERAGE

673

1.1
Location : from
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$1DeletableItems::from → NO_COVERAGE

678

1.1
Location : deleteCommentFromElements
Killed by : none
removed call to java/util/List::forEach → NO_COVERAGE

693

1.1
Location : visitDocument
Killed by : none
removed call to org/docx4j/TraversalUtil::visit → NO_COVERAGE

695

1.1
Location : lambda$visitDocument$0
Killed by : none
removed call to org/docx4j/TraversalUtil::visit → NO_COVERAGE

2.2
Location : visitDocument
Killed by : none
removed call to java/util/stream/Stream::forEach → NO_COVERAGE

696

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

698

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

702

1.1
Location : streamHeaderFooterPart
Killed by : none
replaced return value with Stream.empty for pro/verron/officestamper/utils/wml/WmlUtils::streamHeaderFooterPart → NO_COVERAGE

714

1.1
Location : lambda$visitPartIfExists$0
Killed by : none
removed call to org/docx4j/TraversalUtil::visit → NO_COVERAGE

2.2
Location : visitPartIfExists
Killed by : none
removed call to java/util/Optional::ifPresent → NO_COVERAGE

721

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

722

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

723

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

724

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

725

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

726

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

727

1.1
Location : extractHeaderFooterParts
Killed by : none
replaced return value with Stream.empty for pro/verron/officestamper/utils/wml/WmlUtils::extractHeaderFooterParts → NO_COVERAGE

732

1.1
Location : extractContent
Killed by : none
replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils::extractContent → NO_COVERAGE

755

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

756

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

2.2
Location : hasTagAttribute
Killed by : none
replaced boolean return with false for pro/verron/officestamper/utils/wml/WmlUtils::hasTagAttribute → NO_COVERAGE

757

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

776

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

780

1.1
Location : wrap
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

782

1.1
Location : wrap
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::wrap → NO_COVERAGE

789

1.1
Location : length
Killed by : none
replaced int return with 0 for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::length → NO_COVERAGE

796

1.1
Location : getText
Killed by : none
replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::getText → NO_COVERAGE

803

1.1
Location : getPr
Killed by : none
replaced return value with null for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::getPr → NO_COVERAGE

820

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

2.2
Location : isTouchedByRange
Killed by : none
negated conditional → NO_COVERAGE

821

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

822

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

829

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

2.2
Location : startsInRange
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::startsInRange → NO_COVERAGE

3.3
Location : startsInRange
Killed by : none
changed conditional boundary → NO_COVERAGE

4.4
Location : startsInRange
Killed by : none
negated conditional → NO_COVERAGE

5.5
Location : startsInRange
Killed by : none
changed conditional boundary → NO_COVERAGE

834

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

2.2
Location : endsInRange
Killed by : none
changed conditional boundary → NO_COVERAGE

3.3
Location : endsInRange
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::endsInRange → NO_COVERAGE

835

1.1
Location : endsInRange
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : endsInRange
Killed by : none
negated conditional → NO_COVERAGE

842

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

2.2
Location : englobesRange
Killed by : none
replaced boolean return with true for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::englobesRange → NO_COVERAGE

3.3
Location : englobesRange
Killed by : none
changed conditional boundary → NO_COVERAGE

843

1.1
Location : englobesRange
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : englobesRange
Killed by : none
negated conditional → NO_COVERAGE

851

1.1
Location : endIndex
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

2.2
Location : endIndex
Killed by : none
replaced int return with 0 for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::endIndex → NO_COVERAGE

870

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

885

1.1
Location : left
Killed by : none
replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::left → NO_COVERAGE

900

1.1
Location : right
Killed by : none
replaced return value with "" for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::right → NO_COVERAGE

911

1.1
Location : localize
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : localize
Killed by : none
negated conditional → NO_COVERAGE

912

1.1
Location : localize
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : localize
Killed by : none
replaced int return with 0 for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::localize → NO_COVERAGE

3.3
Location : localize
Killed by : none
negated conditional → NO_COVERAGE

913

1.1
Location : localize
Killed by : none
replaced int return with 0 for pro/verron/officestamper/utils/wml/WmlUtils$StandardRun::localize → NO_COVERAGE

2.2
Location : localize
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.25.5 support