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.exceptions.TemplateProcessingException;
20 import org.thymeleaf.processor.element.AbstractAttributeModelProcessor;
21 import org.thymeleaf.standard.expression.IStandardExpression;
22 import org.thymeleaf.standard.expression.IStandardExpressionParser;
23 import org.thymeleaf.standard.expression.StandardExpressions;
24 import org.thymeleaf.templatemode.TemplateMode;
25 import org.thymeleaf.templateresolver.ITemplateResolver;
26 import org.thymeleaf.templateresolver.TemplateResolution;
31 * Retrievs and parses JSON-data and imports the parsed variables as node-local
35 public class ImportVariablesAttrProcessor extends AbstractAttributeModelProcessor
37 private static final Logger LOG =
38 LoggerFactory.getLogger(ImportVariablesAttrProcessor.class);
39 private static final JsonFactory FACTORY = new JsonFactory();
40 private static final String PROPERTY_NAME =
41 ImportVariablesAttrProcessor.class.getCanonicalName() + "_VARIABLES";
43 public static final Pattern PATTERN =
45 "^\\s*(?:(?:(merge)|replace):)?\\s*(?:(\\{.*\\})|(.*))\\s*$",
46 Pattern.DOTALL | Pattern.CASE_INSENSITIVE
48 public static final int ATTR_PRECEDENCE = 200;
51 public ImportVariablesAttrProcessor(
53 final String attribute,
54 final String substitute
71 public final ProcessorResult processAttribute(
72 final Arguments arguments,
73 final Element element,
77 Configuration config = arguments.getConfiguration();
79 Configuration configuration = arguments.getConfiguration();
81 String parameter = element.getAttributeValue(name);
84 IStandardExpressionParser parser =
85 StandardExpressions.getExpressionParser(configuration);
86 IStandardExpression expression =
87 parser.parseExpression(configuration, arguments, parameter);
88 parameter = (String)expression.execute(configuration, arguments);
90 catch (TemplateProcessingException e) { }
92 if (parameter != null && !parameter.trim().isEmpty())
94 LOG.info("ignoring empty parameter");
95 return ProcessorResult.OK;
98 Iterator<Entry<String, Object>> it = null;
100 Matcher matcher = PATTERN.matcher(parameter);
101 boolean merge = false;
103 String resource = parameter;
105 if (matcher.matches())
107 merge = matcher.group(1) != null;
108 json = matcher.group(2);
109 resource = matcher.group(3);
114 LOG.info("parsing parameter as JSON");
115 LOG.debug("parameter: {}", json);
118 it = SimpleMapper.getObjectIterator(FACTORY.createParser(json));
120 catch (IOException e)
122 LOG.error("cannot parse parameter as JSON: {}", json, e.getMessage());
123 throw new RuntimeException(e);
128 LOG.info("retriving {} as JSON-template", resource);
129 TemplateProcessingParameters params =
130 new TemplateProcessingParameters(
133 new IContext() // << We will not execute the template, hence we need no context
136 public VariablesMap<String, Object> getVariables()
138 return new VariablesMap<>();
142 public Locale getLocale()
144 return Locale.getDefault();
148 public void addContextExecutionInfo(String templateName)
153 for (ITemplateResolver t_resolver : config.getTemplateResolvers())
155 TemplateResolution resolution = t_resolver.resolveTemplate(params);
156 if (resolution == null)
158 if (!"JSON".equals(resolution.getTemplateMode()))
160 IResourceResolver r_resolver = resolution.getResourceResolver();
162 r_resolver.getResourceAsStream(params, resolution.getResourceName());
168 it = SimpleMapper.getObjectIterator(FACTORY.createParser(is));
171 catch (IOException | IllegalArgumentException e)
173 LOG.error("cannot retreive {} as JSON: {}", parameter, e.getMessage());
174 throw new RuntimeException(e);
180 LOG.error("cannot resolve {} as JSON (not found)!", parameter);
181 throw new RuntimeException("Template not found: " + parameter);
187 Map<String, Object> variables = getVariables(element);
192 Entry<String, Object> variable = it.next();
193 String key = variable.getKey();
194 Object value = variable.getValue();
195 Object existing = variables.get(key);
196 if (existing != null)
198 if (value instanceof String)
200 if (!(existing instanceof String))
203 "cannot merge variable {} of type {} with a string",
207 throw new RuntimeException(
208 "Type-Missmatch for variable " + key
212 String string = ((String)existing).concat((String) value);
213 LOG.info("appending variable to string {}", key);
214 element.setNodeLocalVariable(key, string);
216 else if (value instanceof Map)
218 if (!(existing instanceof Map))
221 "cannot merge variable {} of type {} with a map",
225 throw new RuntimeException(
226 "Type-Missmatch for variable " + key
230 Map map = ((Map)existing);
231 map.putAll((Map) value);
232 LOG.info("merging variable with map {}", key);
233 element.setNodeLocalVariable(key, map);
235 else if (value instanceof List)
237 if (!(existing instanceof List))
240 "cannot merge variable {} of type {} with a list",
244 throw new RuntimeException(
245 "Type-Missmatch for variable " + key
249 List list = ((List)existing);
250 list.addAll((List) value);
251 LOG.info("appending contents of variable to list {}", key);
252 element.setNodeLocalVariable(key, list);
257 "variable {} is of unexpected type {}", key, value.getClass()
259 throw new RuntimeException(
260 "Found variable of unexpected type: " + key
266 LOG.info("adding new variable {}", key);
267 element.setNodeLocalVariable(key, value);
274 Entry<String, Object> variable = it.next();
275 String key = variable.getKey();
276 Object value = variable.getValue();
277 LOG.info("adding variable {}", key);
278 variables.put(key, value);
279 element.setNodeLocalVariable(key, value);
282 catch (IllegalArgumentException e)
284 LOG.error("cannot parse {} as JSON: {}", parameter, e.getMessage());
285 throw new RuntimeException(e);
288 element.removeAttribute(name);
290 return ProcessorResult.OK;
294 Map<String, Object> getVariables(Node node)
299 Map<String, Object> variables =
300 (Map<String, Object>)parent.getNodeProperty(PROPERTY_NAME);
302 if (variables != null)
305 parent = parent.getParent();
307 while (parent != null);
309 Map<String, Object> variables = new HashMap<>();
310 node.setNodeProperty(PROPERTY_NAME, variables);
316 public int getPrecedence()
318 return ATTR_PRECEDENCE;