package core.framework.impl.web.service; import core.framework.api.http.HTTPMethod; import core.framework.api.util.Maps; import core.framework.api.web.service.Path; import core.framework.api.web.service.PathParam; import core.framework.impl.code.CodeBuilder; import core.framework.impl.code.DynamicInstanceBuilder; import core.framework.impl.reflect.GenericTypes; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; import static core.framework.impl.code.CodeBuilder.enumVariableLiteral; import static core.framework.impl.code.CodeBuilder.typeVariableLiteral; /** * @author neo */ public class WebServiceClientBuilder<T> { private final Class<T> serviceInterface; private final WebServiceClient client; public WebServiceClientBuilder(Class<T> serviceInterface, WebServiceClient client) { this.serviceInterface = serviceInterface; this.client = client; } public T build() { DynamicInstanceBuilder<T> builder = new DynamicInstanceBuilder<>(serviceInterface, serviceInterface.getCanonicalName() + "$Client"); builder.addField(new CodeBuilder().append("final {} client;", WebServiceClient.class.getCanonicalName()).build()); builder.constructor(new Class<?>[]{WebServiceClient.class}, "this.client = $1;"); for (Method method : serviceInterface.getMethods()) { builder.addMethod(buildMethod(method)); } return builder.build(client); } private String buildMethod(Method method) { CodeBuilder builder = new CodeBuilder(); Type returnType = method.getGenericReturnType(); Map<String, Integer> pathParamIndexes = Maps.newHashMap(); Type requestBeanType = null; Integer requestBeanIndex = null; builder.append("public {} {}(", GenericTypes.rawClass(returnType).getCanonicalName(), method.getName()); Annotation[][] annotations = method.getParameterAnnotations(); Class<?>[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { Class<?> paramClass = parameterTypes[i]; if (i > 0) builder.append(", "); builder.append("{} param{}", paramClass.getCanonicalName(), i); PathParam pathParam = pathParam(annotations[i]); if (pathParam != null) { pathParamIndexes.put(pathParam.value(), i); } else { requestBeanIndex = i; requestBeanType = method.getGenericParameterTypes()[i]; } } builder.append(") {\n"); builder.indent(1).append("java.lang.reflect.Type requestType = {};\n", requestBeanType == null ? "null" : typeVariableLiteral(requestBeanType)); builder.indent(1).append("Object requestBean = {};\n", requestBeanIndex == null ? "null" : "param" + requestBeanIndex); builder.indent(1).append("java.util.Map pathParams = new java.util.HashMap();\n"); pathParamIndexes.forEach((name, index) -> builder.indent(1).append("pathParams.put(\"{}\", param{});\n", name, index)); String returnTypeLiteral = returnType == void.class ? Void.class.getCanonicalName() : GenericTypes.rawClass(returnType).getCanonicalName(); String path = method.getDeclaredAnnotation(Path.class).value(); builder.indent(1).append("String serviceURL = client.serviceURL(\"{}\", pathParams);\n", path); // to pass path as string literal, the escaped char will not be transferred, like \\, currently not convert is because only type regex may contain special char HTTPMethod httpMethod = HTTPMethodHelper.httpMethod(method); builder.indent(1).append("{} response = ({}) client.execute({}, serviceURL, requestType, requestBean, {});\n", returnTypeLiteral, returnTypeLiteral, enumVariableLiteral(httpMethod), typeVariableLiteral(returnType)); if (returnType != void.class) builder.indent(1).append("return response;\n"); builder.append("}"); return builder.build(); } private PathParam pathParam(Annotation[] annotations) { if (annotations.length == 0) return null; for (Annotation annotation : annotations) { if (annotation instanceof PathParam) return (PathParam) annotation; } return null; } }