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

Mutations

70

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

81

1.1
Location : <init>
Killed by : pro.verron.officestamper.test.ProcessorDisplayIfTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorDisplayIfTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#2]
removed call to org/springframework/expression/spel/support/StandardEvaluationContext::addMethodResolver → KILLED

82

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

83

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

86

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:#22]
replaced return value with null for pro/verron/officestamper/core/DocxStamper::lambda$new$0 → KILLED

106

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

131

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

144

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

145

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

146

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:#22]
removed call to pro/verron/officestamper/core/DocxStamper::replaceExpressions → KILLED

147

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

148

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:#13]
removed call to org/docx4j/openpackaging/packages/WordprocessingMLPackage::save → KILLED

155

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

2.2
Location : lambda$preprocess$0
Killed by : pro.verron.officestamper.test.ProcessorDisplayIfTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorDisplayIfTest]/[test-template:conditionalDisplayOfEndnotes(pro.verron.officestamper.test.ContextFactory)]/[test-template-invocation:#2]
removed call to pro/verron/officestamper/api/PreProcessor::process → KILLED

160

1.1
Location : lambda$processComments$0
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

161

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

163

1.1
Location : lambda$processComments$1
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

168

1.1
Location : lambda$replaceExpressions$0
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

2.2
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

169

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:#22]
removed call to pro/verron/officestamper/core/PlaceholderReplacer::resolveExpressions → KILLED

171

1.1
Location : lambda$replaceExpressions$1
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

2.2
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

176

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

180

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

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

Active mutators

Tests examined


Report generated by PIT 1.21.0