]> git.basschouten.com Git - openhab-addons.git/blob
e0e5df8094c523b1b03711eb83e5be8454001bf2
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.transform.jinja.internal;
14
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Map.Entry;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.core.transform.TransformationException;
26 import org.openhab.core.transform.TransformationService;
27 import org.osgi.service.component.annotations.Component;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import com.fasterxml.jackson.databind.JsonNode;
32 import com.fasterxml.jackson.databind.ObjectMapper;
33 import com.hubspot.jinjava.Jinjava;
34 import com.hubspot.jinjava.JinjavaConfig;
35 import com.hubspot.jinjava.interpret.FatalTemplateErrorsException;
36
37 /**
38  * <p>
39  * The implementation of {@link TransformationService} which transforms the input by Jinja2 Expressions.
40  *
41  * @author Jochen Klein - Initial contribution
42  *
43  */
44 @NonNullByDefault
45 @Component(property = { "openhab.transform=JINJA" })
46 public class JinjaTransformationService implements TransformationService {
47
48     private final Logger logger = LoggerFactory.getLogger(JinjaTransformationService.class);
49
50     private final JinjavaConfig config = JinjavaConfig.newBuilder().withFailOnUnknownTokens(true).build();
51     private final Jinjava jinjava = new Jinjava(config);
52
53     /**
54      * Transforms the input <code>value</code> by Jinja template.
55      *
56      * @param template Jinja template
57      * @param value String may contain JSON
58      * @throws TransformationException
59      */
60     @Override
61     public @Nullable String transform(String template, String value) throws TransformationException {
62         String transformationResult;
63         Map<String, @Nullable Object> bindings = new HashMap<>();
64
65         logger.debug("about to transform '{}' by the function '{}'", value, template);
66
67         bindings.put("value", value);
68
69         try {
70             JsonNode tree = new ObjectMapper().readTree(value);
71             bindings.put("value_json", toObject(tree));
72         } catch (IOException e) {
73             // ok, then value_json is null...
74         }
75
76         try {
77             transformationResult = jinjava.render(template, bindings);
78         } catch (FatalTemplateErrorsException e) {
79             throw new TransformationException("An error occurred while transformation. " + e.getMessage(), e);
80         }
81
82         logger.debug("transformation resulted in '{}'", transformationResult);
83
84         return transformationResult;
85     }
86
87     private static @Nullable Object toObject(JsonNode node) {
88         switch (node.getNodeType()) {
89             case ARRAY: {
90                 List<@Nullable Object> result = new ArrayList<>();
91                 for (JsonNode el : node) {
92                     result.add(toObject(el));
93                 }
94                 return result;
95             }
96             case NUMBER:
97                 return node.decimalValue();
98             case OBJECT: {
99                 Map<String, @Nullable Object> result = new HashMap<>();
100                 Iterator<Entry<String, JsonNode>> it = node.fields();
101                 while (it.hasNext()) {
102                     Entry<String, JsonNode> field = it.next();
103                     result.put(field.getKey(), toObject(field.getValue()));
104                 }
105                 return result;
106             }
107             case STRING:
108                 return node.asText();
109             case BOOLEAN:
110                 return node.asBoolean();
111             case NULL:
112             default:
113                 return null;
114         }
115     }
116 }