1 package de.juplo.thymeleaf;
4 import com.fasterxml.jackson.core.JsonFactory;
5 import de.juplo.jackson.SimpleMapper;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.util.HashMap;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.Locale;
13 import java.util.Map.Entry;
14 import java.util.regex.Matcher;
15 import java.util.regex.Pattern;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
18 import org.thymeleaf.Arguments;
19 import org.thymeleaf.Configuration;
20 import org.thymeleaf.TemplateProcessingParameters;
21 import org.thymeleaf.context.IContext;
22 import org.thymeleaf.context.VariablesMap;
23 import org.thymeleaf.dom.Element;
24 import org.thymeleaf.dom.Node;
25 import org.thymeleaf.processor.ProcessorResult;
26 import org.thymeleaf.processor.attr.AbstractAttrProcessor;
27 import org.thymeleaf.resourceresolver.IResourceResolver;
28 import org.thymeleaf.templateresolver.ITemplateResolver;
29 import org.thymeleaf.templateresolver.TemplateResolution;
34 * Retrievs and parses JSON-data and imports the parsed variables as node-local
38 public class ImportVariablesAttrProcessor extends AbstractAttrProcessor
40 private static final Logger LOG =
41 LoggerFactory.getLogger(ImportVariablesAttrProcessor.class);
42 private static final JsonFactory FACTORY = new JsonFactory();
43 private static final String PROPERTY_NAME =
44 ImportVariablesAttrProcessor.class.getCanonicalName() + "_VARIABLES";
46 public static final Pattern PATTERN =
48 "^\\s*(?:(?:(merge)|replace):)?\\s*(?:(\\{.*\\})|(.*))\\s*$",
49 Pattern.DOTALL | Pattern.CASE_INSENSITIVE
51 public static final int ATTR_PRECEDENCE = 200;
54 public ImportVariablesAttrProcessor()
61 public final ProcessorResult processAttribute(
62 final Arguments arguments,
63 final Element element,
67 Configuration config = arguments.getConfiguration();
68 String parameter = element.getAttributeValue(name);
70 Iterator<Entry<String, Object>> it = null;
72 Matcher matcher = PATTERN.matcher(parameter);
73 boolean merge = false;
75 String resource = parameter;
77 if (matcher.matches())
79 merge = matcher.group(1) != null;
80 json = matcher.group(2);
81 resource = matcher.group(3);
86 LOG.info("parsing parameter as JSON");
87 LOG.debug("parameter: {}", json);
90 it = SimpleMapper.getObjectIterator(FACTORY.createParser(json));
94 LOG.error("cannot parse parameter as JSON: {}", json, e.getMessage());
95 throw new RuntimeException(e);
100 LOG.info("retriving {} as JSON-template", resource);
101 TemplateProcessingParameters params =
102 new TemplateProcessingParameters(
105 new IContext() // << We will not execute the template, hence we need no context
108 public VariablesMap<String, Object> getVariables()
110 return new VariablesMap<>();
114 public Locale getLocale()
116 return Locale.getDefault();
120 public void addContextExecutionInfo(String templateName)
125 for (ITemplateResolver t_resolver : config.getTemplateResolvers())
127 TemplateResolution resolution = t_resolver.resolveTemplate(params);
128 if (resolution == null)
130 if (!"JSON".equals(resolution.getTemplateMode()))
132 IResourceResolver r_resolver = resolution.getResourceResolver();
134 r_resolver.getResourceAsStream(params, resolution.getResourceName());
140 it = SimpleMapper.getObjectIterator(FACTORY.createParser(is));
143 catch (IOException | IllegalArgumentException e)
145 LOG.error("cannot retreive {} as JSON: {}", parameter, e.getMessage());
146 throw new RuntimeException(e);
152 LOG.error("cannot resolve {} as JSON (not found)!", parameter);
153 throw new RuntimeException("Template not found: " + parameter);
159 Map<String, Object> variables = getVariables(element);
164 Entry<String, Object> variable = it.next();
165 String key = variable.getKey();
166 Object value = variable.getValue();
167 Object existing = variables.get(key);
168 if (existing != null)
170 if (value instanceof String)
172 if (!(existing instanceof String))
175 "cannot merge variable {} of type {} with a string",
179 throw new RuntimeException(
180 "Type-Missmatch for variable " + key
184 String string = ((String)existing).concat((String) value);
185 LOG.info("appending variable to string {}", key);
186 element.setNodeLocalVariable(key, string);
188 else if (value instanceof Map)
190 if (!(existing instanceof Map))
193 "cannot merge variable {} of type {} with a map",
197 throw new RuntimeException(
198 "Type-Missmatch for variable " + key
202 Map map = ((Map)existing);
203 map.putAll((Map) value);
204 LOG.info("merging variable with map {}", key);
205 element.setNodeLocalVariable(key, map);
207 else if (value instanceof List)
209 if (!(existing instanceof List))
212 "cannot merge variable {} of type {} with a list",
216 throw new RuntimeException(
217 "Type-Missmatch for variable " + key
221 List list = ((List)existing);
222 list.addAll((List) value);
223 LOG.info("appending contents of variable to list {}", key);
224 element.setNodeLocalVariable(key, list);
229 "variable {} is of unexpected type {}", key, value.getClass()
231 throw new RuntimeException(
232 "Found variable of unexpected type: " + key
238 LOG.info("adding new variable {}", key);
239 element.setNodeLocalVariable(key, value);
246 Entry<String, Object> variable = it.next();
247 String key = variable.getKey();
248 Object value = variable.getValue();
249 LOG.info("adding variable {}", key);
250 variables.put(key, value);
251 element.setNodeLocalVariable(key, value);
254 catch (IllegalArgumentException e)
256 LOG.error("cannot parse {} as JSON: {}", parameter, e.getMessage());
257 throw new RuntimeException(e);
260 element.removeAttribute(name);
262 return ProcessorResult.OK;
266 Map<String, Object> getVariables(Node node)
271 Map<String, Object> variables =
272 (Map<String, Object>)parent.getNodeProperty(PROPERTY_NAME);
274 if (variables != null)
277 parent = parent.getParent();
279 while (parent != null);
281 Map<String, Object> variables = new HashMap<>();
282 node.setNodeProperty(PROPERTY_NAME, variables);
288 public int getPrecedence()
290 return ATTR_PRECEDENCE;