1 | package com.renomad.minum.templating; | |
2 | ||
3 | import java.util.List; | |
4 | import java.util.Map; | |
5 | ||
6 | import static com.renomad.minum.utils.Invariants.mustBeTrue; | |
7 | import static com.renomad.minum.utils.SerializationUtils.tokenizer; | |
8 | ||
9 | /** | |
10 | * Represents one item in the list that will eventually be cooked | |
11 | * up into a fully-rendered string. This record is the magic | |
12 | * ingredient to an easy templating system. If it has a key, | |
13 | * then this object will be getting replaced during final string rendering. If it has a substring, | |
14 | * then the substring gets concatenated unchanged when the final string | |
15 | * is rendered. | |
16 | */ | |
17 | class TemplateSection { | |
18 | ||
19 | final String key; | |
20 | private final String subString; | |
21 | private final int indent; | |
22 | private RenderingResult result; | |
23 | ||
24 | /** | |
25 | * @param indent the column number, measured from the left, of the first character of this template key. This is used | |
26 | * to indent the subsequent lines of text when there are newlines in the replacement | |
27 | * text. For example, if the indent is 5, and the value is "a", then it should indent like this: | |
28 | * <br> | |
29 | * <pre>{@code | |
30 | * 12345 | |
31 | * a | |
32 | * }</pre> | |
33 | * @param key the name of the key, e.g. "name", or "color" | |
34 | * @param subString the template content around the keys. For example, in the text | |
35 | * of "my favorite color is {{ color }} and I like it", | |
36 | * it would generate three template sections - "my favorite color is" would be | |
37 | * the first subString, then a key of "color", then a third subString of "and I like it" | |
38 | */ | |
39 | public TemplateSection(String key, String subString, int indent) { | |
40 | this.key = key; | |
41 | this.subString = subString; | |
42 | this.indent = indent; | |
43 | } | |
44 | ||
45 | /** | |
46 | * It would be absurd to send a million lines to a browser, much | |
47 | * less ten million. This is here to set an upper limit on | |
48 | * the tokenizer loop, to prevent certain attacks. | |
49 | */ | |
50 | static final int MAXIMUM_LINES_ALLOWED = 10_000_000; | |
51 | ||
52 | public RenderingResult render(Map<String, String> myMap) { | |
53 | mustBeTrue(subString != null || key != null, "Either the key or substring must exist"); | |
54 |
1
1. render : negated conditional → KILLED |
if (subString != null) { |
55 |
1
1. render : negated conditional → KILLED |
if (result == null) { |
56 | mustBeTrue(key == null, "If this object has a substring, then it must not have a key"); | |
57 | result = new RenderingResult(subString, null); | |
58 | } | |
59 |
1
1. render : replaced return value with null for com/renomad/minum/templating/TemplateSection::render → KILLED |
return result; |
60 | } else { | |
61 | String value = myMap.get(key); | |
62 |
1
1. render : negated conditional → KILLED |
if (value == null) throw new TemplateRenderException("Missing a value for key {"+key+"}"); |
63 | List<String> lines = tokenizer(value, '\n', MAXIMUM_LINES_ALLOWED); | |
64 | ||
65 | // if, after splitting on newlines, we have more than one line, we'll indent the remaining | |
66 | // lines so that they end up at the same column as the first line. | |
67 | var stringBuilder = new StringBuilder(lines.getFirst()); | |
68 |
2
1. render : changed conditional boundary → KILLED 2. render : negated conditional → KILLED |
for (int i = 1; i < lines.size(); i++) { |
69 |
1
1. render : negated conditional → KILLED |
if (lines.get(i).isEmpty()) { |
70 | stringBuilder.append('\n'); | |
71 | } else { | |
72 |
1
1. render : Replaced integer subtraction with addition → KILLED |
stringBuilder.append('\n').append(" ".repeat(indent - 1)).append(lines.get(i)); |
73 | } | |
74 | } | |
75 |
1
1. render : replaced return value with null for com/renomad/minum/templating/TemplateSection::render → KILLED |
return new RenderingResult(stringBuilder.toString(), key); |
76 | } | |
77 | } | |
78 | ||
79 | } | |
Mutations | ||
54 |
1.1 |
|
55 |
1.1 |
|
59 |
1.1 |
|
62 |
1.1 |
|
68 |
1.1 2.2 |
|
69 |
1.1 |
|
72 |
1.1 |
|
75 |
1.1 |