Engine.java

1
package pro.verron.officestamper.core;
2
3
import org.slf4j.Logger;
4
import org.slf4j.LoggerFactory;
5
import org.springframework.expression.EvaluationContext;
6
import org.springframework.expression.TypedValue;
7
import org.springframework.expression.spel.*;
8
import org.springframework.expression.spel.standard.SpelExpressionParser;
9
import pro.verron.officestamper.api.*;
10
11
import java.util.Objects;
12
import java.util.stream.Collectors;
13
14
/// The core engine of OfficeStamper, responsible for processing expressions.
15
public class Engine {
16
    private static final Logger log = LoggerFactory.getLogger(Engine.class);
17
18
    private final SpelParserConfiguration parserConfiguration;
19
    private final ExceptionResolver exceptionResolver;
20
    private final ObjectResolverRegistry objectResolverRegistry;
21
    private final String expression;
22
    private final DocxPart docxPart;
23
    private final SpelExpressionParser expressionParser;
24
    private final TraceabilityReporter traceabilityReporter;
25
26
    /// Constructs an Engine.
27
    ///
28
    /// @param parserConfiguration the parser configuration.
29
    /// @param exceptionResolver the exception resolver.
30
    /// @param objectResolverRegistry the object resolver registry.
31
    /// @param processorContext the processor context.
32
    /// @param traceabilityReporter the traceability reporter.
33
    public Engine(
34
            SpelParserConfiguration parserConfiguration,
35
            ExceptionResolver exceptionResolver,
36
            ObjectResolverRegistry objectResolverRegistry,
37
            ProcessorContext processorContext,
38
            TraceabilityReporter traceabilityReporter
39
    ) {
40
        this.parserConfiguration = parserConfiguration;
41
        this.expressionParser = new SpelExpressionParser(parserConfiguration);
42
        this.exceptionResolver = exceptionResolver;
43
        this.objectResolverRegistry = objectResolverRegistry;
44
        this.expression = processorContext.expression();
45
        this.docxPart = processorContext.part();
46
        this.traceabilityReporter = traceabilityReporter;
47
    }
48
49
    /// Processes the provided evaluation context against the expression defined in the processor context.
50
    ///
51
    /// The method attempts to resolve an expression using the given evaluation context.
52
    ///
53
    /// If successful, the process completes and logs a debug message.
54
    ///
55
    /// Otherwise, on failure ([SpelEvaluationException] or [SpelParseException]), it handles the exception by invoking
56
    /// the exceptionResolver and logs an error.
57
    ///
58
    /// @param evaluationContext the evaluation context for processing the expression.
59
    /// @return true if the processing was successful, otherwise false
60
    public boolean process(EvaluationContext evaluationContext) {
61
        SpelNode spelNode;
62
        try {
63
            spelNode = parseAST(expressionParser, expression);
64
        } catch (SpelParseException e) {
65
            var msgTemplate = "Expression %s could not be parsed successfully.";
66
            var message = msgTemplate.formatted(expression, evaluationContext);
67
            exceptionResolver.resolve(expression, message, e);
68 1 1. process : replaced boolean return with true for pro/verron/officestamper/core/Engine::process → SURVIVED
            return false;
69
        }
70
71
        var expressionState = buildExpressionState(evaluationContext);
72
        try {
73
            var value = spelNode.getValue(expressionState);
74
            log.debug("Processed '{}' successfully.", expression);
75
            var contextBranch = (ContextBranch) Objects.requireNonNull(evaluationContext.getRootObject().getValue());
76 1 1. process : removed call to pro/verron/officestamper/api/TraceabilityReporter::onResolution → SURVIVED
            traceabilityReporter.onResolution(expression, value, contextBranch.stream().collect(Collectors.toList()));
77
        } catch (SpelEvaluationException e) {
78
            var msgTemplate = "Expression %s could not be processed against context '%s'";
79
            var message = msgTemplate.formatted(expression, evaluationContext);
80
            exceptionResolver.resolve(expression, message, e);
81 1 1. process : replaced boolean return with true for pro/verron/officestamper/core/Engine::process → SURVIVED
            return false;
82
        }
83
84 1 1. process : replaced boolean return with false for pro/verron/officestamper/core/Engine::process → KILLED
        return true;
85
    }
86
87
    private static SpelNode parseAST(SpelExpressionParser parser, String expression) {
88
        var parsedExpression = parser.parseRaw(expression);
89
        log.trace("Parsed '{}' successfully.", expression);
90 1 1. parseAST : replaced return value with null for pro/verron/officestamper/core/Engine::parseAST → KILLED
        return parsedExpression.getAST();
91
    }
92
93
    private ExpressionState buildExpressionState(EvaluationContext evaluationContext) {
94
        var contextBranchTypedValue = evaluationContext.getRootObject();
95
        var contextBranch = (ContextBranch) Objects.requireNonNull(contextBranchTypedValue.getValue());
96
        var rootObject = contextBranch.root();
97
        var rootObjectTypedValue = new TypedValue(rootObject);
98
        var expressionState = new ExpressionState(evaluationContext, rootObjectTypedValue, parserConfiguration);
99
        for (Object o : contextBranch) {
100 1 1. buildExpressionState : removed call to org/springframework/expression/spel/ExpressionState::pushActiveContextObject → KILLED
            expressionState.pushActiveContextObject(new TypedValue(o));
101 1 1. buildExpressionState : removed call to org/springframework/expression/spel/ExpressionState::enterScope → KILLED
            expressionState.enterScope();
102
        }
103 1 1. buildExpressionState : replaced return value with null for pro/verron/officestamper/core/Engine::buildExpressionState → KILLED
        return expressionState;
104
    }
105
106
    /// Resolves an [Insert] object by processing the provided evaluation context using the current processor context.
107
    /// Combines the processor context's part and expression with various resolvers to achieve the resolution.
108
    ///
109
    /// @param evaluationContext the evaluation context for processing the expression.
110
    /// @return an [Insert] object representing the resolved result of the expression within the context.
111
    public Insert resolve(EvaluationContext evaluationContext) {
112
        SpelNode spelNode;
113
        try {
114
            spelNode = parseAST(expressionParser, expression);
115
        } catch (SpelParseException e) {
116
            var msgTemplate = "Expression %s could not be parsed successfully.";
117
            var message = msgTemplate.formatted(expression, evaluationContext);
118 1 1. resolve : replaced return value with null for pro/verron/officestamper/core/Engine::resolve → NO_COVERAGE
            return exceptionResolver.resolve(expression, message, e);
119
        }
120
121
        var expressionState = buildExpressionState(evaluationContext);
122
        Object javaResolution;
123
        try {
124
            javaResolution = spelNode.getValue(expressionState);
125
            log.debug("Resolved '{}' successfully.", expression);
126
            var contextBranch = (ContextBranch) Objects.requireNonNull(evaluationContext.getRootObject().getValue());
127 1 1. resolve : removed call to pro/verron/officestamper/api/TraceabilityReporter::onResolution → SURVIVED
            traceabilityReporter.onResolution(expression, javaResolution, contextBranch.stream().collect(Collectors.toList()));
128
        } catch (SpelEvaluationException e) {
129
            var msgTemplate = "Expression %s could not be resolved against context '%s'";
130
            var message = msgTemplate.formatted(expression, evaluationContext);
131 1 1. resolve : replaced return value with null for pro/verron/officestamper/core/Engine::resolve → KILLED
            return exceptionResolver.resolve(expression, message, e);
132
        }
133
134
        try {
135
            var docxResolution = objectResolverRegistry.resolve(docxPart, expression, javaResolution);
136
            log.debug("Converted '{}' to docx ({}) successfully.", expression, docxResolution);
137 1 1. resolve : replaced return value with null for pro/verron/officestamper/core/Engine::resolve → KILLED
            return docxResolution;
138
        } catch (SpelEvaluationException e) {
139
            var msgTemplate = "Expression %s could not be converted to docx inserts.";
140
            var message = msgTemplate.formatted(expression, evaluationContext);
141 1 1. resolve : replaced return value with null for pro/verron/officestamper/core/Engine::resolve → NO_COVERAGE
            return exceptionResolver.resolve(expression, message, e);
142
        }
143
    }
144
}

Mutations

68

1.1
Location : process
Killed by : none
replaced boolean return with true for pro/verron/officestamper/core/Engine::process → SURVIVED
Covering tests

76

1.1
Location : process
Killed by : none
removed call to pro/verron/officestamper/api/TraceabilityReporter::onResolution → SURVIVED
Covering tests

81

1.1
Location : process
Killed by : none
replaced boolean return with true for pro/verron/officestamper/core/Engine::process → SURVIVED
Covering tests

84

1.1
Location : process
Killed by : pro.verron.officestamper.test.ProcessorDisplayIfTest.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.ProcessorDisplayIfTest]/[test-template:conditionalDisplayOfTableRowsTest(pro.verron.officestamper.test.utils.ContextFactory)]/[test-template-invocation:#2]
replaced boolean return with false for pro/verron/officestamper/core/Engine::process → KILLED

90

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

100

1.1
Location : buildExpressionState
Killed by : pro.verron.officestamper.test.RegressionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.RegressionTests]/[test-template:test52(pro.verron.officestamper.test.RegressionTests$Conditions, java.lang.String)]/[test-template-invocation:#3]
removed call to org/springframework/expression/spel/ExpressionState::pushActiveContextObject → KILLED

101

1.1
Location : buildExpressionState
Killed by : pro.verron.officestamper.test.RegressionTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.RegressionTests]/[test-template:test52(pro.verron.officestamper.test.RegressionTests$Conditions, java.lang.String)]/[test-template-invocation:#3]
removed call to org/springframework/expression/spel/ExpressionState::enterScope → KILLED

103

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

118

1.1
Location : resolve
Killed by : none
replaced return value with null for pro/verron/officestamper/core/Engine::resolve → NO_COVERAGE

127

1.1
Location : resolve
Killed by : none
removed call to pro/verron/officestamper/api/TraceabilityReporter::onResolution → SURVIVED
Covering tests

131

1.1
Location : resolve
Killed by : pro.verron.officestamper.test.DefaultTests.[engine:junit-jupiter]/[class:pro.verron.officestamper.test.DefaultTests]/[test-template:features(pro.verron.officestamper.api.OfficeStamperConfiguration, java.lang.Object, org.docx4j.openpackaging.packages.WordprocessingMLPackage, java.lang.String)]/[test-template-invocation:#11]
replaced return value with null for pro/verron/officestamper/core/Engine::resolve → KILLED

137

1.1
Location : resolve
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]
replaced return value with null for pro/verron/officestamper/core/Engine::resolve → KILLED

141

1.1
Location : resolve
Killed by : none
replaced return value with null for pro/verron/officestamper/core/Engine::resolve → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.25.5 support