TemplateSection.java

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

Mutations

47

1.1
Location : <init>
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_HavingKeyAndSubstring_WithStaticText(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

2.2
Location : <init>
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_processSectionOutside_builderNotEmpty(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

3.3
Location : <init>
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_MissingKeyAndSubstring_WithStaticText(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

48

1.1
Location : <init>
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_processSectionOutside_builderNotEmpty(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

2.2
Location : <init>
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_MissingKeyAndSubstring_WithDynamicText(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

3.3
Location : <init>
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_processSectionOutside_builderNotEmpty(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

61

1.1
Location : render
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_indenting_edgeCase_NoIndent(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

70

1.1
Location : render
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_indenting_edgeCase_NoIndent(com.renomad.minum.templating.TemplateSectionTests)
changed conditional boundary → KILLED

2.2
Location : render
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_indenting_edgeCase_NoIndent(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

71

1.1
Location : render
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_indenting_edgeCase_WithIndent(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

74

1.1
Location : render
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_indenting_edgeCase_WithIndent(com.renomad.minum.templating.TemplateSectionTests)
Replaced integer subtraction with addition → KILLED

82

1.1
Location : equals
Killed by : none
replaced boolean return with true for com/renomad/minum/templating/TemplateSection::equals → TIMED_OUT

2.2
Location : equals
Killed by : none
negated conditional → TIMED_OUT

3.3
Location : equals
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_equals(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

84

1.1
Location : equals
Killed by : none
replaced boolean return with true for com/renomad/minum/templating/TemplateSection::equals → TIMED_OUT

2.2
Location : equals
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_equals(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

3.3
Location : equals
Killed by : none
negated conditional → TIMED_OUT

4.4
Location : equals
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_equals(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

5.5
Location : equals
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_equals(com.renomad.minum.templating.TemplateSectionTests)
negated conditional → KILLED

89

1.1
Location : hashCode
Killed by : none
replaced int return with 0 for com/renomad/minum/templating/TemplateSection::hashCode → TIMED_OUT

94

1.1
Location : toString
Killed by : com.renomad.minum.templating.TemplateSectionTests.test_HavingKeyAndSubstring_WithStaticText(com.renomad.minum.templating.TemplateSectionTests)
replaced return value with "" for com/renomad/minum/templating/TemplateSection::toString → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0