1 | package com.renomad.minum.templating; | |
2 | ||
3 | import java.util.*; | |
4 | import java.util.stream.Collectors; | |
5 | ||
6 | import static com.renomad.minum.utils.SerializationUtils.tokenizer; | |
7 | ||
8 | /** | |
9 | * This class provides methods for working with templates. | |
10 | * <p> | |
11 | * The first step is to write a template. Here is an example: | |
12 | * </p> | |
13 | * <pre> | |
14 | * Hello, my name is {{name}} | |
15 | * </pre> | |
16 | * <p> | |
17 | * Then, feed that string into the {@link #buildProcessor} method, like | |
18 | * this: | |
19 | * </p> | |
20 | * <pre> | |
21 | * {@code | |
22 | * String input = "Hello, my name is {{name}}" | |
23 | * TemplateProcessor helloProcessor = TemplateProcessor.buildProcessor(input); | |
24 | * } | |
25 | * </pre> | |
26 | * <p> | |
27 | * The returned value ("helloProcessor") can be rendered with different values. For | |
28 | * example: | |
29 | * </p> | |
30 | * <pre> | |
31 | * {@code | |
32 | * Map<String,String> myMap = Map.of("name", "Susanne"); | |
33 | * String fullyRenderedString = helloProcessor.renderTemplate(myMap); | |
34 | * } | |
35 | * </pre> | |
36 | * <p> | |
37 | * The result is: | |
38 | * </p> | |
39 | * <pre> | |
40 | * {@code Hello, my name is Susanne} | |
41 | * </pre> | |
42 | */ | |
43 | public final class TemplateProcessor { | |
44 | ||
45 | /** | |
46 | * template sections by indentation | |
47 | */ | |
48 | private Map<Integer, List<TemplateSection>> templatesSectionsByIndent; | |
49 | private List<Map<String, String>> dataList = new ArrayList<>(); | |
50 | private final Set<String> keysFoundInTemplate; | |
51 | private final Set<String> keysRegisteredForInnerTemplates; | |
52 | private final String originalText; | |
53 | /** | |
54 | * This value is used to calculate a quick estimate of how many | |
55 | * bytes of memory we will need for the buffer holding our generated string | |
56 | */ | |
57 | private final static double SIZE_ESTIMATE_MODIFIER = 1.1; | |
58 | private final int estimatedSize; | |
59 | private Map<String, TemplateProcessor> innerTemplates; | |
60 | ||
61 | /** | |
62 | * Instantiate a new object with a list of {@link TemplateSection}. | |
63 | */ | |
64 | private TemplateProcessor(List<TemplateSection> templateSections, String originalText) { | |
65 | this.templatesSectionsByIndent = new HashMap<>(); | |
66 | this.templatesSectionsByIndent.put(0, templateSections); | |
67 | keysFoundInTemplate = new HashSet<>(); | |
68 | keysRegisteredForInnerTemplates = new HashSet<>(); | |
69 | this.originalText = originalText; | |
70 | this.innerTemplates = new HashMap<>(); | |
71 |
1
1. <init> : Replaced double multiplication with division → KILLED |
estimatedSize = (int) Math.round(originalText.length() * SIZE_ESTIMATE_MODIFIER); |
72 | } | |
73 | ||
74 | /** | |
75 | * Given a map of key names -> value, render a template. | |
76 | */ | |
77 | public String renderTemplate(Map<String, String> myMap) { | |
78 |
1
1. renderTemplate : removed call to com/renomad/minum/templating/TemplateProcessor::registerData → KILLED |
registerData(List.of(myMap)); |
79 | ||
80 |
1
1. renderTemplate : replaced return value with "" for com/renomad/minum/templating/TemplateProcessor::renderTemplate → KILLED |
return internalRender(true).toString(); |
81 | } | |
82 | ||
83 | /** | |
84 | * Given a list of maps of key names -> value, render a template | |
85 | * multiple times. | |
86 | */ | |
87 | public String renderTemplate(List<Map<String, String>> myMap) { | |
88 |
1
1. renderTemplate : removed call to com/renomad/minum/templating/TemplateProcessor::registerData → KILLED |
registerData(myMap); |
89 | ||
90 |
1
1. renderTemplate : replaced return value with "" for com/renomad/minum/templating/TemplateProcessor::renderTemplate → KILLED |
return internalRender(true).toString(); |
91 | } | |
92 | ||
93 | /** | |
94 | * Recursively assembles the template and sub-templates | |
95 | */ | |
96 | public String renderTemplate() { | |
97 |
1
1. renderTemplate : replaced return value with "" for com/renomad/minum/templating/TemplateProcessor::renderTemplate → KILLED |
return internalRender(true).toString(); |
98 | } | |
99 | ||
100 | /** | |
101 | * Render the template and any nested sub-templates. All templates | |
102 | * must have data registered before running this method. | |
103 | * @param runWithChecks Default: true. Check that there is a 1-to-1 correspondence between | |
104 | * the keys provided and keys in the template and sub-templates, throwing | |
105 | * an exception if there are any errors. Also check that the maps | |
106 | * of data are consistent. This should be set true unless there is a reason | |
107 | * to aim for maximum performance, which is actually not | |
108 | * valuable in most cases, since the bottleneck is the business algorithms, database, | |
109 | * and HTTP processing. | |
110 | */ | |
111 | public String renderTemplate(boolean runWithChecks) { | |
112 |
1
1. renderTemplate : replaced return value with "" for com/renomad/minum/templating/TemplateProcessor::renderTemplate → KILLED |
return internalRender(runWithChecks).toString(); |
113 | } | |
114 | ||
115 | /** | |
116 | * Assign data. Keys must match to template. | |
117 | */ | |
118 | public void registerData(List<Map<String, String>> dataList) { | |
119 |
1
1. registerData : negated conditional → KILLED |
if (dataList == null){ |
120 | throw new TemplateRenderException("provided data cannot be null"); | |
121 |
1
1. registerData : negated conditional → KILLED |
} else if (dataList.isEmpty()) { |
122 | throw new TemplateRenderException("No data provided in registerData call"); | |
123 | } | |
124 | ||
125 | this.dataList = dataList; | |
126 | } | |
127 | ||
128 | /** | |
129 | * Builds a {@link TemplateProcessor} from a string | |
130 | * containing a proper template. Templated values | |
131 | * are surrounded by double-curly-braces, i.e. {{foo}} or {{ foo }} | |
132 | */ | |
133 | public static TemplateProcessor buildProcessor(String template) { | |
134 |
2
1. buildProcessor : negated conditional → KILLED 2. buildProcessor : negated conditional → KILLED |
if (template == null || template.isEmpty()) { |
135 | throw new TemplateRenderException("The input to building a template must be a non-empty string"); | |
136 | } | |
137 | var tp = new TemplateProcessor(new ArrayList<>(), template); | |
138 | List<TemplateSection> tSections = tp.renderToTemplateSections(template); | |
139 | Set<String> keysFound = tSections.stream() | |
140 |
2
1. lambda$buildProcessor$0 : replaced boolean return with true for com/renomad/minum/templating/TemplateProcessor::lambda$buildProcessor$0 → KILLED 2. lambda$buildProcessor$0 : replaced boolean return with false for com/renomad/minum/templating/TemplateProcessor::lambda$buildProcessor$0 → KILLED |
.filter(x -> x.templateType.equals(TemplateType.DYNAMIC_TEXT)) |
141 |
1
1. lambda$buildProcessor$1 : replaced return value with "" for com/renomad/minum/templating/TemplateProcessor::lambda$buildProcessor$1 → KILLED |
.map(x -> x.key) |
142 | .collect(Collectors.toSet()); | |
143 | tp.keysFoundInTemplate.addAll(keysFound); | |
144 | tp.templatesSectionsByIndent.put(0, tSections); | |
145 | ||
146 |
1
1. buildProcessor : replaced return value with null for com/renomad/minum/templating/TemplateProcessor::buildProcessor → KILLED |
return tp; |
147 | } | |
148 | ||
149 | private ArrayList<TemplateSection> renderToTemplateSections(String template) { | |
150 | // this value holds the entire template after processing, comprised | |
151 | // of an ordered list of TemplateSections | |
152 | var tSections = new ArrayList<TemplateSection>(); | |
153 | ||
154 | // these values are used for logging and setting proper indentation | |
155 | int rowNumber = 1; | |
156 | int columnNumber = 1; | |
157 | // this value records the indent of the beginning of template keys, | |
158 | // so we can properly indent the values later. | |
159 | int startOfKey = 0; | |
160 | ||
161 | StringBuilder builder = new StringBuilder(); | |
162 | // this flag is to help us understand whether we are currently reading the | |
163 | // name of a template literal. | |
164 | // e.g. in the case of hello {{ name }}, "name" is the literal. | |
165 | boolean isInsideTemplateKeyLiteral = false; | |
166 |
2
1. renderToTemplateSections : changed conditional boundary → KILLED 2. renderToTemplateSections : negated conditional → KILLED |
for (int i = 0; i < template.length(); i++) { |
167 | char charAtCursor = template.charAt(i); | |
168 | ||
169 |
6
1. renderToTemplateSections : negated conditional → KILLED 2. renderToTemplateSections : changed conditional boundary → KILLED 3. renderToTemplateSections : Replaced integer addition with subtraction → KILLED 4. renderToTemplateSections : negated conditional → KILLED 5. renderToTemplateSections : negated conditional → KILLED 6. renderToTemplateSections : Replaced integer addition with subtraction → KILLED |
if (charAtCursor == '{' && (i + 1) < template.length() && template.charAt(i + 1) == '{') { |
170 | isInsideTemplateKeyLiteral = true; | |
171 |
1
1. renderToTemplateSections : Replaced integer subtraction with addition → KILLED |
startOfKey = columnNumber - 1; |
172 |
1
1. renderToTemplateSections : Changed increment from 1 to -1 → TIMED_OUT |
i += 1; |
173 | builder = processSectionInside(builder, tSections); | |
174 |
7
1. renderToTemplateSections : Replaced integer addition with subtraction → KILLED 2. renderToTemplateSections : negated conditional → KILLED 3. renderToTemplateSections : negated conditional → KILLED 4. renderToTemplateSections : changed conditional boundary → KILLED 5. renderToTemplateSections : negated conditional → KILLED 6. renderToTemplateSections : negated conditional → KILLED 7. renderToTemplateSections : Replaced integer addition with subtraction → KILLED |
} else if (isInsideTemplateKeyLiteral && charAtCursor == '}' && (i + 1) < template.length() && template.charAt(i + 1) == '}') { |
175 | isInsideTemplateKeyLiteral = false; | |
176 |
1
1. renderToTemplateSections : Changed increment from 1 to -1 → KILLED |
i += 1; |
177 | builder = processSectionOutside(builder, tSections, startOfKey); | |
178 | startOfKey = 0; | |
179 | } else { | |
180 | builder.append(charAtCursor); | |
181 | ||
182 | /* | |
183 | if we're at the end of the template, it's our last chance to | |
184 | add a substring (we can't be adding to a key, since if we're | |
185 | at the end, and it's not a closing brace, it's a malformed | |
186 | template. | |
187 | */ | |
188 |
2
1. renderToTemplateSections : negated conditional → KILLED 2. renderToTemplateSections : Replaced integer subtraction with addition → KILLED |
if (i == template.length() - 1) { |
189 |
1
1. renderToTemplateSections : negated conditional → KILLED |
if (isInsideTemplateKeyLiteral) { |
190 | // if we're exiting this string while inside a template literal, then | |
191 | // we're reading a corrupted input, and we should make that clear | |
192 | // to our caller. | |
193 |
2
1. renderToTemplateSections : changed conditional boundary → KILLED 2. renderToTemplateSections : negated conditional → KILLED |
String templateSample = template.length() > 10 ? template.substring(0, 10) + "..." : template; |
194 | throw new TemplateParseException( | |
195 | "parsing failed for string starting with \"" + templateSample + "\" at line " + rowNumber + " and column " + columnNumber); | |
196 | } | |
197 | tSections.add(new TemplateSection(null, builder.toString(), null, TemplateType.STATIC_TEXT, 0)); | |
198 | } | |
199 | } | |
200 | ||
201 |
1
1. renderToTemplateSections : negated conditional → KILLED |
if (charAtCursor == '\n') { |
202 |
1
1. renderToTemplateSections : Changed increment from 1 to -1 → KILLED |
rowNumber += 1; |
203 | columnNumber = 1; | |
204 | } else { | |
205 |
1
1. renderToTemplateSections : Changed increment from 1 to -1 → KILLED |
columnNumber += 1; |
206 | } | |
207 | ||
208 | } | |
209 |
1
1. renderToTemplateSections : replaced return value with null for com/renomad/minum/templating/TemplateProcessor::renderToTemplateSections → KILLED |
return tSections; |
210 | } | |
211 | ||
212 | private static StringBuilder processSectionInside(StringBuilder builder, List<TemplateSection> tSections) { | |
213 |
1
1. processSectionInside : negated conditional → KILLED |
if (!builder.isEmpty()) { |
214 | tSections.add(new TemplateSection(null, builder.toString(), null, TemplateType.STATIC_TEXT, 0)); | |
215 | builder = new StringBuilder(); | |
216 | } | |
217 |
1
1. processSectionInside : replaced return value with null for com/renomad/minum/templating/TemplateProcessor::processSectionInside → KILLED |
return builder; |
218 | } | |
219 | ||
220 | private static StringBuilder processSectionOutside(StringBuilder builder, | |
221 | List<TemplateSection> tSections, | |
222 | int indent) { | |
223 | String key = builder.toString().trim(); | |
224 | tSections.add(new TemplateSection(key, "", null, TemplateType.DYNAMIC_TEXT, indent)); | |
225 | builder = new StringBuilder(); | |
226 |
1
1. processSectionOutside : replaced return value with null for com/renomad/minum/templating/TemplateProcessor::processSectionOutside → KILLED |
return builder; |
227 | } | |
228 | ||
229 | /** | |
230 | * Binds an inner template to a key of this template. | |
231 | */ | |
232 | public TemplateProcessor registerInnerTemplate(String key, TemplateProcessor innerTemplate) { | |
233 |
2
1. registerInnerTemplate : negated conditional → KILLED 2. registerInnerTemplate : negated conditional → KILLED |
if (key == null || key.isBlank()) { |
234 | throw new TemplateRenderException("The key must be a valid non-blank string"); | |
235 | } | |
236 |
1
1. registerInnerTemplate : negated conditional → KILLED |
if (innerTemplate == null) { |
237 | throw new TemplateRenderException("The template must not be null"); | |
238 | } | |
239 |
1
1. registerInnerTemplate : negated conditional → KILLED |
if (this.equals(innerTemplate)) { |
240 | throw new TemplateRenderException("Disallowed to register a template to itself as an inner template"); | |
241 | } | |
242 |
1
1. registerInnerTemplate : negated conditional → KILLED |
if (keysRegisteredForInnerTemplates.contains(key)) { |
243 | throw new TemplateRenderException("key is already registered for use in another template: " + key); | |
244 | } | |
245 | ||
246 | // get the indent we should apply to each line after the first | |
247 | // by seeing what indent exists in the template sections and | |
248 | // creating a separate indented version for each one | |
249 | Set<Integer> necessaryIndentations = this.templatesSectionsByIndent.get(0).stream() | |
250 |
2
1. lambda$registerInnerTemplate$2 : replaced boolean return with true for com/renomad/minum/templating/TemplateProcessor::lambda$registerInnerTemplate$2 → SURVIVED 2. lambda$registerInnerTemplate$2 : replaced boolean return with false for com/renomad/minum/templating/TemplateProcessor::lambda$registerInnerTemplate$2 → KILLED |
.filter(x -> key.equals(x.key)) |
251 |
1
1. lambda$registerInnerTemplate$3 : replaced Integer return value with 0 for com/renomad/minum/templating/TemplateProcessor::lambda$registerInnerTemplate$3 → KILLED |
.map(x -> x.indent).collect(Collectors.toSet()); |
252 | ||
253 | // make sure we have one for zero as well. | |
254 | necessaryIndentations.add(0); | |
255 | ||
256 | ||
257 | var copyOfInnerTemplate = new TemplateProcessor(innerTemplate.templatesSectionsByIndent.get(0), innerTemplate.getOriginalText()); | |
258 | copyOfInnerTemplate.keysFoundInTemplate.addAll(innerTemplate.keysFoundInTemplate); | |
259 | copyOfInnerTemplate.keysRegisteredForInnerTemplates.addAll(innerTemplate.keysRegisteredForInnerTemplates); | |
260 | copyOfInnerTemplate.innerTemplates = new HashMap<>(innerTemplate.innerTemplates); | |
261 | this.innerTemplates.put(key, copyOfInnerTemplate); | |
262 | ||
263 |
1
1. registerInnerTemplate : removed call to java/util/Map::clear → SURVIVED |
copyOfInnerTemplate.templatesSectionsByIndent.clear(); |
264 | ||
265 | // a non-configurable ceiling limit to avoid runaway loops | |
266 | int MAXIMUM_LINES_ALLOWED = 10_000_000; | |
267 | String originalText = copyOfInnerTemplate.getOriginalText(); | |
268 | List<String> lines = tokenizer(originalText, '\n', MAXIMUM_LINES_ALLOWED); | |
269 | ||
270 | // if, after splitting on newlines, we have more than one line, we'll indent the remaining | |
271 | // lines so that they end up at the same column as the first line. | |
272 | for (int indentation : necessaryIndentations) { | |
273 | var indentedInnerTemplateText = new StringBuilder(lines.getFirst()); | |
274 |
2
1. registerInnerTemplate : negated conditional → KILLED 2. registerInnerTemplate : changed conditional boundary → KILLED |
for (int i = 1; i < lines.size(); i++) { |
275 |
1
1. registerInnerTemplate : negated conditional → KILLED |
if (lines.get(i).isEmpty()) { |
276 | indentedInnerTemplateText.append('\n'); | |
277 | } else { | |
278 | indentedInnerTemplateText.append('\n').append(" ".repeat(indentation)).append(lines.get(i)); | |
279 | } | |
280 | } | |
281 | List<TemplateSection> tSections = renderToTemplateSections(indentedInnerTemplateText.toString()); | |
282 | copyOfInnerTemplate.templatesSectionsByIndent.put(indentation, tSections); | |
283 | ||
284 | // now, loop through all the template sections, replacing them appropriately with | |
285 | // new data labeled as INNER_TEMPLATE. | |
286 | Map<Integer, List<TemplateSection>> revisedTemplateSectionsByIndent = new HashMap<>(); | |
287 | for (var templateSectionsByIndent : templatesSectionsByIndent.entrySet()) { | |
288 | List<TemplateSection> revisedList = new ArrayList<>(); | |
289 | for (TemplateSection templateSection : templateSectionsByIndent.getValue()) { | |
290 |
1
1. registerInnerTemplate : negated conditional → KILLED |
if (key.equals(templateSection.key)) { |
291 | revisedList.add(new TemplateSection(templateSection.key, | |
292 | templateSection.staticData, | |
293 | copyOfInnerTemplate, | |
294 | TemplateType.INNER_TEMPLATE, | |
295 | templateSection.indent)); | |
296 | } else { | |
297 | revisedList.add(templateSection); | |
298 | } | |
299 | } | |
300 | revisedTemplateSectionsByIndent.put(templateSectionsByIndent.getKey(), revisedList); | |
301 | } | |
302 | templatesSectionsByIndent = revisedTemplateSectionsByIndent; | |
303 | ||
304 | this.keysRegisteredForInnerTemplates.add(key); | |
305 | } | |
306 | ||
307 |
1
1. registerInnerTemplate : replaced return value with null for com/renomad/minum/templating/TemplateProcessor::registerInnerTemplate → KILLED |
return copyOfInnerTemplate; |
308 | } | |
309 | ||
310 | /** | |
311 | * Returns the original unchanged template string | |
312 | */ | |
313 | public String getOriginalText() { | |
314 |
1
1. getOriginalText : replaced return value with "" for com/renomad/minum/templating/TemplateProcessor::getOriginalText → KILLED |
return originalText; |
315 | } | |
316 | ||
317 | /** | |
318 | * now, loop through the lists of data we were given, with the | |
319 | * internal template sections in hand | |
320 | */ | |
321 | private StringBuilder internalRender(boolean runWithChecks) { | |
322 |
1
1. internalRender : negated conditional → KILLED |
if (runWithChecks) { |
323 |
1
1. internalRender : removed call to com/renomad/minum/templating/TemplateProcessor::correctnessCheck → KILLED |
correctnessCheck(); |
324 | } | |
325 | int capacity = calculateEstimatedSize(); | |
326 | StringBuilder parts = new StringBuilder(capacity); | |
327 |
1
1. internalRender : replaced return value with null for com/renomad/minum/templating/TemplateProcessor::internalRender → KILLED |
return internalRender(0, parts); |
328 | } | |
329 | ||
330 | /** | |
331 | * This examines the currently registered data lists and template keys | |
332 | * and confirms they are aligned. It will throw an exception if they | |
333 | * are not perfectly correlated. | |
334 | * <br> | |
335 | * | |
336 | */ | |
337 | private void correctnessCheck() { | |
338 | HashSet<String> copyOfKeysInTemplate = new HashSet<>(keysFoundInTemplate); | |
339 | copyOfKeysInTemplate.removeAll(this.innerTemplates.keySet()); | |
340 | ||
341 |
1
1. correctnessCheck : negated conditional → KILLED |
if (this.dataList.isEmpty()) { |
342 |
1
1. correctnessCheck : negated conditional → KILLED |
if (!copyOfKeysInTemplate.isEmpty()) { |
343 | // at this point we know there is no data provided but the template | |
344 | // requires data, so throw an exception. | |
345 | ||
346 | throw new TemplateRenderException("No data was provided for these keys: " + copyOfKeysInTemplate); | |
347 | } | |
348 | } else { | |
349 | ||
350 | // check for inconsistencies between maps in the data list | |
351 | Set<String> keysInFirstMap = this.dataList.getFirst().keySet(); | |
352 | for (Map<String, String> data : this.dataList) { | |
353 |
1
1. correctnessCheck : negated conditional → KILLED |
if (!data.keySet().equals(keysInFirstMap)) { |
354 | Set<String> result = differenceBetweenSets(data.keySet(), keysInFirstMap); | |
355 | throw new TemplateRenderException("In registered data, the maps were inconsistent on these keys: " + result); | |
356 | } | |
357 | } | |
358 | ||
359 | // ensure consistency between the registered data and the template keys | |
360 | HashSet<String> copyOfTemplateKeys = new HashSet<>(copyOfKeysInTemplate); | |
361 | copyOfTemplateKeys.removeAll(keysInFirstMap); | |
362 |
1
1. correctnessCheck : negated conditional → KILLED |
if (!copyOfTemplateKeys.isEmpty()) { |
363 | throw new TemplateRenderException("These keys in the template were not provided data: " + copyOfTemplateKeys); | |
364 | } | |
365 | ||
366 | HashSet<String> copyOfDataKeys = new HashSet<>(keysInFirstMap); | |
367 | copyOfDataKeys.removeAll(copyOfKeysInTemplate); | |
368 |
1
1. correctnessCheck : negated conditional → KILLED |
if (!copyOfDataKeys.isEmpty()) { |
369 | throw new TemplateRenderException("These keys in the data did not match anything in the template: " + copyOfDataKeys); | |
370 | } | |
371 | } | |
372 | ||
373 | for (TemplateProcessor tp : this.innerTemplates.values()) { | |
374 |
1
1. correctnessCheck : removed call to com/renomad/minum/templating/TemplateProcessor::correctnessCheck → KILLED |
tp.correctnessCheck(); |
375 | } | |
376 | ||
377 | } | |
378 | ||
379 | private static Set<String> differenceBetweenSets(Set<String> set1, Set<String> set2) { | |
380 | Set<String> union = new HashSet<>(set2); | |
381 | union.addAll(set1); | |
382 | Set<String> intersection = new HashSet<>(set2); | |
383 | intersection.retainAll(set1); | |
384 | ||
385 | Set<String> result = new HashSet<>(union); | |
386 | result.removeAll(intersection); | |
387 |
1
1. differenceBetweenSets : replaced return value with Collections.emptySet for com/renomad/minum/templating/TemplateProcessor::differenceBetweenSets → KILLED |
return result; |
388 | } | |
389 | ||
390 | /** | |
391 | * build up a calculated size estimate for this and all | |
392 | * nested templates. | |
393 | */ | |
394 | private int calculateEstimatedSize() { | |
395 | // the size of the datalist specifies how many times we will render ourselves. | |
396 |
1
1. calculateEstimatedSize : negated conditional → KILLED |
int sizeMultiplier = this.dataList.isEmpty() ? 1 : this.dataList.size(); |
397 |
1
1. calculateEstimatedSize : Replaced integer multiplication with division → KILLED |
int fullCalculatedSize = sizeMultiplier * estimatedSize; |
398 | for (TemplateProcessor innerProcessor : this.innerTemplates.values()) { | |
399 |
1
1. calculateEstimatedSize : Replaced integer addition with subtraction → KILLED |
fullCalculatedSize += innerProcessor.calculateEstimatedSize(); |
400 | } | |
401 |
1
1. calculateEstimatedSize : replaced int return with 0 for com/renomad/minum/templating/TemplateProcessor::calculateEstimatedSize → KILLED |
return fullCalculatedSize; |
402 | } | |
403 | ||
404 | private StringBuilder internalRender(int indent, StringBuilder parts) { | |
405 | Map<String, String> myDataMap = Map.of(); | |
406 | List<TemplateSection> templateSections = templatesSectionsByIndent.get(indent); | |
407 | int templateSectionsSize = templateSections.size(); | |
408 | int dataListIndex = 0; | |
409 |
1
1. internalRender : negated conditional → KILLED |
if (!dataList.isEmpty()) { |
410 | myDataMap = dataList.get(dataListIndex); | |
411 | } | |
412 | ||
413 | // build ourself out for each map of data given | |
414 | while (true) { | |
415 |
2
1. internalRender : changed conditional boundary → KILLED 2. internalRender : negated conditional → KILLED |
for (int i = 0; i < templateSectionsSize; i++) { |
416 | TemplateSection templateSection = templateSections.get(i); | |
417 | switch (templateSection.templateType) { | |
418 | case STATIC_TEXT -> parts.append(templateSection.staticData); | |
419 | case DYNAMIC_TEXT -> parts.append(myDataMap.get(templateSection.key)); | |
420 | default -> templateSection.templateProcessor.internalRender(templateSection.indent, parts); | |
421 | } | |
422 | ||
423 | } | |
424 |
1
1. internalRender : Changed increment from 1 to -1 → KILLED |
dataListIndex += 1; |
425 |
3
1. internalRender : changed conditional boundary → KILLED 2. internalRender : negated conditional → KILLED 3. internalRender : negated conditional → KILLED |
if (!dataList.isEmpty() && dataListIndex < dataList.size()) { |
426 | myDataMap = dataList.get(dataListIndex); | |
427 | parts.append("\n").repeat(" ", indent); | |
428 | } else { | |
429 |
1
1. internalRender : replaced return value with null for com/renomad/minum/templating/TemplateProcessor::internalRender → KILLED |
return parts; |
430 | } | |
431 | } | |
432 | } | |
433 | ||
434 | /** | |
435 | * Returns the reference to an inner template, to enable registering | |
436 | * data and sub-templates. | |
437 | */ | |
438 | public TemplateProcessor getInnerTemplate(String innerTemplateKey) { | |
439 |
1
1. getInnerTemplate : replaced return value with null for com/renomad/minum/templating/TemplateProcessor::getInnerTemplate → KILLED |
return this.innerTemplates.get(innerTemplateKey); |
440 | } | |
441 | } | |
442 | ||
Mutations | ||
71 |
1.1 |
|
78 |
1.1 |
|
80 |
1.1 |
|
88 |
1.1 |
|
90 |
1.1 |
|
97 |
1.1 |
|
112 |
1.1 |
|
119 |
1.1 |
|
121 |
1.1 |
|
134 |
1.1 2.2 |
|
140 |
1.1 2.2 |
|
141 |
1.1 |
|
146 |
1.1 |
|
166 |
1.1 2.2 |
|
169 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
171 |
1.1 |
|
172 |
1.1 |
|
174 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 |
|
176 |
1.1 |
|
188 |
1.1 2.2 |
|
189 |
1.1 |
|
193 |
1.1 2.2 |
|
201 |
1.1 |
|
202 |
1.1 |
|
205 |
1.1 |
|
209 |
1.1 |
|
213 |
1.1 |
|
217 |
1.1 |
|
226 |
1.1 |
|
233 |
1.1 2.2 |
|
236 |
1.1 |
|
239 |
1.1 |
|
242 |
1.1 |
|
250 |
1.1 2.2 |
|
251 |
1.1 |
|
263 |
1.1 |
|
274 |
1.1 2.2 |
|
275 |
1.1 |
|
290 |
1.1 |
|
307 |
1.1 |
|
314 |
1.1 |
|
322 |
1.1 |
|
323 |
1.1 |
|
327 |
1.1 |
|
341 |
1.1 |
|
342 |
1.1 |
|
353 |
1.1 |
|
362 |
1.1 |
|
368 |
1.1 |
|
374 |
1.1 |
|
387 |
1.1 |
|
396 |
1.1 |
|
397 |
1.1 |
|
399 |
1.1 |
|
401 |
1.1 |
|
409 |
1.1 |
|
415 |
1.1 2.2 |
|
424 |
1.1 |
|
425 |
1.1 2.2 3.3 |
|
429 |
1.1 |
|
439 |
1.1 |