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.context.IContext;
19 import org.thymeleaf.context.ITemplateContext;
20 import org.thymeleaf.engine.AttributeName;
21 import org.thymeleaf.engine.EngineEventUtils;
22 import org.thymeleaf.exceptions.TemplateProcessingException;
23 import org.thymeleaf.model.IModel;
24 import org.thymeleaf.model.IProcessableElementTag;
25 import org.thymeleaf.processor.element.AbstractAttributeModelProcessor;
26 import org.thymeleaf.processor.element.IElementModelStructureHandler;
27 import org.thymeleaf.standard.expression.IStandardExpression;
28 import org.thymeleaf.standard.expression.IStandardExpressionParser;
29 import org.thymeleaf.standard.expression.StandardExpressions;
30 import org.thymeleaf.templatemode.TemplateMode;
31 import org.thymeleaf.templateresolver.ITemplateResolver;
32 import org.thymeleaf.templateresolver.TemplateResolution;
37 * Retrievs and parses JSON-data and imports the parsed variables as node-local
41 public class ImportVariablesAttrProcessor extends AbstractAttributeModelProcessor
43 private static final Logger LOG =
44 LoggerFactory.getLogger(ImportVariablesAttrProcessor.class);
45 private static final JsonFactory FACTORY = new JsonFactory();
46 private static final String PROPERTY_NAME =
47 ImportVariablesAttrProcessor.class.getCanonicalName() + "_VARIABLES";
49 public static final Pattern PATTERN =
51 "^\\s*(?:(?:(merge)|replace):)?\\s*(?:(\\{.*\\})|(.*))\\s*$",
52 Pattern.DOTALL | Pattern.CASE_INSENSITIVE
54 public static final int ATTR_PRECEDENCE = 200;
57 public ImportVariablesAttrProcessor(
59 final String attribute,
60 final String substitute
77 protected void doProcess(
78 final ITemplateContext context,
80 final AttributeName attributeName,
81 final String attributeValue,
82 final IElementModelStructureHandler handler
85 final IProcessableElementTag element = (IProcessableElementTag) model.get(0);
86 final String parameter =
87 EngineEventUtils.computeAttributeExpression(
93 .execute(context).toString();
95 if (parameter != null && !parameter.trim().isEmpty())
97 LOG.info("ignoring empty parameter");
101 Iterator<Entry<String, Object>> it = null;
103 Matcher matcher = PATTERN.matcher(parameter);
104 boolean merge = false;
106 String resource = parameter;
108 if (matcher.matches())
110 merge = matcher.group(1) != null;
111 json = matcher.group(2);
112 resource = matcher.group(3);
117 LOG.info("parsing parameter as JSON");
118 LOG.debug("parameter: {}", json);
121 it = SimpleMapper.getObjectIterator(FACTORY.createParser(json));
123 catch (IOException e)
125 LOG.error("cannot parse parameter as JSON: {}", json, e.getMessage());
126 throw new RuntimeException(e);
131 LOG.info("retriving {} as JSON-template", resource);
132 TemplateProcessingParameters params =
133 new TemplateProcessingParameters(
136 new IContext() // << We will not execute the template, hence we need no context
139 public VariablesMap<String, Object> getVariables()
141 return new VariablesMap<>();
145 public Locale getLocale()
147 return Locale.getDefault();
151 public void addContextExecutionInfo(String templateName)
156 for (ITemplateResolver t_resolver : context.getConfiguration().getTemplateResolvers())
158 TemplateResolution resolution = t_resolver.resolveTemplate(params);
159 if (resolution == null)
161 // if (!"JSON".equals(resolution.getTemplateMode()))
163 IResourceResolver r_resolver = resolution.getResourceResolver();
165 r_resolver.getResourceAsStream(params, resolution.getResourceName());
171 it = SimpleMapper.getObjectIterator(FACTORY.createParser(is));
174 catch (IOException | IllegalArgumentException e)
176 LOG.error("cannot retreive {} as JSON: {}", parameter, e.getMessage());
177 throw new RuntimeException(e);
183 LOG.error("cannot resolve {} as JSON (not found)!", parameter);
184 throw new RuntimeException("Template not found: " + parameter);
190 Map<String, Object> variables = getVariables(element);
195 Entry<String, Object> variable = it.next();
196 String key = variable.getKey();
197 Object value = variable.getValue();
198 Object existing = variables.get(key);
199 if (existing != null)
201 if (value instanceof String)
203 if (!(existing instanceof String))
206 "cannot merge variable {} of type {} with a string",
210 throw new RuntimeException(
211 "Type-Missmatch for variable " + key
215 String string = ((String)existing).concat((String) value);
216 LOG.info("appending variable to string {}", key);
217 element.setNodeLocalVariable(key, string);
219 else if (value instanceof Map)
221 if (!(existing instanceof Map))
224 "cannot merge variable {} of type {} with a map",
228 throw new RuntimeException(
229 "Type-Missmatch for variable " + key
233 Map map = ((Map)existing);
234 map.putAll((Map) value);
235 LOG.info("merging variable with map {}", key);
236 element.setNodeLocalVariable(key, map);
238 else if (value instanceof List)
240 if (!(existing instanceof List))
243 "cannot merge variable {} of type {} with a list",
247 throw new RuntimeException(
248 "Type-Missmatch for variable " + key
252 List list = ((List)existing);
253 list.addAll((List) value);
254 LOG.info("appending contents of variable to list {}", key);
255 element.setNodeLocalVariable(key, list);
260 "variable {} is of unexpected type {}", key, value.getClass()
262 throw new RuntimeException(
263 "Found variable of unexpected type: " + key
269 LOG.info("adding new variable {}", key);
270 element.setNodeLocalVariable(key, value);
277 Entry<String, Object> variable = it.next();
278 String key = variable.getKey();
279 Object value = variable.getValue();
280 LOG.info("adding variable {}", key);
281 variables.put(key, value);
282 element.setNodeLocalVariable(key, value);
285 catch (IllegalArgumentException e)
287 LOG.error("cannot parse {} as JSON: {}", parameter, e.getMessage());
288 throw new RuntimeException(e);
291 element.removeAttribute(name);
293 return ProcessorResult.OK;
297 Map<String, Object> getVariables(Node node)
302 Map<String, Object> variables =
303 (Map<String, Object>)parent.getNodeProperty(PROPERTY_NAME);
305 if (variables != null)
308 parent = parent.getParent();
310 while (parent != null);
312 Map<String, Object> variables = new HashMap<>();
313 node.setNodeProperty(PROPERTY_NAME, variables);