StandardParagraph.java

1
package pro.verron.officestamper.core;
2
3
import org.docx4j.wml.*;
4
import org.jvnet.jaxb2_commons.ppp.Child;
5
import pro.verron.officestamper.api.*;
6
import pro.verron.officestamper.utils.wml.DocxIterator;
7
import pro.verron.officestamper.utils.wml.WmlUtils;
8
9
import java.util.Collection;
10
import java.util.List;
11
import java.util.Optional;
12
import java.util.function.Consumer;
13
14
import static java.util.stream.Collectors.joining;
15
import static pro.verron.officestamper.api.OfficeStamperException.throwing;
16
import static pro.verron.officestamper.utils.wml.WmlUtils.getFirstParentWithClass;
17
import static pro.verron.officestamper.utils.wml.WmlUtils.isTagElement;
18
19
/// Represents a wrapper for managing and manipulating DOCX paragraph elements. This class provides methods to
20
/// manipulate the underlying paragraph content, process placeholders, and interact with runs within the paragraph.
21
public class StandardParagraph
22
        implements Paragraph {
23
24
    private final DocxPart part;
25
    private final ContentAccessor contents;
26
    private final ArrayListWml<Object> p;
27
28
    /// Constructs a new instance of the StandardParagraph class.
29
    ///
30
    /// @param part the source DocxPart that contains the paragraph content.
31
    /// @param paragraphContent the list of objects representing the paragraph content.
32
    /// @param p the P object representing the paragraph's structure.
33
    private StandardParagraph(DocxPart part, ContentAccessor paragraphContent, ArrayListWml<Object> p) {
34
        this.part = part;
35
        this.contents = paragraphContent;
36
        this.p = p;
37
    }
38
39
    /// Creates a new instance of `StandardParagraph` from the provided [DocxPart] and parent object.
40
    ///
41
    /// @param part the source DocxPart.
42
    /// @param parent the parent object.
43
    ///
44
    /// @return a new StandardParagraph instance.
45
    public static StandardParagraph from(DocxPart part, Object parent) {
46 1 1. from : replaced return value with null for pro/verron/officestamper/core/StandardParagraph::from → TIMED_OUT
        return switch (parent) {
47
            case P p -> from(part, p);
48
            case CTSdtContentRun contentRun -> from(part, contentRun);
49 1 1. from : negated conditional → NO_COVERAGE
            case CTSmartTagRun smartTagRun when isTagElement(smartTagRun, "officestamper") ->
50
                    from(part, smartTagRun.getParent());
51
            default -> throw new OfficeStamperException("Unsupported parent type: " + parent.getClass());
52
        };
53
    }
54
55
    /// Creates a new instance of StandardParagraph using the provided DocxPart and P objects.
56
    ///
57
    /// @param source the source DocxPart containing the paragraph.
58
    /// @param paragraph the P object representing the structure and content of the paragraph.
59
    ///
60
    /// @return a new instance of StandardParagraph constructed based on the provided source and paragraph.
61
    public static StandardParagraph from(DocxPart source, P paragraph) {
62 1 1. from : replaced return value with null for pro/verron/officestamper/core/StandardParagraph::from → KILLED
        return new StandardParagraph(source, paragraph, (ArrayListWml<Object>) paragraph.getContent());
63
    }
64
65
    /// Creates a new instance of StandardParagraph from the provided DocxPart and CTSdtContentRun objects.
66
    ///
67
    /// @param source the source DocxPart containing the paragraph content.
68
    /// @param paragraph the CTSdtContentRun object representing the content of the paragraph.
69
    ///
70
    /// @return a new instance of StandardParagraph constructed based on the provided DocxPart and paragraph.
71
    public static StandardParagraph from(DocxPart source, CTSdtContentRun paragraph) {
72
        var parent = (SdtRun) paragraph.getParent();
73
        var parentParent = (P) parent.getParent();
74 1 1. from : replaced return value with null for pro/verron/officestamper/core/StandardParagraph::from → TIMED_OUT
        return new StandardParagraph(source, paragraph, (ArrayListWml<Object>) parentParent.getContent());
75
    }
76
77
    /// Replaces a set of paragraph elements with new ones within the current paragraph's siblings. Ensures that the
78
    /// elements to be removed are replaced in the appropriate position.
79
    ///
80
    /// @param toRemove the list of paragraph elements to be removed.
81
    /// @param toAdd the list of paragraph elements to be added.
82
    ///
83
    /// @throws OfficeStamperException if the current paragraph object is not found in its siblings.
84
    @Override
85
    public void replace(List<P> toRemove, List<P> toAdd) {
86
        var siblings = siblings();
87
        int index = siblings.indexOf(p.getParent());
88 2 1. replace : negated conditional → NO_COVERAGE
2. replace : changed conditional boundary → NO_COVERAGE
        if (index < 0) throw new OfficeStamperException("Impossible");
89
        siblings.addAll(index, toAdd);
90
        siblings.removeAll(toRemove);
91
    }
92
93
    private List<Object> siblings() {
94 1 1. siblings : replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::siblings → NO_COVERAGE
        return this.parent(ContentAccessor.class, 1)
95
                   .orElseThrow(throwing("This paragraph direct parent is not a classic parent object"))
96
                   .getContent();
97
    }
98
99
    private <T> Optional<T> parent(Class<T> aClass, int depth) {
100 1 1. parent : replaced return value with Optional.empty for pro/verron/officestamper/core/StandardParagraph::parent → KILLED
        return getFirstParentWithClass((Child) p.getParent(), aClass, depth);
101
    }
102
103
    /// Removes the paragraph represented by the current instance. Delegates the removal process to a utility method
104
    /// that handles the underlying P object.
105
    @Override
106
    public void remove() {
107 1 1. remove : removed call to pro/verron/officestamper/utils/wml/WmlUtils::remove → KILLED
        WmlUtils.remove((Child) p.getParent());
108
    }
109
110
    @Override
111
    public void replace(String expression, Insert insert) {
112 1 1. lambda$replace$0 : replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$replace$0 → NO_COVERAGE
        var newContents = WmlUtils.replaceExpressionWithRun(() -> p, expression, insert.elements(), insert::setRPr);
113
        var content = contents.getContent();
114 1 1. replace : removed call to java/util/List::clear → NO_COVERAGE
        content.clear();
115
        content.addAll(newContents);
116
    }
117
118
    @Override
119
    public void replace(Object start, Object end, Insert insert) {
120
        var content = contents.getContent();
121
        var fromIndex = content.indexOf(start);
122
        var toIndex = content.indexOf(end);
123 2 1. replace : changed conditional boundary → TIMED_OUT
2. replace : negated conditional → KILLED
        if (fromIndex < 0) {
124
            var msg = "The start element (%s) is not in the paragraph (%s)";
125
            throw new OfficeStamperException(msg.formatted(start, this));
126
        }
127 2 1. replace : changed conditional boundary → TIMED_OUT
2. replace : negated conditional → KILLED
        if (toIndex < 0) {
128
            var msg = "The end element (%s) is not in the paragraph (%s)";
129
            throw new OfficeStamperException(msg.formatted(end, this));
130
        }
131 2 1. replace : changed conditional boundary → TIMED_OUT
2. replace : negated conditional → KILLED
        if (fromIndex > toIndex) {
132
            var msg = "The start element (%s) is after the end element (%s)";
133
            throw new OfficeStamperException(msg.formatted(end, this));
134
        }
135
        var expression = extractExpression(start, end);
136 1 1. lambda$replace$1 : replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$replace$1 → KILLED
        var newContents = WmlUtils.replaceExpressionWithRun(() -> p, expression, insert.elements(), insert::setRPr);
137 1 1. replace : removed call to java/util/List::clear → KILLED
        content.clear();
138
        content.addAll(newContents);
139
    }
140
141
    private String extractExpression(Object from, Object to) {
142
        var content = contents.getContent();
143
        var fromIndex = content.indexOf(from);
144
        var toIndex = content.indexOf(to);
145 1 1. extractExpression : Replaced integer addition with subtraction → KILLED
        var subContent = content.subList(fromIndex, toIndex + 1);
146 1 1. lambda$extractExpression$0 : replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$extractExpression$0 → KILLED
        ContentAccessor contentAccessor = () -> subContent;
147 1 1. extractExpression : replaced return value with "" for pro/verron/officestamper/core/StandardParagraph::extractExpression → KILLED
        return new DocxIterator(contentAccessor).selectClass(R.class)
148
                                                .map(WmlUtils::asString)
149
                                                .collect(joining());
150
    }
151
152
    /// Returns the aggregated text over all runs.
153
    ///
154
    /// @return the text of all runs.
155
    @Override
156
    public String asString() {
157 1 1. asString : replaced return value with "" for pro/verron/officestamper/core/StandardParagraph::asString → KILLED
        return WmlUtils.asString(contents);
158
    }
159
160
    /// Applies the given consumer to the paragraph represented by the current instance. This method facilitates custom
161
    /// processing by allowing the client to define specific operations to be performed on the paragraph's internal
162
    /// structure.
163
    ///
164
    /// @param pConsumer the consumer function to apply to the paragraph's structure.
165
    @Override
166
    public void apply(Consumer<ContentAccessor> pConsumer) {
167 2 1. apply : removed call to java/util/function/Consumer::accept → KILLED
2. lambda$apply$0 : replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$apply$0 → KILLED
        pConsumer.accept(() -> p);
168
    }
169
170
    /// Retrieves the nearest parent of the specified type for the current paragraph. The search is performed starting
171
    /// from the current paragraph and traversing up to the root, with a default maximum depth of Integer.MAX_VALUE.
172
    ///
173
    /// @param aClass the class type of the parent to search for
174
    /// @param <T> the generic type of the parent
175
    ///
176
    /// @return an Optional containing the parent of the specified type if found, or an empty Optional if no parent of
177
    ///         the given type exists
178
    @Override
179
    public <T> Optional<T> parent(Class<T> aClass) {
180 1 1. parent : replaced return value with Optional.empty for pro/verron/officestamper/core/StandardParagraph::parent → KILLED
        return parent(aClass, Integer.MAX_VALUE);
181
    }
182
183
    /// Retrieves the collection of comments associated with the current paragraph.
184
    ///
185
    /// @return a collection of [Comments.Comment] objects related to the paragraph.
186
    @Override
187
    public Collection<Comments.Comment> getComment() {
188 2 1. lambda$getComment$0 : replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$getComment$0 → NO_COVERAGE
2. getComment : replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::getComment → NO_COVERAGE
        return CommentUtil.getCommentFor(() -> p, part.document());
189
    }
190
191
    @Override
192
    public Optional<Table.Row> parentTableRow() {
193 2 1. lambda$parentTableRow$0 : replaced return value with null for pro/verron/officestamper/core/StandardParagraph::lambda$parentTableRow$0 → KILLED
2. parentTableRow : replaced return value with Optional.empty for pro/verron/officestamper/core/StandardParagraph::parentTableRow → KILLED
        return parent(Tr.class).map((Tr tr) -> new StandardRow(part, (Tbl) tr.getParent(), tr));
194
    }
195
196
    @Override
197
    public Optional<Table> parentTable() {
198 1 1. parentTable : replaced return value with Optional.empty for pro/verron/officestamper/core/StandardParagraph::parentTable → KILLED
        return parent(Tbl.class).map(StandardTable::new);
199
    }
200
201
    /// Returns the string representation of the paragraph. This method delegates to the `asString` method to aggregate
202
    /// the text content of all runs.
203
    ///
204
    /// @return a string containing the combined text content of the paragraph's runs.
205
    @Override
206
    public String toString() {
207 1 1. toString : replaced return value with "" for pro/verron/officestamper/core/StandardParagraph::toString → NO_COVERAGE
        return asString();
208
    }
209
}

Mutations

46

1.1
Location : from
Killed by : none
replaced return value with null for pro/verron/officestamper/core/StandardParagraph::from → TIMED_OUT

49

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

62

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

74

1.1
Location : from
Killed by : none
replaced return value with null for pro/verron/officestamper/core/StandardParagraph::from → TIMED_OUT

88

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

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

94

1.1
Location : siblings
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::siblings → NO_COVERAGE

100

1.1
Location : parent
Killed by : pro.verron.officestamper.test.ProcessorDisplayIfTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorDisplayIfTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#2]
replaced return value with Optional.empty for pro/verron/officestamper/core/StandardParagraph::parent → KILLED

107

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

112

1.1
Location : lambda$replace$0
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$replace$0 → NO_COVERAGE

114

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

123

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

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

127

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

2.2
Location : replace
Killed by : none
changed conditional boundary → TIMED_OUT

131

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

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

136

1.1
Location : lambda$replace$1
Killed by : pro.verron.officestamper.test.ProcessorReplaceWithTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorReplaceWithTest]/[method:notWorking1()]
replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$replace$1 → KILLED

137

1.1
Location : replace
Killed by : pro.verron.officestamper.test.ProcessorReplaceWithTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorReplaceWithTest]/[method:notWorking1()]
removed call to java/util/List::clear → KILLED

145

1.1
Location : extractExpression
Killed by : pro.verron.officestamper.test.ProcessorReplaceWithTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorReplaceWithTest]/[method:notWorking1()]
Replaced integer addition with subtraction → KILLED

146

1.1
Location : lambda$extractExpression$0
Killed by : pro.verron.officestamper.test.ProcessorReplaceWithTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorReplaceWithTest]/[method:notWorking1()]
replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$extractExpression$0 → KILLED

147

1.1
Location : extractExpression
Killed by : pro.verron.officestamper.test.ProcessorReplaceWithTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorReplaceWithTest]/[method:notWorking1()]
replaced return value with "" for pro/verron/officestamper/core/StandardParagraph::extractExpression → KILLED

157

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

167

1.1
Location : apply
Killed by : pro.verron.officestamper.test.CustomProcessorTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.CustomProcessorTests]/[test-template:should_allow_custom_processors_injection(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
removed call to java/util/function/Consumer::accept → KILLED

2.2
Location : lambda$apply$0
Killed by : pro.verron.officestamper.test.CustomProcessorTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.CustomProcessorTests]/[test-template:should_allow_custom_processors_injection(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#1]
replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$apply$0 → KILLED

180

1.1
Location : parent
Killed by : pro.verron.officestamper.test.ProcessorDisplayIfTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorDisplayIfTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#2]
replaced return value with Optional.empty for pro/verron/officestamper/core/StandardParagraph::parent → KILLED

188

1.1
Location : lambda$getComment$0
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$getComment$0 → NO_COVERAGE

2.2
Location : getComment
Killed by : none
replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::getComment → NO_COVERAGE

193

1.1
Location : lambda$parentTableRow$0
Killed by : pro.verron.officestamper.test.ProcessorDisplayIfTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorDisplayIfTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#2]
replaced return value with null for pro/verron/officestamper/core/StandardParagraph::lambda$parentTableRow$0 → KILLED

2.2
Location : parentTableRow
Killed by : pro.verron.officestamper.test.ProcessorDisplayIfTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorDisplayIfTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#2]
replaced return value with Optional.empty for pro/verron/officestamper/core/StandardParagraph::parentTableRow → KILLED

198

1.1
Location : parentTable
Killed by : pro.verron.officestamper.test.ProcessorDisplayIfTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorDisplayIfTest]/[test-template:conditionalDisplayOfTableBug32Test(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#2]
replaced return value with Optional.empty for pro/verron/officestamper/core/StandardParagraph::parentTable → KILLED

207

1.1
Location : toString
Killed by : none
replaced return value with "" for pro/verron/officestamper/core/StandardParagraph::toString → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.22.0