io.vertx.codetrans.lang.kotlin.KotlinCodeWriter Maven / Gradle / Ivy
package io.vertx.codetrans.lang.kotlin;
import com.sun.source.tree.LambdaExpressionTree;
import io.vertx.codegen.type.*;
import io.vertx.codetrans.*;
import io.vertx.codetrans.expression.*;
import io.vertx.codegen.type.ApiTypeInfo;
import io.vertx.codegen.type.ClassTypeInfo;
import io.vertx.codegen.type.EnumTypeInfo;
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.CodeWriter;
import io.vertx.codetrans.MethodSignature;
import io.vertx.codetrans.TypeArg;
import io.vertx.codetrans.expression.*;
import io.vertx.codetrans.statement.StatementModel;
import kotlin.collections.CollectionsKt;
import javax.lang.model.element.TypeElement;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author Sergey Mashkov
*/
public class KotlinCodeWriter extends CodeWriter {
private static final Map BASIC_TYPES = new HashMap<>();
static {
BASIC_TYPES.put(java.lang.Byte.class.getName(), "Byte");
BASIC_TYPES.put(byte.class.getName(), "Byte");
BASIC_TYPES.put(java.lang.Short.class.getName(), "Short");
BASIC_TYPES.put(short.class.getName(), "Short");
BASIC_TYPES.put(java.lang.Integer.class.getName(), "Int");
BASIC_TYPES.put(int.class.getName(), "Int");
BASIC_TYPES.put(java.lang.Long.class.getName(), "Long");
BASIC_TYPES.put(long.class.getName(), "Long");
BASIC_TYPES.put(java.lang.Float.class.getName(), "Float");
BASIC_TYPES.put(float.class.getName(), "Float");
BASIC_TYPES.put(java.lang.Double.class.getName(), "Double");
BASIC_TYPES.put(double.class.getName(), "Double");
BASIC_TYPES.put(java.lang.Character.class.getName(), "Char");
BASIC_TYPES.put(char.class.getName(), "Char");
BASIC_TYPES.put(java.lang.Boolean.class.getName(), "Boolean");
BASIC_TYPES.put(boolean.class.getName(), "Boolean");
}
private int jsonLevel = 0;
public KotlinCodeWriter(CodeBuilder builder) {
super(builder);
}
@Override
public KotlinCodeBuilder getBuilder() {
return (KotlinCodeBuilder) super.getBuilder();
}
@Override
public void renderStringLiteral(List> parts) {
append('"');
for (Object part : parts) {
if (part instanceof ExpressionModel) {
append("${");
((ExpressionModel) part).render(this);
append("}");
} else {
renderChars(part.toString());
}
}
append('"');
}
public void renderChars(String value) {
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
switch (c) {
case '\b':
append("\\b");
break;
case '\f':
append("\\u000c");
break;
case '\n':
append("\\n");
break;
case '\t':
append("\\t");
break;
case '\r':
append("\\r");
break;
case '"':
append("\\\"");
break;
case '\\':
append("\\\\");
break;
case '$':
append("\\$");
default:
if (c < 32 || c > 126) {
String s = Integer.toHexString(c).toUpperCase();
while (s.length() < 4) {
s = "0" + s;
}
append("\\u").append(s);
} else {
append(c);
}
}
}
}
@Override
public void renderNewList() {
append("mutableListOf()");
}
@Override
public void renderNewMap() {
append("mutableMapOf()");
}
@Override
public void renderThis() {
append("this");
}
@Override
public void renderMethodReference(ExpressionModel expression, MethodSignature signature) {
append("{ ");
List arguments = new ArrayList<>();
if (!signature.getParameterTypes().isEmpty()) {
for (int i = 0, m = signature.getParameterTypes().size(); i < m; ++i) {
String name;
if (m == 1) {
name = "it";
} else {
name = "p" + Integer.toString(i);
}
arguments.add(new IdentifierModel(builder, name, VariableScope.VARIABLE));
}
if (arguments.size() > 1) {
for (int i = 0, m = arguments.size(); i < m; ++i) {
if (i > 0) {
append(", ");
}
append(((IdentifierModel) arguments.get(i)).name);
}
append(" -> ");
}
}
renderMethodInvocation(expression, VoidTypeInfo.INSTANCE, signature, VoidTypeInfo.INSTANCE, Collections.emptyList(), arguments, CollectionsKt.emptyList());
append(" }");
}
@Override
public void renderLongLiteral(String value) {
renderChars(value);
append('L');
}
@Override
public void renderFloatLiteral(String value) {
renderChars(value);
append('f');
}
@Override
public void renderDoubleLiteral(String value) {
renderChars(value);
}
@Override
public void renderBinary(BinaryExpressionModel expression) {
expression.getLeft().render(this);
append(" ");
switch (expression.getOp()) {
case "&":
append("and");
break;
case "|":
append("or");
break;
case "^":
append("xor");
break;
default:
append(expression.getOp());
}
append(" ");
expression.getRight().render(this);
}
@Override
public void renderStatement(StatementModel statement) {
statement.render(this);
append("\n");
}
@Override
public void renderTryCatch(StatementModel tryBlock, StatementModel catchBlock) {
append("try {\n");
indent();
tryBlock.render(this);
unindent();
append("} catch(e: Exception) {\n");
indent();
catchBlock.render(this);
unindent();
append("}\n");
}
@Override
public void renderThrow(String throwableType, ExpressionModel reason) {
append("throw ");
append(throwableType);
append("(");
if (reason != null) {
reason.render(this);
}
append(")");
}
@Override
public void renderSystemOutPrintln(ExpressionModel expression) {
append("println(");
expression.render(this);
append(")");
}
@Override
public void renderSystemErrPrintln(ExpressionModel expression) {
append("System.err.println(");
expression.render(this);
append(")");
}
@Override
public void renderLambda(LambdaExpressionTree.BodyKind bodyKind, List parameterTypes, List parameterNames, CodeModel body) {
append("{");
if (!parameterNames.isEmpty()) {
for (int i = 0; i < parameterNames.size(); i++) {
if (i == 0) {
append(" ");
} else {
append(", ");
}
append(parameterNames.get(i));
}
append(" ->\n");
} else {
append("\n");
}
indent();
body.render(this);
if (bodyKind == LambdaExpressionTree.BodyKind.EXPRESSION) {
append("\n");
}
unindent();
append("}");
}
@Override
public void renderApiType(ApiTypeInfo apiType) {
append(apiType.getSimpleName());
}
@Override
public void renderJavaType(ClassTypeInfo javaType) {
switch (javaType.getKind()) {
case STRING:
append("String");
break;
case VOID:
append("Unit");
break;
case BOXED_PRIMITIVE:
renderBasicType(javaType);
break;
default:
append(javaType.getName());
}
}
private static final Set reservedWords = new HashSet<>(Arrays.asList("object", "class"));
@Override
public void renderIdentifier(String name, VariableScope scope) {
if (reservedWords.contains(name)) {
append("`");
append(name);
append("`");
} else {
append(name);
}
}
public void renderBasicType(TypeInfo type) {
append(BASIC_TYPES.getOrDefault(type.getName(), type.getName()));
}
@Override
public void renderAsyncResultSucceeded(TypeInfo resultType, String name) {
append(name).append(".succeeded()");
}
@Override
public void renderAsyncResultFailed(TypeInfo resultType, String name) {
append(name).append(".failed()");
}
@Override
public void renderAsyncResultCause(TypeInfo resultType, String name) {
append(name).append(".cause()");
}
@Override
public void renderAsyncResultValue(TypeInfo resultType, String name) {
append(name).append(".result()");
}
@Override
public void renderEnumConstant(EnumTypeInfo type, String constant) {
append(type.getSimpleName()).append('.').append(constant);
}
@Override
public void renderListAdd(ExpressionModel list, ExpressionModel value) {
list.render(this);
append(".add(");
value.render(this);
append(")");
}
@Override
public void renderListSize(ExpressionModel list) {
list.render(this);
append(".size");
}
@Override
public void renderListGet(ExpressionModel list, ExpressionModel index) {
list.render(this);
append("[");
index.render(this);
append("]");
}
@Override
public void renderMapGet(ExpressionModel map, ExpressionModel key) {
map.render(this);
append("[");
key.render(this);
append("]");
}
@Override
public void renderMapPut(ExpressionModel map, ExpressionModel key, ExpressionModel value) {
map.render(this);
append("[");
key.render(this);
append("] = ");
value.render(this);
}
@Override
public void renderMapForEach(ExpressionModel map, String keyName, TypeInfo keyType, String valueName, TypeInfo valueType, LambdaExpressionTree.BodyKind bodyKind, CodeModel block) {
append("for ((").append(keyName).append(", ").append(valueName).append(") in ");
map.render(this);
append(") {\n");
indent();
block.render(this);
if (bodyKind == LambdaExpressionTree.BodyKind.EXPRESSION) {
append("\n");
}
unindent();
append("}\n");
}
@Override
public void renderNew(ExpressionModel expression, TypeInfo type, List argumentModels) {
expression.render(this);
append('(');
for (int i = 0; i < argumentModels.size(); i++) {
if (i > 0) {
append(", ");
}
argumentModels.get(i).render(this);
}
append(')');
}
@Override
public void renderInstanceOf(ExpressionModel expression, TypeElement type) {
expression.render(this);
append(" is ");
append(type.getQualifiedName());
}
@Override
public void renderListLiteral(List arguments) {
append("listOf(");
for (int i = 0; i < arguments.size(); ++i) {
if (i > 0) {
append(", ");
}
arguments.get(i).render(this);
}
append(")");
}
@Override
public void renderJsonArrayToString(ExpressionModel expression) {
expression.render(this);
append(".toString()");
}
@Override
public void renderJsonObjectToString(ExpressionModel expression) {
expression.render(this);
append(".toString()");
}
@Override
public void renderJsonArray(JsonArrayLiteralModel jsonArray) {
renderJsonArray(jsonArray.getValues());
}
@Override
public void renderJsonArrayAdd(ExpressionModel expression, ExpressionModel value) {
expression.render(this);
if (value instanceof NullLiteralModel) {
append(".addNull()");
} else {
append(".add(");
value.render(this);
append(")");
}
}
private void jsonEnter() {
if (jsonLevel == 0) {
append("json {\n");
indent();
}
jsonLevel++;
}
private void jsonLeave() {
jsonLevel--;
if (jsonLevel == 0) {
unindent();
append("\n}");
}
}
@Override
public void renderJsonArrayGet(ExpressionModel expression, Class> type, ExpressionModel index) {
expression.render(this);
append(".");
if (type == Object.class) {
append("get(");
} else {
append("get");
append(type.getSimpleName());
}
append("(");
index.render(this);
append(')');
}
private void renderJsonArray(List entries) {
jsonEnter();
append("array(");
for (int i = 0; i < entries.size(); ++i) {
if (i > 0) {
append(", ");
}
entries.get(i).render(this);
}
append(")");
jsonLeave();
}
@Override
public void renderJsonObject(JsonObjectLiteralModel jsonObject) {
jsonEnter();
renderMapStructure("obj", jsonObject.getMembers());
jsonLeave();
}
@Override
public void renderJsonObjectAssign(ExpressionModel expression, String name, ExpressionModel value) {
ArrayList args = new ArrayList<>();
args.add(new StringLiteralModel(getBuilder(), name));
if (value instanceof NullLiteralModel) {
renderMethodInvocation(expression, VoidTypeInfo.INSTANCE, new MethodSignature("putNull", Collections.emptyList(), false, VoidTypeInfo.INSTANCE), VoidTypeInfo.INSTANCE, Collections.emptyList(), args, Collections.emptyList());
} else {
args.add(value);
renderMethodInvocation(expression, VoidTypeInfo.INSTANCE, new MethodSignature("put", Collections.emptyList(), false, VoidTypeInfo.INSTANCE), VoidTypeInfo.INSTANCE, Collections.emptyList(), args, Collections.emptyList());
}
}
@Override
public void renderMethodInvocation(ExpressionModel expression, TypeInfo receiverType, MethodSignature method, TypeInfo returnType, List typeArguments, List argumentModels, List argumentTypes) {
if (!(expression instanceof ThisModel)) {
expression.render(this);
append('.');
}
renderIdentifier(method.getName(), VariableScope.FIELD);
if (typeArguments.size() > 0) {
boolean needed = typeArguments.stream().filter(typeArg -> typeArg == null || !typeArg.resolved).count() > 0;
if (needed) {
append('<');
append(typeArguments.stream().map(ti -> {
if (ti != null) {
return ti.value.getSimpleName();
} else {
return "Any";
}
}).collect(Collectors.joining(", ")));
append('>');
}
}
append('(');
for (int i = 0; i < argumentModels.size(); i++) {
if (i > 0) {
append(", ");
}
argumentModels.get(i).render(this);
}
append(')');
}
@Override
public void renderJsonObjectMemberSelect(ExpressionModel expression, Class> type, String name) {
expression.render(this);
append(".");
if (type == Object.class) {
append("get");
} else {
append("get");
append(type.getSimpleName());
}
append("(");
renderStringLiteral(name);
append(")");
}
@Override
public void renderToDataObject(JsonObjectModel model, ClassTypeInfo type) {
append(type.getSimpleName());
append("(");
model.render(this);
append(")");
}
@Override
public void renderDataObject(DataObjectLiteralModel model) {
append(model.getType().getSimpleName());
append("(");
if (model.getMembers().iterator().hasNext()) {
append("\n");
indent();
int index = 0;
for (Member m : model.getMembers()) {
if (index > 0) {
append(",\n");
}
append(m.getName()).append(" = ");
renderMember(m);
index++;
}
unindent();
}
append(")");
}
@Override
public void renderDataObjectToJson(IdentifierModel model) {
model.render(this);
append(".toJson()");
}
@Override
public void renderDataObjectAssign(ExpressionModel expression, String name, ExpressionModel value) {
renderDataObjectMemberSelect(expression, name);
append(" = ");
value.render(this);
}
@Override
public void renderDataObjectMemberSelect(ExpressionModel expression, String name) {
expression.render(this);
append(".");
renderIdentifier(name, VariableScope.FIELD);
}
@Override
public void renderJsonObjectSize(ExpressionModel expression) {
expression.render(this);
append(".size()");
}
@Override
public void renderJsonArraySize(ExpressionModel expression) {
expression.render(this);
append(".size()");
}
@Override
public void renderMemberSelect(ExpressionModel expression, String identifier) {
expression.render(this);
append('.');
renderIdentifier(identifier, VariableScope.FIELD);
}
private void renderMapStructure(String builderFunctionName, Iterable members) {
List membersList = new ArrayList<>();
CollectionsKt.addAll(membersList, members);
boolean feedLine = membersList.size() > 1;
append(builderFunctionName);
append("(");
if (feedLine) {
append("\n");
}
indent();
int i = 0;
for (Member m : membersList) {
if (i > 0) {
append(",");
if (feedLine) {
append("\n");
}
}
renderStringLiteral(m.getName());
append(" to ");
renderMember(m);
i++;
}
unindent();
if (feedLine) {
append("\n");
}
append(")");
}
private void renderMap(Iterable members) {
renderMapStructure("mapOf", members);
}
private void renderMember(Member m) {
if (m instanceof Member.Single) {
((Member.Single) m).getValue().render(this);
} else if (m instanceof Member.Sequence) {
renderListLiteral(((Member.Sequence) m).getValues());
} else if (m instanceof Member.Entries) {
renderMap(((Member.Entries) m).entries());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy