package io.vertx.codetrans.lang.kotlin;
import com.sun.source.tree.LambdaExpressionTree;
import io.vertx.codegen.type.ApiTypeInfo;
import io.vertx.codegen.type.ClassTypeInfo;
import io.vertx.codegen.type.EnumTypeInfo;
import io.vertx.codegen.type.ParameterizedTypeInfo;
import io.vertx.codegen.type.PrimitiveTypeInfo;
import io.vertx.codegen.type.TypeInfo;
import io.vertx.codegen.type.VoidTypeInfo;
import io.vertx.codetrans.CodeBuilder;
import io.vertx.codetrans.CodeModel;
import io.vertx.codetrans.MethodModel;
import io.vertx.codetrans.RenderMode;
import io.vertx.codetrans.RunnableCompilationUnit;
import io.vertx.codetrans.expression.ApiTypeModel;
import io.vertx.codetrans.expression.DataObjectClassModel;
import io.vertx.codetrans.expression.EnumExpressionModel;
import io.vertx.codetrans.expression.ExpressionModel;
import io.vertx.codetrans.expression.JsonArrayClassModel;
import io.vertx.codetrans.expression.JsonObjectClassModel;
import io.vertx.codetrans.expression.LambdaExpressionModel;
import io.vertx.codetrans.expression.NullLiteralModel;
import io.vertx.codetrans.expression.VariableScope;
import io.vertx.codetrans.statement.StatementModel;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
public class KotlinCodeBuilder implements CodeBuilder {
private TreeSet<String> imports = new TreeSet<>();
KotlinCodeBuilder() {
}
@Override
public KotlinCodeWriter newWriter() {
return new KotlinCodeWriter(this);
}
@Override
public String render(RunnableCompilationUnit unit, RenderMode renderMode) {
KotlinCodeWriter writer = newWriter();
if (renderMode != RenderMode.SNIPPET) {
String foo;
String className = unit.getMain().getClassName();
String pkg;
if (unit.isVerticle()) {
int index = className.lastIndexOf('.');
pkg = className.substring(0, index);
foo = className.substring(index + 1);
} else {
pkg = className;
foo = unit.getMain().getSignature().getName();
}
writer.append("package ").append(pkg).append("\n\n");
for (String i : imports) {
writer.append("import ").append(i).append("\n");
}
writer.append("\n");
switch (renderMode) {
case TEST:
writer.append("object ").append(foo).append(" {\n");
writer.indent();
break;
case EXAMPLE:
writer.append("class ").append(foo).append(" : io.vertx.core.AbstractVerticle() ").append(" {\n");
writer.indent();
break;
}
for (Map.Entry<String, StatementModel> field : unit.getFields().entrySet()) {
field.getValue().render(writer);
writer.append("\n");
}
for (Map.Entry<String, MethodModel> method : unit.getMethods().entrySet()) {
writer.append("fun ").append(method.getKey()).append("(");
List<TypeInfo> types = method.getValue().getSignature().getParameterTypes();
List<String> names = method.getValue().getParameterNames();
int count = Math.min(types.size(), names.size());
for (int i = 0; i < count; ++i) {
String name = names.get(i);
TypeInfo type = types.get(i);
if (i > 0) {
writer.append(", ");
}
writer.append(name).append(": ");
renderType(type, writer);
}
writer.append(") ");
TypeInfo returnType = method.getValue().getSignature().getReturnType();
if (returnType != VoidTypeInfo.INSTANCE) {
writer.append(": ");
renderType(returnType, writer);
}
writer.append("{\n");
writer.indent();
method.getValue().render(writer);
writer.unindent();
writer.append("}\n");
}
switch (renderMode) {
case TEST:
writer.append("fun ").append(unit.getMain().getSignature().getName()).append("() {\n");
writer.indent();
break;
case EXAMPLE:
writer.append("override fun ").append("start").append("() {\n");
writer.indent();
break;
}
}
unit.getMain().render(writer);
if (renderMode != RenderMode.SNIPPET) {
writer.unindent();
writer.append("}\n");
writer.unindent();
writer.append("}\n");
}
return writer.getBuffer().toString();
}
@Override
public DataObjectClassModel dataObjectClass(ClassTypeInfo type) {
addImport(type);
imports.add(type.translatePackageName("kotlin") + ".*");
return CodeBuilder.super.dataObjectClass(type);
}
@Override
public EnumExpressionModel enumType(EnumTypeInfo type) {
addImport(type);
return CodeBuilder.super.enumType(type);
}
@Override
public ApiTypeModel apiType(ApiTypeInfo type) {
addImport(type);
return CodeBuilder.super.apiType(type);
}
@Override
public ExpressionModel asyncResultHandler(LambdaExpressionTree.BodyKind bodyKind, ParameterizedTypeInfo resultType, String resultName, CodeModel body, CodeModel succeededBody, CodeModel failedBody) {
return new LambdaExpressionModel(this, bodyKind, Collections.singletonList(resultType), Collections.singletonList(resultName), body);
}
@Override
public StatementModel variableDecl(VariableScope scope, TypeInfo type, String name, ExpressionModel initializer) {
return StatementModel.render(renderer -> {
switch (scope) {
case PARAMETER:
break;
case VARIABLE:
case FIELD:
case GLOBAL:
renderer.append("var ");
break;
}
renderer.append(name);
if (initializer != null) {
if (initializer instanceof NullLiteralModel) {
renderer.append(": ");
renderType(type, (KotlinCodeWriter) renderer);
renderer.append("? = null");
} else {
renderer.append(" = ");
initializer.render(renderer);
}
} else {
renderer.append(": ");
renderType(type, (KotlinCodeWriter) renderer);
}
});
}
@Override
public StatementModel enhancedForLoop(String variableName, ExpressionModel expression, StatementModel body) {
return StatementModel.render(renderer -> {
renderer.append("for (").append(variableName).append(" in ");
expression.render(renderer);
renderer.append(") {\n");
renderer.indent();
body.render(renderer);
renderer.unindent();
renderer.append("}");
});
}
@Override
public StatementModel forLoop(StatementModel initializer, ExpressionModel condition, ExpressionModel update, StatementModel body) {
return StatementModel.render(renderer -> {
renderer.renderStatement(initializer);
renderer.append("while (");
condition.render(renderer);
renderer.append(") {\n");
renderer.indent();
body.render(renderer);
renderer.append("\n");
update.render(renderer);
renderer.unindent();
renderer.append("}\n");
});
}
@Override
public StatementModel sequenceForLoop(String variableName, ExpressionModel fromValue, ExpressionModel toValue, StatementModel body) {
return StatementModel.render(renderer -> {
renderer.append("for (");
renderer.renderIdentifier(variableName, VariableScope.VARIABLE);
renderer.append(" in ");
fromValue.render(renderer);
renderer.append(" until ");
toValue.render(renderer);
renderer.append(") {\n");
renderer.indent();
body.render(renderer);
renderer.unindent();
renderer.append("\n}\n");
});
}
@Override
public JsonObjectClassModel jsonObjectClassModel() {
imports.add("io.vertx.kotlin.core.json.*");
return CodeBuilder.super.jsonObjectClassModel();
}
@Override
public JsonArrayClassModel jsonArrayClassModel() {
imports.add("io.vertx.kotlin.core.json.*");
return CodeBuilder.super.jsonArrayClassModel();
}
private void renderType(TypeInfo type, KotlinCodeWriter renderer) {
if (type instanceof ApiTypeInfo) {
renderer.renderApiType((ApiTypeInfo) type);
} else if (type instanceof ClassTypeInfo) {
renderer.renderJavaType((ClassTypeInfo) type);
} else if (type instanceof PrimitiveTypeInfo) {
renderer.renderBasicType(type);
} else {
renderer.append(type.getName());
}
}
private void addImport(ClassTypeInfo importedType) {
String fqn = importedType.getName();
imports.add(fqn);
}
}