WIP
[maven-thymeleaf-skin] / src / main / java / de / juplo / thymeleaf / ImportVariablesAttrProcessor.java
index bc3f3da..fe2725e 100644 (file)
@@ -2,33 +2,19 @@ package de.juplo.thymeleaf;
 
 
 import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonLocation;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.IOException;
 import java.io.InputStream;
 import java.time.format.DateTimeFormatter;
-import java.util.Locale;
-import java.util.Map.Entry;
-import java.util.logging.Level;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Configurable;
-import org.springframework.context.MessageSource;
-import org.thymeleaf.Arguments;
-import org.thymeleaf.Configuration;
-import org.thymeleaf.TemplateProcessingParameters;
-import org.thymeleaf.context.IContext;
-import org.thymeleaf.dom.Element;
-import org.thymeleaf.processor.ProcessorResult;
-import org.thymeleaf.processor.attr.AbstractAttrProcessor;
-import org.thymeleaf.resourceresolver.IResourceResolver;
-import org.thymeleaf.standard.expression.IStandardExpression;
-import org.thymeleaf.templateresolver.ITemplateResolver;
-import org.thymeleaf.templateresolver.TemplateResolution;
-import org.thymeleaf.util.StringUtils;
 
 
 
@@ -36,16 +22,8 @@ import org.thymeleaf.util.StringUtils;
  *
  * @author kai
  */
-@Configurable
-public class ImportVariablesAttrProcessor extends AbstractAttrProcessor
+public class ImportVariablesAttrProcessor
 {
-  public static final int ATTR_PRECEDENCE = 200;
-  public static final String ATTR_VAR_NAME =
-      JuploDialect.DIALECT_PREFIX + ":var";
-  public static final String ATTR_LOCALE_NAME =
-      JuploDialect.DIALECT_PREFIX + ":locale";
-  public static final String DEFAULT_VAR_NAME = "crumb";
-
   private static final Logger LOG =
       LoggerFactory.getLogger(ImportVariablesAttrProcessor.class);
   private static final DateTimeFormatter FORMATTER =
@@ -54,101 +32,114 @@ public class ImportVariablesAttrProcessor extends AbstractAttrProcessor
   private static final JsonFactory FACTORY = new JsonFactory();
 
 
-  @Autowired
-  MessageSource messageSource;
-  @Autowired
-  Locale defaultLocale;
-
-
-  public ImportVariablesAttrProcessor()
+  public Object processAttribute(InputStream is)
   {
-    super("crumb");
-  }
-
-
-  @Override
-  public final ProcessorResult processAttribute(
-      final Arguments arguments,
-      final Element element,
-      final String name
-      )
-  {
-    Configuration config = arguments.getConfiguration();
-    String templateName = element.getAttributeValue(name);
-
-    TemplateProcessingParameters params =
-        new TemplateProcessingParameters(
-            config,
-            templateName,
-            (IContext)null // << We will not execute the template, hence we need no context
-            );
-
-    for (ITemplateResolver t_resolver : config.getTemplateResolvers())
-    {
-      TemplateResolution resolution = t_resolver.resolveTemplate(params);
-      if (resolution == null)
-        continue;
-      if (!"JSON".equals(resolution.getTemplateMode()))
-        continue;
-      IResourceResolver r_resolver = resolution.getResourceResolver();
-      InputStream is = r_resolver.getResourceAsStream(params, templateName);
-      if (is == null)
-        continue;
-
       try
       {
         /** Read the JSON and create the variables */
         JsonParser parser = FACTORY.createParser(is);
 
-        JsonToken root = parser.nextToken();
+        JsonToken token = parser.nextToken();
 
-
-        JsonNode root = MAPPER.readTree(is);
-
-        if (root == null)
+        if (token == null)
         {
-          LOG.warn("found empty content for {}", templateName);
-          break;
+          LOG.warn("empty input-stream");
+          return null;
         }
 
-        if (!root.isObject())
-        {
-          LOG.error("{} must contain an object as root-element", templateName);
-          throw new RuntimeException(
-              "The root-element of " +
-              templateName +
-              " has to be an object, that contains the variable-definitions!"
-              );
-        }
-root.
-        for (Entry<String, JsonNode> entry : root.fields())
-        {
-          Object var = ImportVariablesAttrProcessor.convert(root.get(i));
-          element.setNodeLocalVariable(name, var);
-        }
+        Object result = convert(parser);
+
+        if (parser.nextToken() != null)
+          fail(parser, "unexpected data after parsed variables");
+
+        return result;
       }
       catch (IOException e)
       {
-        LOG.error("cannot parse {} as JSON: {}", templateName, e.getMessage());
+        LOG.error("cannot parse input-stream as JSON: {}", e.getMessage());
         throw new RuntimeException(e);
       }
-    }
+  }
 
-    element.removeAttribute(name);
 
-    return ProcessorResult.OK;
-  }
+  static Object convert(JsonParser parser) throws IOException
+  {
+    JsonToken token = parser.getCurrentToken();
+    if (token == null)
+      fail(parser, "unexpected EOF");
+
+    switch (token)
+    {
+      case VALUE_STRING:       return parser.getText();
+      case VALUE_NUMBER_INT:   return parser.getIntValue();
+      case VALUE_NUMBER_FLOAT: return parser.getDoubleValue();
+      case START_OBJECT:       return convertObject(parser);
+      case START_ARRAY:        return convertArray(parser);
+      case VALUE_TRUE:         return Boolean.TRUE;
+      case VALUE_FALSE:        return Boolean.FALSE;
+      case VALUE_NULL:         return null;
+    }
 
+    fail(parser, "unexpected token " + token.toString());
+    return null; // << Will never be reached, because fail always throws an exception
+  }
 
-  @Override
-  public int getPrecedence()
+  static Map<String, Object> convertObject(JsonParser parser) throws IOException
   {
-    return ATTR_PRECEDENCE;
+    JsonToken token = parser.nextToken();
+    if (token == null)
+      fail(parser, "unexpected EOF");
+
+    Map<String, Object> map = new LinkedHashMap<>();
+
+    while (!JsonToken.END_OBJECT.equals(token))
+    {
+      if (!JsonToken.FIELD_NAME.equals(token))
+        fail(parser, "expected a field-name");
+
+      String name = parser.getText();
+      parser.nextToken();
+      Object value = convert(parser);
+      map.put(name, value);
+
+      token = parser.nextToken();
+      if (token == null)
+        fail(parser, "unexpected EOF");
+    }
+
+    return map;
   }
 
+  static List<Object> convertArray(JsonParser parser) throws IOException
+  {
+    JsonToken token = parser.nextToken();
+    if (token == null)
+      fail(parser, "unexpected EOF");
+
+    List<Object> list = new LinkedList<>();
+
+    while (!JsonToken.END_ARRAY.equals(token))
+    {
+      list.add(convert(parser));
+
+      token = parser.nextToken();
+      if (token == null)
+        fail(parser, "unexpected EOF");
+    }
+
+    return list;
+  }
 
-  public static Object convert(JsonNode node)
+  static void fail(JsonParser parser, String message)
   {
-    return null;
+    JsonLocation location = parser.getCurrentLocation();
+    LOG.error(
+        "{} at char-offset {} (line {}, column {})",
+        message,
+        location.getCharOffset(),
+        location.getLineNr(),
+        location.getColumnNr()
+        );
+    throw new RuntimeException("Cannot parse JSON: " + message);
   }
 }