DocxStamper.java

1
package pro.verron.officestamper.core;
2
3
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
4
import org.docx4j.openpackaging.parts.Part;
5
import org.docx4j.wml.ContentAccessor;
6
import pro.verron.officestamper.api.*;
7
import pro.verron.officestamper.utils.svg.SvgUtils;
8
9
import java.util.ArrayList;
10
import java.util.List;
11
import java.util.Map;
12
13
import static org.docx4j.openpackaging.parts.relationships.Namespaces.FOOTER;
14
import static org.docx4j.openpackaging.parts.relationships.Namespaces.HEADER;
15
16
/// The [DocxStamper] class is an implementation of the [OfficeStamper] interface used to stamp DOCX templates with a
17
/// context object and write the result to an output stream.
18
///
19
/// @author Tom Hombergs
20
/// @author Joseph Verron
21
/// @version ${version}
22
/// @since 1.0.0
23
public class DocxStamper
24
        implements OfficeStamper<WordprocessingMLPackage> {
25
26
    private final List<PreProcessor> preprocessors;
27
    private final List<PostProcessor> postprocessors;
28
    private final EngineFactory engineFactory;
29
    private final EvaluationContextFactory contextFactory;
30
    private final Map<Class<?>, Object> interfaceFunctions;
31
    private final List<CustomFunction> customFunctions;
32
    private final Map<Class<?>, CommentProcessorFactory> commentProcessors;
33
34
    /// Creates new [DocxStamper] with the given configuration.
35
    ///
36
    /// @param configuration the configuration to use for this [DocxStamper].
37
    public DocxStamper(OfficeStamperConfiguration configuration) {
38
        this.contextFactory = configuration.getEvaluationContextFactory();
39
        this.interfaceFunctions = configuration.getExpressionFunctions();
40
        this.customFunctions = configuration.customFunctions();
41
        this.commentProcessors = configuration.getCommentProcessors();
42
        // Apply global SVG safe-mode preference early so that any SVG manipulations during stamping honor it.
43 2 1. <init> : negated conditional → SURVIVED
2. <init> : removed call to pro/verron/officestamper/utils/svg/SvgUtils::disableSafeMode → NO_COVERAGE
        if (SecurityMode.PERMISSIVE.equals(configuration.getSvgSecurityMode())) SvgUtils.disableSafeMode();
44 1 1. <init> : removed call to pro/verron/officestamper/utils/svg/SvgUtils::enableSafeMode → SURVIVED
        else SvgUtils.enableSafeMode();
45
        this.engineFactory = processorContext -> {
46
            var parserConfiguration = configuration.getParserConfiguration();
47
            var exceptionResolver = configuration.getExceptionResolver();
48
            var resolvers = configuration.getResolvers();
49
            var registry = new ObjectResolverRegistry(resolvers);
50 1 1. lambda$new$0 : replaced return value with null for pro/verron/officestamper/core/DocxStamper::lambda$new$0 → KILLED
            return new Engine(parserConfiguration, exceptionResolver, registry, processorContext);
51
        };
52
        this.preprocessors = new ArrayList<>(configuration.getPreprocessors());
53
        this.postprocessors = new ArrayList<>(configuration.getPostprocessors());
54
    }
55
56
    /// Reads in a .docx template and "stamps" it, using the specified context object to fill out any expressions it
57
    /// finds.
58
    ///
59
    /// In the .docx template you have the following options to influence the "stamping" process:
60
    ///   - Use expressions like `${name}` or `${person.isOlderThan(18)}` in the template's text. These expressions are
61
    /// resolved against the contextRoot object you pass into this method and are replaced by the results.
62
    ///   - Use comments within the .docx template to mark certain paragraphs to be manipulated.
63
    ///
64
    /// Within comments, you can put expressions in which you can use the following methods by default:
65
    ///   - `displayParagraphIf(boolean)` to conditionally display paragraphs or not
66
    ///   - `displayTableRowIf(boolean)` to conditionally display table rows or not
67
    ///   - `displayTableIf(boolean)` to conditionally display whole tables or not
68
    ///   - `repeatTableRow(List<Object>)` to create a new table row for each object in the list and resolve expressions
69
    /// within the table cells against one of the objects within the list.
70
    ///
71
    /// If you need a wider vocabulary of methods available in the comments, you can create your own [CommentProcessor]
72
    /// and register it via [OfficeStamperConfiguration#addCommentProcessor(Class, CommentProcessorFactory)].
73
    ///
74
    /// @param document the .docx template to stamp
75
    /// @param contextRoot the context object to use for stamping
76
    ///
77
    /// @return the stamped document
78
    @Override
79
    public WordprocessingMLPackage stamp(WordprocessingMLPackage document, Object contextRoot) {
80 1 1. stamp : removed call to pro/verron/officestamper/core/DocxStamper::preprocess → KILLED
        preprocess(document);
81 1 1. stamp : removed call to pro/verron/officestamper/core/DocxStamper::process → KILLED
        process(document, contextRoot);
82 1 1. stamp : removed call to pro/verron/officestamper/core/DocxStamper::postprocess → KILLED
        postprocess(document);
83 1 1. stamp : replaced return value with null for pro/verron/officestamper/core/DocxStamper::stamp → KILLED
        return document;
84
    }
85
86
    private void preprocess(WordprocessingMLPackage document) {
87 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));
88
    }
89
90
    private void process(WordprocessingMLPackage document, Object contextRoot) {
91
        var mainDocumentPart = document.getMainDocumentPart();
92
        var mainPart = new TextualDocxPart(document, mainDocumentPart, mainDocumentPart);
93 1 1. process : removed call to pro/verron/officestamper/core/DocxStamper::process → KILLED
        process(mainPart, contextRoot);
94
95
        var relationshipsPart = mainDocumentPart.getRelationshipsPart();
96
        for (var relationship : relationshipsPart.getRelationshipsByType(HEADER)) {
97
            Part part1 = relationshipsPart.getPart(relationship);
98
            TextualDocxPart textualDocxPart = new TextualDocxPart(document, part1, (ContentAccessor) part1);
99 1 1. process : removed call to pro/verron/officestamper/core/DocxStamper::process → KILLED
            process(textualDocxPart, contextRoot);
100
        }
101
102
        for (var relationship : relationshipsPart.getRelationshipsByType(FOOTER)) {
103
            Part part = relationshipsPart.getPart(relationship);
104
            TextualDocxPart textualDocxPart = new TextualDocxPart(document, part, (ContentAccessor) part);
105 1 1. process : removed call to pro/verron/officestamper/core/DocxStamper::process → KILLED
            process(textualDocxPart, contextRoot);
106
        }
107
    }
108
109
    private void postprocess(WordprocessingMLPackage document) {
110 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));
111
    }
112
113
    private void process(DocxPart part, Object contextRoot) {
114
        var contextTree = new ContextRoot(contextRoot);
115
        var iterator = DocxHook.ofHooks(part::content, part);
116 1 1. process : negated conditional → KILLED
        while (iterator.hasNext()) {
117
            var hook = iterator.next();
118
            var officeStamperContextFactory = new OfficeStamperEvaluationContextFactory(customFunctions,
119
                    commentProcessors,
120
                    interfaceFunctions,
121
                    contextFactory);
122 1 1. process : negated conditional → TIMED_OUT
            if (hook.run(engineFactory, contextTree, officeStamperContextFactory)) {
123 1 1. process : removed call to pro/verron/officestamper/utils/iterator/ResetableIterator::reset → KILLED
                iterator.reset();
124
            }
125
        }
126
    }
127
128
}

Mutations

43

1.1
Location : <init>
Killed by : none
negated conditional → SURVIVED
Covering tests

2.2
Location : <init>
Killed by : none
removed call to pro/verron/officestamper/utils/svg/SvgUtils::disableSafeMode → NO_COVERAGE

44

1.1
Location : <init>
Killed by : none
removed call to pro/verron/officestamper/utils/svg/SvgUtils::enableSafeMode → SURVIVED
Covering tests

50

1.1
Location : lambda$new$0
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/DocxStamper::lambda$new$0 → KILLED

80

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.utils.ContextFactory)]/[test-template-invocation:#2]
removed call to pro/verron/officestamper/core/DocxStamper::preprocess → KILLED

81

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.utils.ContextFactory)]/[test-template-invocation:#2]
removed call to pro/verron/officestamper/core/DocxStamper::process → KILLED

82

1.1
Location : stamp
Killed by : pro.verron.officestamper.test.CustomFunctionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.CustomFunctionTests]/[test-template:trifunctions(pro.verron.officestamper.test.utils.ContextFactory, java.lang.String, java.lang.String)]/[test-template-invocation:#12]
removed call to pro/verron/officestamper/core/DocxStamper::postprocess → KILLED

83

1.1
Location : stamp
Killed by : pro.verron.officestamper.test.DateFormatTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DateFormatTests]/[test-template:features(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#2]
replaced return value with null for pro/verron/officestamper/core/DocxStamper::stamp → KILLED

87

1.1
Location : preprocess
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]
removed call to java/util/List::forEach → KILLED

2.2
Location : lambda$preprocess$0
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]
removed call to pro/verron/officestamper/api/PreProcessor::process → KILLED

93

1.1
Location : process
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]
removed call to pro/verron/officestamper/core/DocxStamper::process → KILLED

99

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

105

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

110

1.1
Location : postprocess
Killed by : pro.verron.officestamper.test.CustomFunctionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.CustomFunctionTests]/[test-template:trifunctions(pro.verron.officestamper.test.utils.ContextFactory, java.lang.String, java.lang.String)]/[test-template-invocation:#12]
removed call to java/util/List::forEach → KILLED

2.2
Location : lambda$postprocess$0
Killed by : pro.verron.officestamper.test.CustomFunctionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.CustomFunctionTests]/[test-template:trifunctions(pro.verron.officestamper.test.utils.ContextFactory, java.lang.String, java.lang.String)]/[test-template-invocation:#12]
removed call to pro/verron/officestamper/api/PostProcessor::process → KILLED

116

1.1
Location : process
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]
negated conditional → KILLED

122

1.1
Location : process
Killed by : none
negated conditional → TIMED_OUT

123

1.1
Location : process
Killed by : pro.verron.officestamper.test.ProcessorRepeatDocPart_BadPlaceholderTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorRepeatDocPart_BadPlaceholderTest]/[test-template:testBadExpressionShouldNotBlockCallerThread(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#2]
removed call to pro/verron/officestamper/utils/iterator/ResetableIterator::reset → KILLED

Active mutators

Tests examined


Report generated by PIT 1.23.1 support