DocxStamper.java

1
package pro.verron.officestamper.core;
2
3
import org.docx4j.openpackaging.exceptions.Docx4JException;
4
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
5
import org.docx4j.openpackaging.parts.relationships.Namespaces;
6
import org.springframework.expression.spel.SpelParserConfiguration;
7
import org.springframework.expression.spel.standard.SpelExpressionParser;
8
import org.springframework.expression.spel.support.StandardEvaluationContext;
9
import org.springframework.lang.NonNull;
10
import pro.verron.officestamper.api.*;
11
12
import java.io.InputStream;
13
import java.io.OutputStream;
14
import java.util.ArrayList;
15
import java.util.HashMap;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.function.Function;
19
20
import static pro.verron.officestamper.core.Invokers.streamInvokers;
21
22
/// The DocxStamper class is an implementation of the [OfficeStamper]
23
/// interface that is used to stamp DOCX templates with a context object and
24
/// write the result to an output stream.
25
///
26
/// @author Tom Hombergs
27
/// @author Joseph Verron
28
/// @version ${version}
29
/// @since 1.0.0
30
public class DocxStamper
31
        implements OfficeStamper<WordprocessingMLPackage> {
32
33
    private final List<PreProcessor> preprocessors;
34
    private final List<PostProcessor> postprocessors;
35
    private final PlaceholderReplacer placeholderReplacer;
36
    private final Function<DocxPart, CommentProcessorRegistry> commentProcessorRegistrySupplier;
37
38
    /// Creates a new DocxStamper with the given configuration.
39
    ///
40
    /// @param configuration the configuration to use for this DocxStamper.
41
    public DocxStamper(OfficeStamperConfiguration configuration) {
42
        this(
43
                configuration.getLineBreakPlaceholder(),
44
                configuration.getEvaluationContextConfigurer(),
45
                configuration.getExpressionFunctions(),
46
                configuration.customFunctions(),
47
                configuration.getResolvers(),
48
                configuration.getCommentProcessors(),
49
                configuration.getPreprocessors(),
50
                configuration.getPostprocessors(),
51
                configuration.getSpelParserConfiguration(),
52
                configuration.getExceptionResolver()
53
        );
54
    }
55
56
    private DocxStamper(
57
            @NonNull String lineBreakPlaceholder,
58
            EvaluationContextConfigurer evaluationContextConfigurer,
59
            Map<Class<?>, Object> expressionFunctions,
60
            List<CustomFunction> functions,
61
            List<ObjectResolver> resolvers,
62
            Map<Class<?>, Function<ParagraphPlaceholderReplacer, CommentProcessor>> configurationCommentProcessors,
63
            List<PreProcessor> preprocessors,
64
            List<PostProcessor> postprocessors,
65
            SpelParserConfiguration spelParserConfiguration,
66
            ExceptionResolver exceptionResolver
67
    ) {
68
        var expressionParser = new SpelExpressionParser(spelParserConfiguration);
69
70
        var evaluationContext = new StandardEvaluationContext();
71 1 1. <init> : removed call to pro/verron/officestamper/api/EvaluationContextConfigurer::configureEvaluationContext → RUN_ERROR
        evaluationContextConfigurer.configureEvaluationContext(evaluationContext);
72
73
        var expressionResolver = new ExpressionResolver(evaluationContext, expressionParser);
74
        var typeResolverRegistry = new ObjectResolverRegistry(resolvers);
75
        this.placeholderReplacer = new PlaceholderReplacer(
76
                typeResolverRegistry,
77
                expressionResolver,
78
                Placeholders.raw(lineBreakPlaceholder),
79
                exceptionResolver);
80
81
        var commentProcessors = buildCommentProcessors(configurationCommentProcessors);
82 1 1. <init> : removed call to org/springframework/expression/spel/support/StandardEvaluationContext::addMethodResolver → KILLED
        evaluationContext.addMethodResolver(new Invokers(streamInvokers(commentProcessors)));
83 1 1. <init> : removed call to org/springframework/expression/spel/support/StandardEvaluationContext::addMethodResolver → KILLED
        evaluationContext.addMethodResolver(new Invokers(streamInvokers(expressionFunctions)));
84 1 1. <init> : removed call to org/springframework/expression/spel/support/StandardEvaluationContext::addMethodResolver → KILLED
        evaluationContext.addMethodResolver(new Invokers(functions.stream()
85
                                                                  .map(Invokers::ofCustomFunction)));
86
87 1 1. lambda$new$0 : replaced return value with null for pro/verron/officestamper/core/DocxStamper::lambda$new$0 → KILLED
        this.commentProcessorRegistrySupplier = source -> new CommentProcessorRegistry(
88
                source,
89
                expressionResolver,
90
                commentProcessors,
91
                exceptionResolver);
92
93
        this.preprocessors = new ArrayList<>(preprocessors);
94
        this.postprocessors = new ArrayList<>(postprocessors);
95
    }
96
97
    private CommentProcessors buildCommentProcessors(
98
            Map<Class<?>, Function<ParagraphPlaceholderReplacer, CommentProcessor>> commentProcessors
99
    ) {
100
        var processors = new HashMap<Class<?>, CommentProcessor>();
101
        for (var entry : commentProcessors.entrySet()) {
102
            processors.put(
103
                    entry.getKey(),
104
                    entry.getValue()
105
                         .apply(placeholderReplacer));
106
        }
107 1 1. buildCommentProcessors : replaced return value with null for pro/verron/officestamper/core/DocxStamper::buildCommentProcessors → KILLED
        return new CommentProcessors(processors);
108
    }
109
110
    /// Reads in a .docx template and "stamps" it into the given OutputStream, using the specified context object to
111
    /// fill out any expressions it finds.
112
    ///
113
    /// In the .docx template you have the following options to influence the "stamping" process:
114
    ///   - Use expressions like ${name} or ${person.isOlderThan(18)} in the template's text. These expressions are
115
    ///     resolved
116
    ///     against the contextRoot object you pass into this method and are replaced by the results.
117
    ///   - Use comments within the .docx template to mark certain paragraphs to be manipulated.
118
    ///
119
    /// Within comments, you can put expressions in which you can use the following methods by default:
120
    ///   - _displayParagraphIf(boolean)_ to conditionally display paragraphs or not
121
    ///   - _displayTableRowIf(boolean)_ to conditionally display table rows or not
122
    ///   - _displayTableIf(boolean)_ to conditionally display whole tables or not
123
    ///   - _repeatTableRow(List&lt;Object&gt;)_ to create a new table row for each object in the list and
124
    ///     resolve expressions
125
    ///     within the table cells against one of the objects within the list.
126
    ///
127
    /// If you need a wider vocabulary of methods available in the comments, you can create your own ICommentProcessor
128
    /// and register it via [OfficeStamperConfiguration#addCommentProcessor(Class, Function)].
129
    public void stamp(InputStream template, Object contextRoot, OutputStream out) {
130
        try {
131
            WordprocessingMLPackage document = WordprocessingMLPackage.load(template);
132 1 1. stamp : removed call to pro/verron/officestamper/core/DocxStamper::stamp → NO_COVERAGE
            stamp(document, contextRoot, out);
133
        } catch (Docx4JException e) {
134
            throw new OfficeStamperException(e);
135
        }
136
    }
137
138
139
    /// Same as [#stamp(InputStream, Object, OutputStream)] except that you
140
    /// may pass in a DOCX4J document as a template instead of an InputStream.
141
    @Override
142
    public void stamp(WordprocessingMLPackage document, Object contextRoot, OutputStream out) {
143
        try {
144
            var source = new TextualDocxPart(document);
145 1 1. stamp : removed call to pro/verron/officestamper/core/DocxStamper::preprocess → KILLED
            preprocess(document);
146 1 1. stamp : removed call to pro/verron/officestamper/core/DocxStamper::processComments → KILLED
            processComments(source, contextRoot);
147 1 1. stamp : removed call to pro/verron/officestamper/core/DocxStamper::replaceExpressions → KILLED
            replaceExpressions(source, contextRoot);
148 1 1. stamp : removed call to pro/verron/officestamper/core/DocxStamper::postprocess → KILLED
            postprocess(document);
149 1 1. stamp : removed call to org/docx4j/openpackaging/packages/WordprocessingMLPackage::save → KILLED
            document.save(out);
150
        } catch (Docx4JException e) {
151
            throw new OfficeStamperException(e);
152
        }
153
    }
154
155
    private void preprocess(WordprocessingMLPackage document) {
156 2 1. preprocess : removed call to java/util/List::forEach → KILLED
2. lambda$preprocess$1 : removed call to pro/verron/officestamper/api/PreProcessor::process → KILLED
        preprocessors.forEach(processor -> processor.process(document));
157
    }
158
159
    private void processComments(DocxPart document, Object contextObject) {
160
        document.streamParts(Namespaces.HEADER)
161 2 1. lambda$processComments$2 : removed call to pro/verron/officestamper/core/DocxStamper::runProcessors → SURVIVED
2. processComments : removed call to java/util/stream/Stream::forEach → SURVIVED
                .forEach(header -> runProcessors(header, contextObject));
162 1 1. processComments : removed call to pro/verron/officestamper/core/DocxStamper::runProcessors → KILLED
        runProcessors(document, contextObject);
163
        document.streamParts(Namespaces.FOOTER)
164 2 1. lambda$processComments$3 : removed call to pro/verron/officestamper/core/DocxStamper::runProcessors → SURVIVED
2. processComments : removed call to java/util/stream/Stream::forEach → SURVIVED
                .forEach(footer -> runProcessors(footer, contextObject));
165
    }
166
167
    private void replaceExpressions(DocxPart document, Object contextObject) {
168
        document.streamParts(Namespaces.HEADER)
169 2 1. replaceExpressions : removed call to java/util/stream/Stream::forEach → KILLED
2. lambda$replaceExpressions$4 : removed call to pro/verron/officestamper/core/PlaceholderReplacer::resolveExpressions → KILLED
                .forEach(s -> placeholderReplacer.resolveExpressions(s, contextObject));
170 1 1. replaceExpressions : removed call to pro/verron/officestamper/core/PlaceholderReplacer::resolveExpressions → KILLED
        placeholderReplacer.resolveExpressions(document, contextObject);
171
        document.streamParts(Namespaces.FOOTER)
172 2 1. replaceExpressions : removed call to java/util/stream/Stream::forEach → KILLED
2. lambda$replaceExpressions$5 : removed call to pro/verron/officestamper/core/PlaceholderReplacer::resolveExpressions → KILLED
                .forEach(s -> placeholderReplacer.resolveExpressions(s, contextObject));
173
    }
174
175
    private void runProcessors(DocxPart source, Object contextObject) {
176
        var processors = commentProcessorRegistrySupplier.apply(source);
177 1 1. runProcessors : removed call to pro/verron/officestamper/core/CommentProcessorRegistry::runProcessors → KILLED
        processors.runProcessors(contextObject);
178
    }
179
180
    private void postprocess(WordprocessingMLPackage document) {
181 2 1. lambda$postprocess$6 : removed call to pro/verron/officestamper/api/PostProcessor::process → KILLED
2. postprocess : removed call to java/util/List::forEach → KILLED
        postprocessors.forEach(processor -> processor.process(document));
182
    }
183
}

Mutations

71

1.1
Location : <init>
Killed by : none
removed call to pro/verron/officestamper/api/EvaluationContextConfigurer::configureEvaluationContext → RUN_ERROR

82

1.1
Location : <init>
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#35]
removed call to org/springframework/expression/spel/support/StandardEvaluationContext::addMethodResolver → KILLED

83

1.1
Location : <init>
Killed by : pro.verron.officestamper.test.RegressionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.RegressionTests]/[method:test64()]
removed call to org/springframework/expression/spel/support/StandardEvaluationContext::addMethodResolver → KILLED

84

1.1
Location : <init>
Killed by : pro.verron.officestamper.test.CustomFunctionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.CustomFunctionTests]/[test-template:trifunctions(pro.verron.officestamper.test.ContextFactory, java.lang.String, java.lang.String)]/[test-template-invocation:#8]
removed call to org/springframework/expression/spel/support/StandardEvaluationContext::addMethodResolver → KILLED

87

1.1
Location : lambda$new$0
Killed by : pro.verron.officestamper.test.ResolutionTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ResolutionTest]/[test-template:testCustomResolution(java.lang.String, boolean, boolean, boolean, boolean, boolean, java.lang.String, boolean, java.lang.String)]/[test-template-invocation:#29]
replaced return value with null for pro/verron/officestamper/core/DocxStamper::lambda$new$0 → KILLED

107

1.1
Location : buildCommentProcessors
Killed by : pro.verron.officestamper.test.ResolutionTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ResolutionTest]/[test-template:testCustomResolution(java.lang.String, boolean, boolean, boolean, boolean, boolean, java.lang.String, boolean, java.lang.String)]/[test-template-invocation:#29]
replaced return value with null for pro/verron/officestamper/core/DocxStamper::buildCommentProcessors → KILLED

132

1.1
Location : stamp
Killed by : none
removed call to pro/verron/officestamper/core/DocxStamper::stamp → NO_COVERAGE

145

1.1
Location : stamp
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#2]
removed call to pro/verron/officestamper/core/DocxStamper::preprocess → KILLED

146

1.1
Location : stamp
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to pro/verron/officestamper/core/DocxStamper::processComments → KILLED

147

1.1
Location : stamp
Killed by : pro.verron.officestamper.test.ResolutionTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ResolutionTest]/[test-template:testCustomResolution(java.lang.String, boolean, boolean, boolean, boolean, boolean, java.lang.String, boolean, java.lang.String)]/[test-template-invocation:#29]
removed call to pro/verron/officestamper/core/DocxStamper::replaceExpressions → KILLED

148

1.1
Location : stamp
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfEndnotes(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to pro/verron/officestamper/core/DocxStamper::postprocess → KILLED

149

1.1
Location : stamp
Killed by : pro.verron.officestamper.test.ResolutionTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ResolutionTest]/[test-template:testCustomResolution(java.lang.String, boolean, boolean, boolean, boolean, boolean, java.lang.String, boolean, java.lang.String)]/[test-template-invocation:#28]
removed call to org/docx4j/openpackaging/packages/WordprocessingMLPackage::save → KILLED

156

1.1
Location : preprocess
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#2]
removed call to java/util/List::forEach → KILLED

2.2
Location : lambda$preprocess$1
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(java.lang.String, pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, java.io.InputStream, java.lang.String)]/[test-template-invocation:#2]
removed call to pro/verron/officestamper/api/PreProcessor::process → KILLED

161

1.1
Location : lambda$processComments$2
Killed by : none
removed call to pro/verron/officestamper/core/DocxStamper::runProcessors → SURVIVED
Covering tests

2.2
Location : processComments
Killed by : none
removed call to java/util/stream/Stream::forEach → SURVIVED Covering tests

162

1.1
Location : processComments
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to pro/verron/officestamper/core/DocxStamper::runProcessors → KILLED

164

1.1
Location : lambda$processComments$3
Killed by : none
removed call to pro/verron/officestamper/core/DocxStamper::runProcessors → SURVIVED
Covering tests

2.2
Location : processComments
Killed by : none
removed call to java/util/stream/Stream::forEach → SURVIVED Covering tests

169

1.1
Location : replaceExpressions
Killed by : pro.verron.officestamper.test.HeaderAndFooterTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.HeaderAndFooterTest]/[test-template:placeholders(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#2]
removed call to java/util/stream/Stream::forEach → KILLED

2.2
Location : lambda$replaceExpressions$4
Killed by : pro.verron.officestamper.test.HeaderAndFooterTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.HeaderAndFooterTest]/[test-template:placeholders(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#2]
removed call to pro/verron/officestamper/core/PlaceholderReplacer::resolveExpressions → KILLED

170

1.1
Location : replaceExpressions
Killed by : pro.verron.officestamper.test.ResolutionTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ResolutionTest]/[test-template:testCustomResolution(java.lang.String, boolean, boolean, boolean, boolean, boolean, java.lang.String, boolean, java.lang.String)]/[test-template-invocation:#29]
removed call to pro/verron/officestamper/core/PlaceholderReplacer::resolveExpressions → KILLED

172

1.1
Location : replaceExpressions
Killed by : pro.verron.officestamper.test.HeaderAndFooterTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.HeaderAndFooterTest]/[test-template:placeholders(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#2]
removed call to java/util/stream/Stream::forEach → KILLED

2.2
Location : lambda$replaceExpressions$5
Killed by : pro.verron.officestamper.test.HeaderAndFooterTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.HeaderAndFooterTest]/[test-template:placeholders(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#2]
removed call to pro/verron/officestamper/core/PlaceholderReplacer::resolveExpressions → KILLED

177

1.1
Location : runProcessors
Killed by : pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.FailOnUnresolvedPlaceholderTest]/[test-template:fails(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to pro/verron/officestamper/core/CommentProcessorRegistry::runProcessors → KILLED

181

1.1
Location : lambda$postprocess$6
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfEndnotes(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to pro/verron/officestamper/api/PostProcessor::process → KILLED

2.2
Location : postprocess
Killed by : pro.verron.officestamper.test.ConditionalDisplayTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ConditionalDisplayTest]/[test-template:conditionalDisplayOfEndnotes(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#1]
removed call to java/util/List::forEach → KILLED

Active mutators

Tests examined


Report generated by PIT 1.20.0