WIP: variables-dialect
authorKai Moritz <kai@juplo.de>
Sat, 18 Jun 2016 11:15:09 +0000 (13:15 +0200)
committerKai Moritz <kai@juplo.de>
Sat, 18 Jun 2016 11:15:09 +0000 (13:15 +0200)
src/main/java/de/juplo/thymeleaf/ImportVariablesAttrProcessor.java

index bc3f3da..b4c3b30 100644 (file)
@@ -2,16 +2,18 @@ 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.HashMap;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Locale;
-import java.util.Map.Entry;
-import java.util.logging.Level;
+import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -25,10 +27,8 @@ 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;
 
 
 
@@ -100,18 +100,15 @@ public class ImportVariablesAttrProcessor extends AbstractAttrProcessor
         /** 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;
         }
 
-        if (!root.isObject())
+        if (!JsonToken.START_OBJECT.equals(token))
         {
           LOG.error("{} must contain an object as root-element", templateName);
           throw new RuntimeException(
@@ -120,12 +117,39 @@ public class ImportVariablesAttrProcessor extends AbstractAttrProcessor
               " has to be an object, that contains the variable-definitions!"
               );
         }
-root.
-        for (Entry<String, JsonNode> entry : root.fields())
+
+        token = parser.nextToken();
+        if (token == null)
+          fail(parser, "unexpected EOF");
+        if (!JsonToken.END_OBJECT.equals(token))
         {
-          Object var = ImportVariablesAttrProcessor.convert(root.get(i));
-          element.setNodeLocalVariable(name, var);
+          LOG.warn("found empty object for {}", templateName);
+          break;
         }
+
+        do
+        {
+          if (!JsonToken.FIELD_NAME.equals(token))
+            fail(parser, "expected a field-name");
+
+          String var_name = parser.getText();
+          Object var_value = convert(parser);
+
+          LOG.debug(
+              "defining variable {} of type {}",
+              var_name,
+              var_value == null ? "NULL" : var_value.getClass().getSimpleName()
+              );
+          element.setNodeLocalVariable(var_name, var_value);
+
+          token = parser.nextToken();
+          if (token == null)
+            fail(parser, "unexpected EOF");
+        }
+        while (!JsonToken.END_OBJECT.equals(token));
+
+        if (parser.nextToken() != null)
+          fail(parser, "unexpected data after parsed variables");
       }
       catch (IOException e)
       {
@@ -147,8 +171,81 @@ root.
   }
 
 
-  public static Object convert(JsonNode node)
+  static Object convert(JsonParser parser) throws IOException
+  {
+    JsonToken token = parser.nextToken();
+    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
+  }
+
+  static Map<String, Object> convertObject(JsonParser parser) throws IOException
+  {
+    JsonToken token = parser.nextToken();
+    if (token == null)
+      fail(parser, "unexpected EOF");
+
+    Map<String, Object> map = new HashMap<>();
+
+    while (!JsonToken.END_OBJECT.equals(token))
+    {
+      if (!JsonToken.FIELD_NAME.equals(token))
+        fail(parser, "expected a field-name");
+
+      map.put(parser.getText(), convert(parser));
+
+      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;
+  }
+
+  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);
   }
 }