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.exceptions.TemplateProcessingException;
26 import org.thymeleaf.processor.ProcessorResult;
27 import org.thymeleaf.processor.attr.AbstractAttrProcessor;
28 import org.thymeleaf.resourceresolver.IResourceResolver;
29 import org.thymeleaf.standard.expression.IStandardExpression;
30 import org.thymeleaf.standard.expression.IStandardExpressionParser;
31 import org.thymeleaf.standard.expression.StandardExpressions;
32 import org.thymeleaf.templateresolver.ITemplateResolver;
33 import org.thymeleaf.templateresolver.TemplateResolution;
38 * Retrievs and parses JSON-data and imports the parsed variables as node-local
42 public class ImportVariablesAttrProcessor extends AbstractAttrProcessor
44 private static final Logger LOG =
45 LoggerFactory.getLogger(ImportVariablesAttrProcessor.class);
46 private static final JsonFactory FACTORY = new JsonFactory();
47 private static final String PROPERTY_NAME =
48 ImportVariablesAttrProcessor.class.getCanonicalName() + "_VARIABLES";
50 public static final Pattern PATTERN =
52 "^\\s*(?:(?:(merge)|replace):)?\\s*(?:(\\{.*\\})|(.*))\\s*$",
53 Pattern.DOTALL | Pattern.CASE_INSENSITIVE
55 public static final int ATTR_PRECEDENCE = 200;
58 public ImportVariablesAttrProcessor()
65 public final ProcessorResult processAttribute(
66 final Arguments arguments,
67 final Element element,
71 Configuration config = arguments.getConfiguration();
73 Configuration configuration = arguments.getConfiguration();
75 String parameter = element.getAttributeValue(name);
78 IStandardExpressionParser parser =
79 StandardExpressions.getExpressionParser(configuration);
80 IStandardExpression expression =
81 parser.parseExpression(configuration, arguments, parameter);
82 parameter = (String)expression.execute(configuration, arguments);
84 catch (TemplateProcessingException e) { }
86 if (parameter != null && !parameter.trim().isEmpty())
88 LOG.info("ignoring empty parameter");
89 return ProcessorResult.OK;
92 Iterator<Entry<String, Object>> it = null;
94 Matcher matcher = PATTERN.matcher(parameter);
95 boolean merge = false;
97 String resource = parameter;
99 if (matcher.matches())
101 merge = matcher.group(1) != null;
102 json = matcher.group(2);
103 resource = matcher.group(3);
108 LOG.info("parsing parameter as JSON");
109 LOG.debug("parameter: {}", json);
112 it = SimpleMapper.getObjectIterator(FACTORY.createParser(json));
114 catch (IOException e)
116 LOG.error("cannot parse parameter as JSON: {}", json, e.getMessage());
117 throw new RuntimeException(e);
122 LOG.info("retriving {} as JSON-template", resource);
123 TemplateProcessingParameters params =
124 new TemplateProcessingParameters(
127 new IContext() // << We will not execute the template, hence we need no context
130 public VariablesMap<String, Object> getVariables()
132 return new VariablesMap<>();
136 public Locale getLocale()
138 return Locale.getDefault();
142 public void addContextExecutionInfo(String templateName)
147 for (ITemplateResolver t_resolver : config.getTemplateResolvers())
149 TemplateResolution resolution = t_resolver.resolveTemplate(params);
150 if (resolution == null)
152 if (!"JSON".equals(resolution.getTemplateMode()))
154 IResourceResolver r_resolver = resolution.getResourceResolver();
156 r_resolver.getResourceAsStream(params, resolution.getResourceName());
162 it = SimpleMapper.getObjectIterator(FACTORY.createParser(is));
165 catch (IOException | IllegalArgumentException e)
167 LOG.error("cannot retreive {} as JSON: {}", parameter, e.getMessage());
168 throw new RuntimeException(e);
174 LOG.error("cannot resolve {} as JSON (not found)!", parameter);
175 throw new RuntimeException("Template not found: " + parameter);
181 Map<String, Object> variables = getVariables(element);
186 Entry<String, Object> variable = it.next();
187 String key = variable.getKey();
188 Object value = variable.getValue();
189 Object existing = variables.get(key);
190 if (existing != null)
192 if (value instanceof String)
194 if (!(existing instanceof String))
197 "cannot merge variable {} of type {} with a string",
201 throw new RuntimeException(
202 "Type-Missmatch for variable " + key
206 String string = ((String)existing).concat((String) value);
207 LOG.info("appending variable to string {}", key);
208 element.setNodeLocalVariable(key, string);
210 else if (value instanceof Map)
212 if (!(existing instanceof Map))
215 "cannot merge variable {} of type {} with a map",
219 throw new RuntimeException(
220 "Type-Missmatch for variable " + key
224 Map map = ((Map)existing);
225 map.putAll((Map) value);
226 LOG.info("merging variable with map {}", key);
227 element.setNodeLocalVariable(key, map);
229 else if (value instanceof List)
231 if (!(existing instanceof List))
234 "cannot merge variable {} of type {} with a list",
238 throw new RuntimeException(
239 "Type-Missmatch for variable " + key
243 List list = ((List)existing);
244 list.addAll((List) value);
245 LOG.info("appending contents of variable to list {}", key);
246 element.setNodeLocalVariable(key, list);
251 "variable {} is of unexpected type {}", key, value.getClass()
253 throw new RuntimeException(
254 "Found variable of unexpected type: " + key
260 LOG.info("adding new variable {}", key);
261 element.setNodeLocalVariable(key, value);
268 Entry<String, Object> variable = it.next();
269 String key = variable.getKey();
270 Object value = variable.getValue();
271 LOG.info("adding variable {}", key);
272 variables.put(key, value);
273 element.setNodeLocalVariable(key, value);
276 catch (IllegalArgumentException e)
278 LOG.error("cannot parse {} as JSON: {}", parameter, e.getMessage());
279 throw new RuntimeException(e);
282 element.removeAttribute(name);
284 return ProcessorResult.OK;
288 Map<String, Object> getVariables(Node node)
293 Map<String, Object> variables =
294 (Map<String, Object>)parent.getNodeProperty(PROPERTY_NAME);
296 if (variables != null)
299 parent = parent.getParent();
301 while (parent != null);
303 Map<String, Object> variables = new HashMap<>();
304 node.setNodeProperty(PROPERTY_NAME, variables);
310 public int getPrecedence()
312 return ATTR_PRECEDENCE;