All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.smallrye.mutiny.vertx.codegen.lang.CodeGenHelper Maven / Gradle / Ivy

The newest version!
package io.smallrye.mutiny.vertx.codegen.lang;

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.vertx.MutinyHelper;
import io.smallrye.mutiny.vertx.TypeArg;
import io.smallrye.mutiny.vertx.codegen.MutinyGenerator;
import io.vertx.codegen.*;
import io.vertx.codegen.doc.Tag;
import io.vertx.codegen.type.*;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Promise;

import javax.lang.model.element.Element;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import static io.smallrye.mutiny.vertx.codegen.MutinyGenerator.ID;
import static io.vertx.codegen.type.ClassKind.*;
import static java.util.stream.Collectors.joining;

public class CodeGenHelper {

    private CodeGenHelper() {
        // avoid direct instantiation
    }

    public static boolean hasParentClass(ClassModel model) {
        TypeInfo concreteSuperType = model.getConcreteSuperType();
        if (concreteSuperType == null) {
            return false;
        }
        if (concreteSuperType.isParameterized()) {
            return !MutinyGenerator.IGNORED_TYPES.contains(concreteSuperType.getRaw().getName());
        } else {
            return !MutinyGenerator.IGNORED_TYPES.contains(concreteSuperType.getName());
        }
    }

    public static MethodKind methodKind(MethodInfo methodInfo) {
        List params = methodInfo.getParams();
        int lastParamIndex = params.size() - 1;
        if (lastParamIndex >= 0) {
            TypeInfo lastParamType = params.get(lastParamIndex).getType();
            if (lastParamType.getKind() == ClassKind.HANDLER) {
                TypeInfo typeArg = ((ParameterizedTypeInfo) lastParamType).getArgs().get(0);
                if (typeArg.getKind() == ClassKind.ASYNC_RESULT) {
                    return MethodKind.CALLBACK;
                } else {
                    return MethodKind.HANDLER;
                }
            }
        }
        return MethodKind.OTHER;
    }

    public static String genTypeName(TypeInfo type) {
        return genTypeName(type, false);
    }

    public static String genTranslatedTypeName(TypeInfo type) {
        return genTypeName(type, true);
    }

    protected static boolean isImported(TypeInfo type) {
        switch (type.getKind()) {
            case JSON_OBJECT:
            case JSON_ARRAY:
            case ASYNC_RESULT:
            case HANDLER:
            case LIST:
            case SET:
            case BOXED_PRIMITIVE:
            case STRING:
            case VOID:
            case FUNCTION:
                return true;
            default:
                return false;
        }
    }

    private static String expandInnerTypes(String type) {
        return type.replace('$', '.');
    }

    public static String genTypeName(TypeInfo type, boolean translate) {
        if (!translate && type.isParameterized() && type.getRaw().getName().equals(Uni.class.getName())) {
            ParameterizedTypeInfo parameterizedType = (ParameterizedTypeInfo) type;
            return expandInnerTypes("io.vertx.core.Future<" + genTypeName(parameterizedType.getArg(0), translate) + ">");
        }

        if (type.isParameterized()) {
            ParameterizedTypeInfo pt = (ParameterizedTypeInfo) type;
            if (translate && type.getRaw().getName().equals(Handler.class.getName()) && pt.getArg(0).getName().startsWith(Promise.class.getName())) {
                TypeInfo arg = ((ParameterizedTypeInfo) pt.getArg(0)).getArg(0);
                return expandInnerTypes(Uni.class.getName() + "<" + genTypeName(arg, true) + ">");
            }
            return expandInnerTypes(genTypeName(pt.getRaw(), translate) + pt.getArgs().stream().map(a -> genTypeName(a, translate))
                    .collect(joining(", ", "<", ">")));
        } else {
            if (type.getKind() == ClassKind.API && translate) {
                // TODO Is this still a thing? Future is not API anymore
                if (type.getName().equals(Future.class.getName())) {
                    return expandInnerTypes(Uni.class.getName());
                }
                return expandInnerTypes(type.translateName(ID));
            } else if (translate && type.getName().equals(Future.class.getName())) {
                return expandInnerTypes(Uni.class.getName());
            } else {
                if (isImported(type)) {
                    return expandInnerTypes(type.getSimpleName());
                } else {
                    return expandInnerTypes(type.getName());
                }
            }
        }
    }

    private static boolean isSameType(TypeInfo type, MethodInfo method) {
        ClassKind kind = type.getKind();
        if (type.isDataObjectHolder() || kind.basic || kind.json || kind == ENUM || kind == OTHER || kind == THROWABLE
                || kind == VOID) {
            return true;
        } else if (kind == OBJECT) {
            if (type.isVariable()) {
                return !isReified((TypeVariableInfo) type, method);
            } else {
                return true;
            }
        } else if (type.isParameterized()) {
            ParameterizedTypeInfo parameterizedTypeInfo = (ParameterizedTypeInfo) type;
            if (kind == LIST || kind == SET || kind == ASYNC_RESULT) {
                return isSameType(parameterizedTypeInfo.getArg(0), method);
            } else if (kind == MAP) {
                return isSameType(parameterizedTypeInfo.getArg(1), method);
            } else if (kind == HANDLER) {
                return isSameType(parameterizedTypeInfo.getArg(0), method);
            } else if (kind == FUNCTION) {
                return isSameType(parameterizedTypeInfo.getArg(0), method)
                        && isSameType(parameterizedTypeInfo.getArg(1), method);
            }
        }
        return false;
    }

    public static String genConvParam(Map> methodTypeArgMap, TypeInfo type,
                                      MethodInfo method, String expr) {
        if (type.isParameterized() && (type.getRaw().getName().equals(Multi.class.getName()))) {
            ParameterizedTypeInfo parameterizedType = (ParameterizedTypeInfo) type;
            String adapterFunction = "obj -> " + genConvParam(methodTypeArgMap, parameterizedType.getArg(0), method, "obj");
            return "io.smallrye.mutiny.vertx.ReadStreamSubscriber.asReadStream(" + expr + ", " + adapterFunction + ").resume()";
        } else if (type.isParameterized()
                && (type.getRaw().getName().equals(Future.class.getName()))) {
            ParameterizedTypeInfo parameterizedType = (ParameterizedTypeInfo) type;
            TypeInfo arg = parameterizedType.getArg(0);
            if (arg.getKind() == API) {
                return "io.smallrye.mutiny.vertx.UniHelper.toFuture(" + expr + ".map(r -> r.getDelegate()))";
            } else {
                return "io.smallrye.mutiny.vertx.UniHelper.toFuture(" + expr + ")";
            }
        } else if (type.isParameterized() && type.getRaw().getName().equals(Supplier.class.getName())) {
            ParameterizedTypeInfo parameterizedType = (ParameterizedTypeInfo) type;
            if (parameterizedType.getArg(0).getRaw().getName().equals(Future.class.getName())) {
                return "() -> io.smallrye.mutiny.vertx.UniHelper.toFuture(" + expr + ".get())";
            }
        }

        ClassKind kind = type.getKind();
        if (isSameType(type, method)) {
            return expr;
        } else if (kind == OBJECT) {
            if (type.isVariable()) {
                String typeArg = genTypeArg((TypeVariableInfo) type, method);
                if (typeArg != null) {
                    return typeArg + ".<" + type.getName() + ">unwrap(" + expr + ")";
                }
            }
            return expr;
        } else if (kind == API) {
            return expr + ".getDelegate()";
        } else if (kind == CLASS_TYPE) {
            return MutinyHelper.class.getName() + ".unwrap(" + expr + ")";
        } else if (type.isParameterized()) {
            ParameterizedTypeInfo parameterizedTypeInfo = (ParameterizedTypeInfo) type;
            if (kind == HANDLER) {
                TypeInfo eventType = parameterizedTypeInfo.getArg(0);
                ClassKind eventKind = eventType.getKind();
                if (eventKind == ASYNC_RESULT) {
                    TypeInfo resultType = ((ParameterizedTypeInfo) eventType).getArg(0);
                    return "new io.smallrye.mutiny.vertx.DelegatingHandler<>(" + expr + ", ar -> ar.map(event -> " + genConvReturn(methodTypeArgMap, resultType, method, "event") + "))";
                } else if (eventType.isParameterized() && eventType.getRaw().getName().equals(Promise.class.getName())) {
                    return "new Handler<" + genTypeName(eventType) + ">() {\n" +
                            "          public void handle(" + genTypeName(eventType) + " event) {\n" +
                            "            " + expr + ".subscribe().with(it -> event.complete(it), failure -> event.fail(failure));\n" +
                            "          }\n" +
                            "      }";
                } else {
                    return "new io.smallrye.mutiny.vertx.DelegatingHandler<>(" + expr + ", event -> " + genConvReturn(methodTypeArgMap, eventType, method, "event") + ")";
                }
            } else if (kind == FUNCTION) {
                TypeInfo argType = parameterizedTypeInfo.getArg(0);
                TypeInfo retType = parameterizedTypeInfo.getArg(1);
                String translatedReturnedType = genTranslatedTypeName(retType);
                if (translatedReturnedType.startsWith("io.smallrye.mutiny.Uni<")) {
                    return generatingAFunctionReturningAFuture(methodTypeArgMap, method, expr, argType, retType);
                } else {
                    return "new java.util.function.Function<" + genTypeName(argType) + "," + retType.getName() + ">() {\n" +
                            "      public " + genTypeName(retType) + " apply(" + genTypeName(argType) + " arg) {\n" +
                            "        " + translatedReturnedType + " ret = " + expr + ".apply("
                            + genConvReturn(methodTypeArgMap, argType, method, "arg") + ");\n" +
                            "        return " + genConvParam(methodTypeArgMap, retType, method, "ret") + ";\n" +
                            "      }\n" +
                            "    }";
                }

            } else if (kind == LIST || kind == SET) {
                return expr + ".stream().map(elt -> " + genConvParam(methodTypeArgMap, parameterizedTypeInfo.getArg(0),
                        method, "elt")
                        + ").collect(java.util.stream.Collectors.to" + type.getRaw().getSimpleName() + "())";
            } else if (kind == MAP) {
                return expr + ".entrySet().stream().collect(java.util.stream.Collectors.toMap(e -> e.getKey(), e -> "
                        + genConvParam(methodTypeArgMap, parameterizedTypeInfo.getArg(1), method, "e.getValue()")
                        + "))";
            } else if (kind == FUTURE) {
                ParameterizedTypeInfo futureType = (ParameterizedTypeInfo) type;
                return expr + ".map(val -> " + genConvParam(methodTypeArgMap, futureType.getArg(0), method, "val") + ")";
            }
        }
        return expr;
    }

    private static String generatingAFunctionReturningAFuture(
            Map> methodTypeArgMap, MethodInfo method, String expr,
            TypeInfo argType, TypeInfo retType) {

        // If the retType if a Future and X is of type API, we need to unwrap the mutiny type to the bare type
        // (call getDelegate())
        boolean appendDelegate = false;
        if (retType.getKind() == FUTURE) {
            ParameterizedTypeInfo ret = (ParameterizedTypeInfo) retType;
            appendDelegate = ret.getArg(0).getKind() == API;
        }

        if (appendDelegate) {
            return "new java.util.function.Function<" + genTypeName(argType) + "," + retType.getName() + ">() {\n" +
                    "      public " + genTypeName(retType) + " apply(" + genTypeName(argType) + " arg) {\n" +
                    "            return io.smallrye.mutiny.vertx.UniHelper.toFuture(\n" +
                    "                 " + expr + ".apply(" + genConvReturn(methodTypeArgMap, argType, method, "arg") + ").map(x -> x.getDelegate())\n" +
                    "            );\n" +
                    "         }\n" +
                    "     }";
        }
        return "new java.util.function.Function<" + genTypeName(argType) + "," + retType.getName() + ">() {\n" +
                "      public " + genTypeName(retType) + " apply(" + genTypeName(argType) + " arg) {\n" +
                "            return io.smallrye.mutiny.vertx.UniHelper.toFuture(\n" +
                "                 " + expr + ".apply(" + genConvReturn(methodTypeArgMap, argType, method, "arg") + ")\n" +
                "            );\n" +
                "         }\n" +
                "     }";

    }

    private static boolean isReified(TypeVariableInfo typeVar, MethodInfo method) {
        if (typeVar.isClassParam()) {
            return true;
        } else {
            TypeArgExpression typeArg = method.resolveTypeArg(typeVar);
            return typeArg != null && typeArg.isClassType();
        }
    }

    private static String genTypeArg(TypeVariableInfo typeVar, MethodInfo method) {
        if (typeVar.isClassParam()) {
            return "__typeArg_" + typeVar.getParam().getIndex();
        } else {
            TypeArgExpression typeArg = method.resolveTypeArg(typeVar);
            if (typeArg != null) {
                if (typeArg.isClassType()) {
                    return TypeArg.class.getName() + ".of(" + typeArg.getParam().getName() + ")";
                } else {
                    return typeArg.getParam().getName() + ".__typeArg_" + typeArg.getIndex();
                }
            }
        }
        return null;
    }

    public static void genTypeArg(TypeInfo arg, MethodInfo method, int depth, StringBuilder sb) {
        ClassKind argKind = arg.getKind();
        if (argKind == API) {
            sb.append("new TypeArg<").append(arg.translateName(ID))
                    .append(">(o").append(depth).append(" -> ");
            sb.append(arg.getRaw().translateName(ID)).append(".newInstance((").append(arg.getRaw()).append(")o")
                    .append(depth);
            if (arg instanceof ParameterizedTypeInfo) {
                ParameterizedTypeInfo parameterizedType = (ParameterizedTypeInfo) arg;
                List args = parameterizedType.getArgs();
                for (int i = 0; i < args.size(); i++) {
                    sb.append(", ");
                    genTypeArg(args.get(i), method, depth + 1, sb);
                }
            }
            sb.append(")");
            sb.append(", o").append(depth).append(" -> o").append(depth).append(".getDelegate())");
        } else {
            String typeArg = "TypeArg.unknown()";
            if (argKind == OBJECT && arg.isVariable()) {
                String resolved = genTypeArg((TypeVariableInfo) arg, method);
                if (resolved != null) {
                    typeArg = resolved;
                }
            }
            sb.append(typeArg);
        }
    }

    private static String genTypeArg(Map> methodTypeArgMap, TypeInfo arg,
                                     MethodInfo method) {
        Map typeArgMap = methodTypeArgMap.get(method);
        if (typeArgMap != null) {
            String typeArgRef = typeArgMap.get(arg);
            if (typeArgRef != null) {
                return typeArgRef;
            }
        }
        StringBuilder sb = new StringBuilder();
        genTypeArg(arg, method, 0, sb);
        return sb.toString();
    }

    private static void fail(String m) {
        //        throw new RuntimeException(m);
    }

    public static String writeReturnStatementForApi(Map> methodTypeArgMap,
                                                    TypeInfo type, MethodInfo method, String expr) {
        StringBuilder tmp = new StringBuilder(type.getRaw().translateName(ID));
        tmp.append(".newInstance((");
        tmp.append(type.getRaw());
        tmp.append(")");
        tmp.append(expr);
        if (type.isParameterized()) {
            ParameterizedTypeInfo parameterizedTypeInfo = (ParameterizedTypeInfo) type;
            for (TypeInfo arg : parameterizedTypeInfo.getArgs()) {
                tmp.append(", ");
                tmp.append(genTypeArg(methodTypeArgMap, arg, method));
            }
        }
        tmp.append(")");
        return tmp.toString();
    }

    public static String genConvReturn(Map> methodTypeArgMap, TypeInfo type,
                                       MethodInfo method, String expr) {
        ClassKind kind = type.getKind();
        if (kind == OBJECT) {
            if (type.isVariable()) {
                String typeArg = genTypeArg((TypeVariableInfo) type, method);
                if (typeArg != null) {
                    return "(" + type.getName() + ")" + typeArg + ".wrap(" + expr + ")";
                }
            }
            return "(" + type.getSimpleName() + ") " + expr;
        } else if (isSameType(type, method)) {
            return expr;
        } else if (kind == API) {
            return writeReturnStatementForApi(methodTypeArgMap, type, method, expr);
        } else if (type.isParameterized()) {
            ParameterizedTypeInfo parameterizedTypeInfo = (ParameterizedTypeInfo) type;
            if (kind == HANDLER) {
                TypeInfo abc = parameterizedTypeInfo.getArg(0);
                if (abc.getKind() == ASYNC_RESULT) {
                    TypeInfo tutu = ((ParameterizedTypeInfo) abc).getArg(0);
                    return "new Handler>() {\n" +
                            "      public void handle(AsyncResult<" + genTranslatedTypeName(tutu) + "> ar) {\n" +
                            "        if (ar.succeeded()) {\n" +
                            "          " + expr + ".handle(io.vertx.core.Future.succeededFuture("
                            + genConvParam(methodTypeArgMap, tutu, method, "ar.result()") + "));\n" +
                            "        } else {\n" +
                            "          " + expr + ".handle(io.vertx.core.Future.failedFuture(ar.cause()));\n" +
                            "        }\n" +
                            "      }\n" +
                            "    }";
                } else {
                    return "new Handler<" + genTranslatedTypeName(abc) + ">() {\n" +
                            "      public void handle(" + genTranslatedTypeName(abc) + " event) {\n" +
                            "          " + expr + ".handle(" + genConvParam(methodTypeArgMap, abc, method, "event")
                            + ");\n" +
                            "      }\n" +
                            "    }";
                }
            } else if (kind == LIST || kind == SET) {
                return expr + ".stream().map(elt -> " + genConvReturn(methodTypeArgMap, parameterizedTypeInfo.getArg(0),
                        method, "elt")
                        + ").collect(java.util.stream.Collectors.to" + type.getRaw().getSimpleName() + "())";
            } else if (kind == MAP) {
                return expr + ".entrySet().stream().collect(Collectors.toMap(_e -> _e.getKey(), _e -> " + genConvReturn(
                        methodTypeArgMap, parameterizedTypeInfo.getArg(1), method, "_e.getValue()") + "))";
            } else if (kind == FUTURE) {
                ParameterizedTypeInfo futureType = (ParameterizedTypeInfo) type;
                return expr + ".map(val -> " + genConvReturn(methodTypeArgMap, futureType.getArg(0), method, "val") + ")";
            }
        }
        return expr;
    }

    static String genOptTypeParamsDecl(ClassTypeInfo type, String deflt) {
        if (type.getParams().size() > 0) {
            return type.getParams().stream().map(TypeParamInfo::getName).collect(joining(",", "<", ">"));
        } else {
            return deflt;
        }
    }

    public static String renderLinkToHtml(Tag.Link link) {
        ClassTypeInfo rawType = link.getTargetType().getRaw();
        if (rawType.getModule() != null) {
            String label = link.getLabel().trim();
            if (rawType.getKind() == ClassKind.API) {
                Element elt = link.getTargetElement();
                String eltKind = elt.getKind().name();
                String ret = "{@link " + rawType.translateName(ID);
                if ("METHOD".equals(eltKind)) {
                    /* todo find a way for translating the complete signature */
                    ret += "#" + elt.getSimpleName().toString();
                }
                if (label.length() > 0) {
                    ret += " " + label;
                }
                ret += "}";
                return ret;
            }
        }
        return "{@link " + rawType.getName() + "}";
    }

    public static String renderLinkToHtml(ClassTypeInfo owner, MethodInfo method) {
        if (owner.getKind() == ClassKind.API) {
            String ret = "{@link " + owner.translateName(ID);
            ret += "#" + method.getName();
            if (!method.getParams().isEmpty()) {
                ret += "(" + method.getParams().stream()
                        .map(p -> {
                            TypeInfo type = p.getType();
                            if (type.getKind() == API) {
                                return type.translateName(ID);
                            } else {
                                return type.getSimpleName();
                            }
                        }).collect(joining(",")) + ")";
            }
            ret += "}";
            return ret;
        }
        return "{@link " + owner.getName() + "}";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy