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

Mutations

45

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

48

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

60

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

71

1.1
Location : from
Killed by : pro.verron.officestamper.test.GoogleDocsSupportTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.GoogleDocsSupportTest]/[method:conditionalRepeatedParagraphs_createdByGoogleDocs()]
replaced return value with null for pro/verron/officestamper/core/StandardParagraph::from → KILLED

84

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

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

90

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

96

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

103

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

108

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

110

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

119

1.1
Location : replace
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

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

123

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 → SURVIVED
Covering tests

127

1.1
Location : replace
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

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

132

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

133

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

141

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

142

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

143

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

153

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:#2]
replaced return value with "" for pro/verron/officestamper/core/StandardParagraph::asString → KILLED

163

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:#2]
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:#2]
replaced return value with Collections.emptyList for pro/verron/officestamper/core/StandardParagraph::lambda$apply$0 → KILLED

175

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

183

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

188

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

193

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

203

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.25.5 support