1 package de.juplo.thymeleaf;
4 import com.fasterxml.jackson.core.JsonFactory;
5 import com.fasterxml.jackson.core.JsonLocation;
6 import com.fasterxml.jackson.core.JsonParser;
7 import com.fasterxml.jackson.core.JsonToken;
8 import com.fasterxml.jackson.databind.ObjectMapper;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.time.format.DateTimeFormatter;
12 import java.util.LinkedHashMap;
13 import java.util.LinkedList;
14 import java.util.List;
15 import java.util.Locale;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19 import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.beans.factory.annotation.Configurable;
21 import org.springframework.context.MessageSource;
22 import org.thymeleaf.Arguments;
23 import org.thymeleaf.Configuration;
24 import org.thymeleaf.TemplateProcessingParameters;
25 import org.thymeleaf.context.IContext;
26 import org.thymeleaf.context.VariablesMap;
27 import org.thymeleaf.dom.Element;
28 import org.thymeleaf.processor.ProcessorResult;
29 import org.thymeleaf.processor.attr.AbstractAttrProcessor;
30 import org.thymeleaf.resourceresolver.IResourceResolver;
31 import org.thymeleaf.templateresolver.ITemplateResolver;
32 import org.thymeleaf.templateresolver.TemplateResolution;
41 public class ImportVariablesAttrProcessor extends AbstractAttrProcessor
43 public static final int ATTR_PRECEDENCE = 200;
44 public static final String ATTR_VAR_NAME =
45 JuploDialect.DIALECT_PREFIX + ":var";
46 public static final String ATTR_LOCALE_NAME =
47 JuploDialect.DIALECT_PREFIX + ":locale";
48 public static final String DEFAULT_VAR_NAME = "crumb";
50 private static final Logger LOG =
51 LoggerFactory.getLogger(ImportVariablesAttrProcessor.class);
52 private static final DateTimeFormatter FORMATTER =
53 DateTimeFormatter.ofPattern("dd.MM");
54 private static final ObjectMapper MAPPER = new ObjectMapper();
55 private static final JsonFactory FACTORY = new JsonFactory();
59 MessageSource messageSource;
64 public ImportVariablesAttrProcessor()
71 public final ProcessorResult processAttribute(
72 final Arguments arguments,
73 final Element element,
77 Configuration config = arguments.getConfiguration();
78 String templateName = element.getAttributeValue(name);
80 TemplateProcessingParameters params =
81 new TemplateProcessingParameters(
84 new IContext() // << We will not execute the template, hence we need no context
87 public VariablesMap<String, Object> getVariables()
89 return new VariablesMap<>();
93 public Locale getLocale()
95 return Locale.getDefault();
99 public void addContextExecutionInfo(String templateName)
104 for (ITemplateResolver t_resolver : config.getTemplateResolvers())
106 TemplateResolution resolution = t_resolver.resolveTemplate(params);
107 if (resolution == null)
109 if (!"JSON".equals(resolution.getTemplateMode()))
111 IResourceResolver r_resolver = resolution.getResourceResolver();
113 r_resolver.getResourceAsStream(params, resolution.getResourceName());
119 /** Read the JSON and create the variables */
120 JsonParser parser = FACTORY.createParser(is);
122 JsonToken token = parser.nextToken();
126 LOG.warn("found empty content for {}", templateName);
130 if (!JsonToken.START_OBJECT.equals(token))
132 LOG.error("{} must contain an object as root-element", templateName);
133 throw new RuntimeException(
134 "The root-element of " +
136 " has to be an object, that contains the variable-definitions!"
140 token = parser.nextToken();
142 fail(parser, "unexpected EOF");
143 if (JsonToken.END_OBJECT.equals(token))
145 LOG.warn("found empty object for {}", templateName);
151 if (!JsonToken.FIELD_NAME.equals(token))
152 fail(parser, "expected a field-name");
154 String var_name = parser.getText();
156 Object var_value = convert(parser);
159 "defining variable {} of type {}",
161 var_value == null ? "NULL" : var_value.getClass().getSimpleName()
163 element.setNodeLocalVariable(var_name, var_value);
165 token = parser.nextToken();
167 fail(parser, "unexpected EOF");
169 while (!JsonToken.END_OBJECT.equals(token));
171 if (parser.nextToken() != null)
172 fail(parser, "unexpected data after parsed variables");
174 catch (IOException e)
176 LOG.error("cannot parse {} as JSON: {}", templateName, e.getMessage());
177 throw new RuntimeException(e);
181 element.removeAttribute(name);
183 return ProcessorResult.OK;
188 public int getPrecedence()
190 return ATTR_PRECEDENCE;
194 static Object convert(JsonParser parser) throws IOException
196 JsonToken token = parser.getCurrentToken();
198 fail(parser, "unexpected EOF");
202 case VALUE_STRING: return parser.getText();
203 case VALUE_NUMBER_INT: return parser.getIntValue();
204 case VALUE_NUMBER_FLOAT: return parser.getDoubleValue();
205 case START_OBJECT: return convertObject(parser);
206 case START_ARRAY: return convertArray(parser);
207 case VALUE_TRUE: return Boolean.TRUE;
208 case VALUE_FALSE: return Boolean.FALSE;
209 case VALUE_NULL: return null;
212 fail(parser, "unexpected token " + token.toString());
213 return null; // << Will never be reached, because fail always throws an exception
216 static Map<String, Object> convertObject(JsonParser parser) throws IOException
218 JsonToken token = parser.nextToken();
220 fail(parser, "unexpected EOF");
222 Map<String, Object> map = new LinkedHashMap<>();
224 while (!JsonToken.END_OBJECT.equals(token))
226 if (!JsonToken.FIELD_NAME.equals(token))
227 fail(parser, "expected a field-name");
229 String name = parser.getText();
231 Object value = convert(parser);
232 map.put(name, value);
234 token = parser.nextToken();
236 fail(parser, "unexpected EOF");
242 static List<Object> convertArray(JsonParser parser) throws IOException
244 JsonToken token = parser.nextToken();
246 fail(parser, "unexpected EOF");
248 List<Object> list = new LinkedList<>();
250 while (!JsonToken.END_ARRAY.equals(token))
252 list.add(convert(parser));
254 token = parser.nextToken();
256 fail(parser, "unexpected EOF");
262 static void fail(JsonParser parser, String message)
264 JsonLocation location = parser.getCurrentLocation();
266 "{} at char-offset {} (line {}, column {})",
268 location.getCharOffset(),
269 location.getLineNr(),
270 location.getColumnNr()
272 throw new RuntimeException("Cannot parse JSON: " + message);