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<Object>)_ 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 |
|
81 |
1.1 |
|
82 |
1.1 |
|
83 |
1.1 |
|
86 |
1.1 |
|
106 |
1.1 |
|
131 |
1.1 |
|
144 |
1.1 |
|
145 |
1.1 |
|
146 |
1.1 |
|
147 |
1.1 |
|
148 |
1.1 |
|
155 |
1.1 2.2 |
|
160 |
1.1 2.2 |
|
161 |
1.1 |
|
163 |
1.1 2.2 |
|
168 |
1.1 2.2 |
|
169 |
1.1 |
|
171 |
1.1 2.2 |
|
176 |
1.1 |
|
180 |
1.1 2.2 |