DocxIterator.java

1
package pro.verron.officestamper.utils.wml;
2
3
import org.docx4j.wml.ContentAccessor;
4
import org.docx4j.wml.Pict;
5
import org.docx4j.wml.SdtBlock;
6
import org.docx4j.wml.SdtRun;
7
import org.jspecify.annotations.Nullable;
8
import pro.verron.officestamper.utils.iterator.ResetableIterator;
9
10
import java.util.*;
11
import java.util.function.Supplier;
12
13
import static org.docx4j.XmlUtils.unwrap;
14
15
/// An iterator that allows the traversal of objects within a WordprocessingML-based document part. The iterator
16
/// supports nested structures, enabling iteration over content that may have hierarchical data, like paragraphs,
17
/// structured document tags (SDTs), and runs.
18
///
19
/// This class implements the [ResetableIterator] interface, allowing for the iteration to be reset to its initial
20
/// state, ensuring reusability of the same iterator instance.
21
public class DocxIterator
22
        implements ResetableIterator<Object> {
23
24
    private final Supplier<Iterator<Object>> supplier;
25
    private Queue<Iterator<?>> iteratorQueue;
26
    private @Nullable Object next;
27
28
    /// Creates a new [DocxIterator] instance that iterates over the content of the given [ContentAccessor].
29
    ///
30
    /// @param contentAccessor the content accessor whose content will be iterated over
31
    public DocxIterator(ContentAccessor contentAccessor) {
32
        this(contentAccessor.getContent()::iterator);
33
    }
34
35
    private DocxIterator(Supplier<Iterator<Object>> supplier) {
36
        this.supplier = supplier;
37 1 1. <init> : removed call to pro/verron/officestamper/utils/wml/DocxIterator::initialize → KILLED
        initialize();
38
    }
39
40
    private void initialize() {
41
        var startingIterator = supplier.get();
42
        this.iteratorQueue = Collections.asLifoQueue(new ArrayDeque<>());
43
        this.iteratorQueue.add(startingIterator);
44 1 1. initialize : negated conditional → KILLED
        this.next = startingIterator.hasNext() ? unwrap(startingIterator.next()) : null;
45
    }
46
47
    /// Selects and casts elements of the specified class type from the iterator.
48
    ///
49
    /// @param aClass the class type to filter and cast elements to
50
    /// @param <T> the type of elements to select
51
    ///
52
    /// @return a new [ResetableIterator] containing only elements of the specified class type
53
    public <T> ResetableIterator<T> selectClass(Class<T> aClass) {
54 1 1. selectClass : replaced return value with null for pro/verron/officestamper/utils/wml/DocxIterator::selectClass → KILLED
        return filter(aClass::isInstance).map(aClass::cast);
55
    }
56
57
    @Override
58
    public void reset() {
59 1 1. reset : removed call to pro/verron/officestamper/utils/wml/DocxIterator::initialize → KILLED
        initialize();
60
    }
61
62
    @Override
63
    public boolean hasNext() {
64 2 1. hasNext : replaced boolean return with true for pro/verron/officestamper/utils/wml/DocxIterator::hasNext → KILLED
2. hasNext : negated conditional → KILLED
        return next != null;
65
    }
66
67
    @Override
68
    public Object next() {
69 1 1. next : negated conditional → KILLED
        if (next == null) throw new NoSuchElementException("No more elements to iterate");
70
71
        var result = next;
72
73
        next = null;
74
        switch (result) {
75
            case ContentAccessor contentAccessor -> {
76
                var content = contentAccessor.getContent();
77
                iteratorQueue.add(content.iterator());
78
            }
79
            case SdtRun sdtRun -> {
80
                var sdtContent = sdtRun.getSdtContent();
81
                var content = sdtContent.getContent();
82
                iteratorQueue.add(content.iterator());
83
            }
84
            case SdtBlock sdtBlock -> {
85
                var sdtContent = sdtBlock.getSdtContent();
86
                var content = sdtContent.getContent();
87
                iteratorQueue.add(content.iterator());
88
            }
89
            case Pict pict -> {
90
                var content = pict.getAnyAndAny();
91
                iteratorQueue.add(content.iterator());
92
            }
93
            default -> { /* DO NOTHING */ }
94
        }
95 2 1. next : negated conditional → KILLED
2. next : negated conditional → KILLED
        while (!iteratorQueue.isEmpty() && next == null) {
96
            var nextIterator = iteratorQueue.poll();
97 1 1. next : negated conditional → KILLED
            if (nextIterator.hasNext()) {
98
                next = unwrap(nextIterator.next());
99
                iteratorQueue.add(nextIterator);
100
            }
101
        }
102 1 1. next : replaced return value with null for pro/verron/officestamper/utils/wml/DocxIterator::next → KILLED
        return result;
103
    }
104
}

Mutations

37

1.1
Location : <init>
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testHasNextReturnsTrueWhenThereIsElement()]
removed call to pro/verron/officestamper/utils/wml/DocxIterator::initialize → KILLED

44

1.1
Location : initialize
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testHasNextReturnsTrueWhenThereIsElement()]
negated conditional → KILLED

54

1.1
Location : selectClass
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testSelectClassFiltersElementsByType()]
replaced return value with null for pro/verron/officestamper/utils/wml/DocxIterator::selectClass → KILLED

59

1.1
Location : reset
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testResetResetsToInitialState()]
removed call to pro/verron/officestamper/utils/wml/DocxIterator::initialize → KILLED

64

1.1
Location : hasNext
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testHasNextReturnsFalseWhenNoElements()]
replaced boolean return with true for pro/verron/officestamper/utils/wml/DocxIterator::hasNext → KILLED

2.2
Location : hasNext
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testHasNextReturnsTrueWhenThereIsElement()]
negated conditional → KILLED

69

1.1
Location : next
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testNextThrowsExceptionWhenNoMoreElements()]
negated conditional → KILLED

95

1.1
Location : next
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testNextHandlesNestedContentAccessor()]
negated conditional → KILLED

2.2
Location : next
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testNextHandlesNestedContentAccessor()]
negated conditional → KILLED

97

1.1
Location : next
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testNextHandlesNestedContentAccessor()]
negated conditional → KILLED

102

1.1
Location : next
Killed by : pro.verron.officestamper.utils.wml.DocxIteratorTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.utils.wml.DocxIteratorTest]/[method:testNextHandlesNestedContentAccessor()]
replaced return value with null for pro/verron/officestamper/utils/wml/DocxIterator::next → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.0