package io.vertx.codegen.generators.dataobjecthelper;

import io.vertx.codegen.Generator;
import io.vertx.codegen.DataObjectModel;
import io.vertx.codegen.PropertyInfo;
import io.vertx.codegen.annotations.DataObject;
import io.vertx.codegen.annotations.ModuleGen;
import io.vertx.codegen.type.ClassKind;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.time.Instant;
import java.util.*;

Author:Julien Viet
/** * @author <a href="mailto:julien@julienviet.com">Julien Viet</a> */
public class DataObjectHelperGen extends Generator<DataObjectModel> { public DataObjectHelperGen() { kinds = Collections.singleton("dataObject"); name = "data_object_converters"; } @Override public Collection<Class<? extends Annotation>> annotations() { return Arrays.asList(DataObject.class, ModuleGen.class); } @Override public String filename(DataObjectModel model) { if (model.isClass() && model.getGenerateConverter()) { return model.getFqn() + "Converter.java"; } return null; } @Override public String render(DataObjectModel model, int index, int size, Map<String, Object> session) { StringWriter buffer = new StringWriter(); PrintWriter writer = new PrintWriter(buffer); String visibility= model.isPublicConverter() ? "public" : ""; String simpleName = model.getType().getSimpleName(); boolean inheritConverter = model.getInheritConverter(); writer.print("package " + model.getType().getPackageName() + ";\n"); writer.print("\n"); writer.print("import io.vertx.core.json.JsonObject;\n"); writer.print("import io.vertx.core.json.JsonArray;\n"); writer.print("import java.time.Instant;\n"); writer.print("import java.time.format.DateTimeFormatter;\n"); writer.print("\n"); writer.print("/**\n"); writer.print(" * Converter for {@link " + model.getType() + "}.\n"); writer.print(" * NOTE: This class has been automatically generated from the {@link " + model.getType() + "} original class using Vert.x codegen.\n"); writer.print(" */\n"); writer.print(visibility + " class " + simpleName + "Converter {\n"); writer.print("\n"); generateFromson(visibility, inheritConverter, model, writer); writer.print("\n"); generateToJson(visibility, inheritConverter, model, writer); writer.print("}\n"); return buffer.toString(); } private void generateToJson(String visibility, boolean inheritConverter, DataObjectModel model, PrintWriter writer) { String simpleName = model.getType().getSimpleName(); writer.print(" " + visibility + " static void toJson(" + simpleName + " obj, JsonObject json) {\n"); writer.print(" toJson(obj, json.getMap());\n"); writer.print(" }\n"); writer.print("\n"); writer.print(" " + visibility + " static void toJson(" + simpleName + " obj, java.util.Map<String, Object> json) {\n"); model.getPropertyMap().values().forEach(prop -> { if ((prop.isDeclared() || inheritConverter) && prop.getGetterMethod() != null && prop.isJsonifiable()) { ClassKind propKind = prop.getType().getKind(); if (propKind.basic) { if (propKind == ClassKind.STRING) { genPropToJson("", "", prop, writer); } else { switch (prop.getType().getSimpleName()) { case "char": case "Character": genPropToJson("Character.toString(", ")", prop, writer); break; default: genPropToJson("", "", prop, writer); } } } else { switch (propKind) { case API: if (prop.getType().getName().equals("io.vertx.core.buffer.Buffer")) { genPropToJson("java.util.Base64.getEncoder().encodeToString(", ".getBytes())", prop, writer); } break; case ENUM: genPropToJson("", ".name()", prop, writer); break; case JSON_OBJECT: case JSON_ARRAY: case OBJECT: genPropToJson("", "", prop, writer); break; case DATA_OBJECT: genPropToJson("", ".toJson()", prop, writer); break; case OTHER: if (prop.getType().getName().equals(Instant.class.getName())) { genPropToJson("DateTimeFormatter.ISO_INSTANT.format(", ")", prop, writer); } break; } } } }); writer.print(" }\n"); } private void genPropToJson(String before, String after, PropertyInfo prop, PrintWriter writer) { String indent = " "; if (prop.isList() || prop.isSet()) { writer.print(indent + "if (obj." + prop.getGetterMethod() + "() != null) {\n"); writer.print(indent + " JsonArray array = new JsonArray();\n"); writer.print(indent + " obj." + prop.getGetterMethod() + "().forEach(item -> array.add(" + before + "item" + after + "));\n"); writer.print(indent + " json.put(\"" + prop.getName() + "\", array);\n"); writer.print(indent + "}\n"); } else if (prop.isMap()) { writer.print(indent + "if (obj." + prop.getGetterMethod() + "() != null) {\n"); writer.print(indent + " JsonObject map = new JsonObject();\n"); writer.print(indent + " obj." + prop.getGetterMethod() + "().forEach((key, value) -> map.put(key, " + before + "value" + after + "));\n"); writer.print(indent + " json.put(\"" + prop.getName() + "\", map);\n"); writer.print(indent + "}\n"); } else { String sp = ""; if (prop.getType().getKind() != ClassKind.PRIMITIVE) { sp = " "; writer.print(indent + "if (obj." + prop.getGetterMethod() + "() != null) {\n"); } writer.print(indent + sp + "json.put(\"" + prop.getName() + "\", " + before + "obj." + prop.getGetterMethod() + "()" + after + ");\n"); if (prop.getType().getKind() != ClassKind.PRIMITIVE) { writer.print(indent + "}\n"); } } } private void generateFromson(String visibility, boolean inheritConverter, DataObjectModel model, PrintWriter writer) { writer.print(" " + visibility + " static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, " + model.getType().getSimpleName() + " obj) {\n"); writer.print(" for (java.util.Map.Entry<String, Object> member : json) {\n"); writer.print(" switch (member.getKey()) {\n"); model.getPropertyMap().values().forEach(prop -> { if (prop.isDeclared() || inheritConverter) { ClassKind propKind = prop.getType().getKind(); if (propKind.basic) { if (propKind == ClassKind.STRING) { genPropFromJson("String", "(String)", "", prop, writer); } else { switch (prop.getType().getSimpleName()) { case "boolean": case "Boolean": genPropFromJson("Boolean", "(Boolean)", "", prop, writer); break; case "byte": case "Byte": genPropFromJson("Number", "((Number)", ").byteValue()", prop, writer); break; case "short": case "Short": genPropFromJson("Number", "((Number)", ").shortValue()", prop, writer); break; case "int": case "Integer": genPropFromJson("Number", "((Number)", ").intValue()", prop, writer); break; case "long": case "Long": genPropFromJson("Number", "((Number)", ").longValue()", prop, writer); break; case "float": case "Float": genPropFromJson("Number", "((Number)", ").floatValue()", prop, writer); break; case "double": case "Double": genPropFromJson("Number", "((Number)", ").doubleValue()", prop, writer); break; case "char": case "Character": genPropFromJson("String", "((String)", ").charAt(0)", prop, writer); break; } } } else { switch (propKind) { case API: if (prop.getType().getName().equals("io.vertx.core.buffer.Buffer")) { genPropFromJson("String", "io.vertx.core.buffer.Buffer.buffer(java.util.Base64.getDecoder().decode((String)", "))", prop, writer); } break; case JSON_OBJECT: genPropFromJson("JsonObject", "((JsonObject)", ").copy()", prop, writer); break; case JSON_ARRAY: genPropFromJson("JsonArray", "((JsonArray)", ").copy()", prop, writer); break; case DATA_OBJECT: genPropFromJson("JsonObject", "new " + prop.getType().getName() + "((JsonObject)", ")", prop, writer); break; case ENUM: genPropFromJson("String", prop.getType().getName() + ".valueOf((String)", ")", prop, writer); break; case OBJECT: genPropFromJson("Object", "", "", prop, writer); break; case OTHER: if (prop.getType().getName().equals(Instant.class.getName())) { genPropFromJson("String", "Instant.from(DateTimeFormatter.ISO_INSTANT.parse((String)", "))", prop, writer); } break; default: } } } }); writer.print(" }\n"); writer.print(" }\n"); writer.print(" }\n"); } private void genPropFromJson(String cast, String before, String after, PropertyInfo prop, PrintWriter writer) { String indent = " "; writer.print(indent + "case \"" + prop.getName() + "\":\n"); if (prop.isList() || prop.isSet()) { writer.print(indent + " if (member.getValue() instanceof JsonArray) {\n"); if (prop.isSetter()) { String coll = prop.isList() ? "java.util.ArrayList" : "java.util.LinkedHashSet"; writer.print(indent + " " + coll + "<" + prop.getType().getName() + "> list = new " + coll + "<>();\n"); writer.print(indent + " ((Iterable<Object>)member.getValue()).forEach( item -> {\n"); writer.print(indent + " if (item instanceof " + cast + ")\n"); writer.print(indent + " list.add(" + before + "item" + after + ");\n"); writer.print(indent + " });\n"); writer.print(indent + " obj." + prop.getSetterMethod() + "(list);\n"); } else if (prop.isAdder()) { writer.print(indent + " ((Iterable<Object>)member.getValue()).forEach( item -> {\n"); writer.print(indent + " if (item instanceof " + cast + ")\n"); writer.print(indent + " obj." + prop.getAdderMethod() + "(" + before + "item" + after + ");\n"); writer.print(indent + " });\n"); } writer.print(indent + " }\n"); } else if (prop.isMap()) { writer.print(indent + " if (member.getValue() instanceof JsonObject) {\n"); if (prop.isAdder()) { writer.print(indent + " ((Iterable<java.util.Map.Entry<String, Object>>)member.getValue()).forEach(entry -> {\n"); writer.print(indent + " if (entry.getValue() instanceof " + cast + ")\n"); writer.print(indent + " obj." + prop.getAdderMethod() + "(entry.getKey(), " + before + "entry.getValue()" + after + ");\n"); writer.print(indent + " });\n"); } else if (prop.isSetter()) { writer.print(indent + " java.util.Map<String, " + prop.getType().getName() + "> map = new java.util.LinkedHashMap<>();\n"); writer.print(indent + " ((Iterable<java.util.Map.Entry<String, Object>>)member.getValue()).forEach(entry -> {\n"); writer.print(indent + " if (entry.getValue() instanceof " + cast + ")\n"); writer.print(indent + " map.put(entry.getKey(), " + before + "entry.getValue()" + after + ");\n"); writer.print(indent + " });\n"); writer.print(indent + " obj." + prop.getSetterMethod() + "(map);\n"); } writer.print(indent + " }\n"); } else { if (prop.isSetter()) { writer.print(indent + " if (member.getValue() instanceof " + cast + ") {\n"); writer.print(indent + " obj." + prop.getSetterMethod()+ "(" + before + "member.getValue()" + after + ");\n"); writer.print(indent + " }\n"); } } writer.print(indent + " break;\n"); } }