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

Mutations

44

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

47

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

59

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

70

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

83

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

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

89

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

95

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

102

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

107

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

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

115

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

119

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

123

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

128

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 pro/verron/officestamper/utils/wml/WmlUtils::replaceExpressionWithRun → KILLED

2.2
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

139

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

140

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

141

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

149

1.1
Location : asString
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 "" for pro/verron/officestamper/core/StandardParagraph::asString → KILLED

159

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

171

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

179

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

184

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

189

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

199

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