io.vertx.serviceproxy.sockjs.generator.SockjsServiceProxyJSGenerator Maven / Gradle / Ivy
The newest version!
package io.vertx.serviceproxy.sockjs.generator;
import io.vertx.codegen.*;
import io.vertx.codegen.doc.Token;
import io.vertx.codegen.format.CamelCase;
import io.vertx.codegen.format.SnakeCase;
import io.vertx.codegen.type.*;
import io.vertx.codegen.writer.CodeWriter;
import io.vertx.serviceproxy.generator.model.ProxyMethodInfo;
import io.vertx.serviceproxy.generator.model.ProxyModel;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static io.vertx.codegen.type.ClassKind.*;
public class SockjsServiceProxyJSGenerator extends AbstractSockjsServiceProxyGenerator {
SockjsServiceProxyJSGenerator() {
this.name = "sockjs_service_proxies";
this.kinds = Collections.singleton("proxy");
}
@Override
public String filename(ProxyModel model) {
ClassTypeInfo type = model.getType();
return "resources/" + type.getModuleName() + "-js/" + Helper.convertCamelCaseToUnderscores(type.getRaw().getSimpleName()) + "-proxy.js";
}
/**
* Generate the module name of a type
*/
private String getModuleName(ClassTypeInfo type) {
return type.getModuleName() + "-js/" + CamelCase.INSTANCE.to(SnakeCase.INSTANCE, type.getSimpleName());
}
private void genMethod(ProxyModel model, String methodName, boolean genStatic, @SuppressWarnings("SameParameterValue") Predicate methodFilter, CodeWriter writer) {
ClassTypeInfo type = model.getType();
String simpleName = type.getSimpleName();
Map> methodsByName = model.getMethodMap();
List methodList = methodsByName.get(methodName);
if (methodFilter != null) {
List methodTmpl = methodList;
methodList = new ArrayList<>();
for (MethodInfo method : methodTmpl) {
if (methodFilter.test(method)) {
methodList.add(method);
}
}
}
if (methodList.size() > 0) {
boolean overloaded = methodList.size() > 1;
MethodInfo method = methodList.get(methodList.size() - 1);
if (genStatic == method.isStaticMethod()) {
writer.println("/**");
if (method.getDoc() != null) {
Token.toHtml(method.getDoc().getTokens(), "", this::renderLinkToHtml, "\n", writer);
}
writer.println();
writer.print(" ");
if (genStatic) {
writer.format("@memberof module:%s", getModuleName(type)).println();
} else {
writer.println("@public");
}
boolean first = true;
for (ParamInfo param : method.getParams()) {
if (first) {
first = false;
} else {
writer.println();
}
writer.format(" @param %s {%s} ", param.getName(), getJSDocType(param.getType()));
if (param.getDescription() != null) {
Token.toHtml(param.getDescription().getTokens(), "", this::renderLinkToHtml, "", writer);
writer.print(" ");
}
}
writer.println();
if (method.getReturnType().getKind() != VOID) {
writer.format(" @return {%s}", getJSDocType(method.getReturnType()));
if (method.getReturnDescription() != null) {
writer.print(" ");
Token.toHtml(method.getReturnDescription().getTokens(), "", this::renderLinkToHtml, "", writer);
}
writer.println();
}
writer.println(" */");
writer.format("%s.%s = ", genStatic ? simpleName : "this", methodName);
if (overloaded) {
writer.println(" function() {");
} else {
writer.format(" function(%s) {\n", (method.getParams().stream().map(ParamInfo::getName).collect(Collectors.joining(", "))));
}
int mcnt = 0;
writer.indent();
writer.println("var __args = arguments;");
for (MethodInfo m : methodList) {
writer.print(mcnt++ == 0 ? "if" : "else if");
int paramSize = m.getParams().size();
writer.format(" (__args.length === %s", paramSize);
int cnt = 0;
if (paramSize > 0) {
writer.print(" && ");
}
first = true;
for (ParamInfo param : m.getParams()) {
if (first) {
first = false;
} else {
writer.print(" && ");
}
switch (param.getType().getKind()) {
case PRIMITIVE:
case BOXED_PRIMITIVE:
if (param.isNullable()) {
writer.print("(");
}
writer.format("typeof __args[%s] ===", cnt);
String paramSimpleName = param.getType().getSimpleName();
if ("boolean".equalsIgnoreCase(paramSimpleName)) {
writer.print("'boolean'");
} else if ("char".equals(paramSimpleName) || "Character".equals(paramSimpleName)) {
writer.print("'string'");
} else {
writer.print("'number'");
}
if (param.isNullable()) {
writer.format(" || __args[%s] == null)", cnt);
}
break;
case STRING:
case ENUM:
if (param.isNullable()) {
writer.print("(");
}
writer.format("typeof __args[%s] === 'string'", cnt);
if (param.isNullable()) {
writer.format(" || __args[%s] == null)", cnt);
}
break;
case CLASS_TYPE:
writer.format("typeof __args[%s] === 'function'", cnt);
break;
case API:
writer.format("typeof __args[%s] === 'object' && ", cnt);
if (param.isNullable()) {
writer.format("(__args[%s] == null || ", cnt);
}
writer.format("__args[%s]._jdel", cnt);
if (param.isNullable()) {
writer.print(")");
}
break;
case JSON_ARRAY:
case LIST:
case SET:
writer.format("typeof __args[%s] === 'object' && ", cnt);
if (param.isNullable()) {
writer.print("(");
}
writer.format("__args[%s] instanceof Array", cnt);
if (param.isNullable()) {
writer.format(" || __args[%s] == null)", cnt);
}
break;
case HANDLER:
if (param.isNullable()) {
writer.print("(");
}
writer.format("typeof __args[%s] === 'function'", cnt);
if (param.isNullable()) {
writer.format(" || __args[%s] == null)", cnt);
}
break;
case OBJECT:
if (param.getType().isVariable() && ((TypeVariableInfo) param.getType()).isClassParam()) {
writer.format("j_%s.accept(__args[%s])", param.getType().getName(), cnt);
} else {
writer.format("typeof __args[%s] !== 'function'", cnt);
}
break;
case FUNCTION:
writer.format("typeof __args[%s] === 'function'", cnt);
break;
case THROWABLE:
writer.format("typeof __args[%s] === 'object'", cnt);
break;
default:
if (!param.isNullable()) {
writer.print("(");
}
writer.format("typeof __args[%s] === 'object'", cnt);
if (!param.isNullable()) {
writer.format(" && __args[%s] != null)", cnt);
}
}
cnt++;
}
writer.println(") {");
writer.indent();
genMethodAdapter(model, m, writer);
writer.unindent();
writer.print("}");
}
writer.unindent();
writer.println(" else throw new TypeError('function invoked with invalid arguments');");
writer.println("};");
writer.println();
}
}
}
@Override
public String render(ProxyModel model, int index, int size, Map session) {
StringWriter sw = new StringWriter();
CodeWriter writer = new CodeWriter(sw);
ClassTypeInfo type = model.getType();
String simpleName = type.getSimpleName();
genLicenses(writer);
writer.println();
writer.format("/// ", Helper.convertCamelCaseToUnderscores(type.getRaw().getSimpleName())).println();
writer.println();
writer.format("/** @module %s */", getModuleName(type)).println();
List imports = new ArrayList<>();
//generate the module loader shim
writer.println("!function (factory) {");
writer.indent().println("if (typeof require === 'function' && typeof module !== 'undefined') {");
// write imports as commonJS
writer.indent();
imports.clear();
for (ApiTypeInfo referencedType : model.getReferencedTypes()) {
if(referencedType.isProxyGen()) {
String refedType = referencedType.getSimpleName();
imports.add(refedType);
writer.format("var %s = require('./%s-proxy');", refedType, getModuleName(referencedType)).println();
}
}
writer.format("factory(%s);", String.join(", ", imports)).println();
writer.unindent().println("} else if (typeof define === 'function' && define.amd) {");
writer.indent().println("// AMD loader");
imports.clear();
for (ApiTypeInfo referencedType : model.getReferencedTypes()) {
if(referencedType.isProxyGen()) {
imports.add("'" + getModuleName(referencedType) + "-proxy'");
}
}
writer.format("define('%s-proxy', [%s], factory);", getModuleName(type), String.join(", ", imports));
writer.println();
writer.unindent().println("} else {");
writer.indent().println("// plain old include");
imports.clear();
for (ApiTypeInfo referencedType : model.getReferencedTypes()) {
if(referencedType.isProxyGen()) {
String refedType = referencedType.getSimpleName();
imports.add("this." + refedType);
}
}
writer.format("%s = factory(%s);", simpleName, String.join(", ", imports)).println();
writer.unindent().println("}");
imports.clear();
for (ApiTypeInfo referencedType : model.getReferencedTypes()) {
if(referencedType.isProxyGen()) {
String refedType = referencedType.getSimpleName();
imports.add(refedType);
}
}
writer.unindent().format("}(function (%s) {", String.join(", ", imports)).println();
writer.indent();
writer.println();
genDoc(model, writer);
//The constructor
writer.format("var %s = function(eb, address) {", simpleName).println();
writer.indent().println("var j_eb = eb;");
writer.println("var j_address = address;");
writer.println("var closed = false;");
writer.println("var that = this;");
writer.println("var convCharCollection = function(coll) {");
writer.indent().println("var ret = [];");
writer.println("for (var i = 0;i < coll.length;i++) {");
writer.indent().println("ret.push(String.fromCharCode(coll[i]));");
writer.unindent().println("}");
writer.println("return ret;");
writer.unindent().println("};");
//Apply any supertypes
for (TypeInfo superType : model.getSuperTypes()) {
writer.format("%s.call(this, j_val);", superType.getRaw().getSimpleName()).println();
}
writer.println();
//Now iterate through each unique method
for (String methodName : model.getMethodMap().keySet()) {
//Call out to actually generate the method, we only consider non static methods here
genMethod(model, methodName, false, this::methodFilter, writer);
}
writer.unindent().println("};");
writer.println();
//Iterate through the methods again, this time only considering the static ones
for (String methodName : model.getMethodMap().keySet()) {
//Call out to generate the static method
genMethod(model, methodName, true, this::methodFilter, writer);
}
writer.println("if (typeof exports !== 'undefined') {");
writer.indent().println("if (typeof module !== 'undefined' && module.exports) {");
writer.indent().format("exports = module.exports = %s;", simpleName).println();
writer.unindent().println("} else {");
writer.indent().format("exports.%s = %s;", simpleName, simpleName).println();
writer.unindent().println("}");
writer.unindent().println("} else {");
writer.indent().format("return %s;", simpleName).println();
writer.unindent().println("}");
writer.unindent().print("});");
return sw.toString();
}
private void genMethodAdapter(ProxyModel model, MethodInfo m, CodeWriter writer) {
ProxyMethodInfo method = (ProxyMethodInfo) m;
writer.println("if (closed) {");
writer.indent().println("throw new Error('Proxy is closed');");
writer.unindent().println("}");
genMethodCall(method, writer);
writer.println(";");
if (method.isProxyClose()) {
writer.println("closed = true;");
}
if (method.isFluent()) {
writer.println("return that;");
} else {
writer.println("return;");
}
}
private void genMethodCall(MethodInfo method, PrintWriter writer){
List params = method.getParams();
int psize = params.size();
ParamInfo lastParam = psize > 0 ? params.get(psize - 1) : null;
boolean hasResultHandler = lastParam != null && lastParam.getType().getKind() == HANDLER && ((ParameterizedTypeInfo) lastParam.getType()).getArg(0).getKind() == ASYNC_RESULT;
if (hasResultHandler) {
psize--;
}
writer.print("j_eb.send(j_address, {");
boolean first = true;
for (int pcnt = 0; pcnt < psize; pcnt++) {
if (first) {
first = false;
} else {
writer.print(", ");
}
ParamInfo param = params.get(pcnt);
String paramTypeName = param.getType().getName();
String paramName = param.getName();
writer.format("\"%s\":", paramName);
if ("java.lang.Character".equals(paramTypeName) || "char".equals(paramTypeName)) {
writer.format("__args[%d].charCodeAt(0)", pcnt);
} else {
writer.format("__args[%d]", pcnt);
}
}
writer.format("}, {\"action\":\"%s\"}", method.getName());
if (hasResultHandler) {
ParameterizedTypeInfo handlerType = (ParameterizedTypeInfo) lastParam.getType();
ParameterizedTypeInfo asyncResultType = (ParameterizedTypeInfo) handlerType.getArg(0);
TypeInfo resultType = asyncResultType.getArg(0);
writer.format(", function(err, result) { __args[%d](err, result && ", psize);
ClassKind resultKind = resultType.getKind();
if (resultType.getKind() == API) {
writer.format("new %s(j_eb, result.headers.proxyaddr)", resultType.getSimpleName());
} else if ((resultKind == LIST || resultKind == SET) && "java.lang.Character".equals(((ParameterizedTypeInfo)resultType).getArg(0).getName())) {
writer.print("convCharCollection(result.body)");
} else {
writer.print("result.body");
}
writer.print("); }");
}
writer.print(")");
}
void genDoc(ProxyModel model, CodeWriter writer) {
writer.println("/**");
if (model.getIfaceComment() != null) {
writer.println(Helper.removeTags(model.getIfaceComment()));
}
writer.println(" @class");
writer.println("*/");
}
}