lib/client_generator.js (252 lines of code) (raw):

/* eslint-disable max-len */ 'use strict'; const Emitter = require('@darabonba/emitter'); const assert = require('assert'); const path = require('path'); const { _name, save, SSE } = require('./util'); const CommonGenerator = require('./common_generator'); const InterfaceGenerator = require('./interface_generator'); class BuilderGenerator extends Emitter { constructor(ast, ctx) { super(' '); this.ast = ast; this.ctx = ctx; } codegen(level = 0) { this.emitln(`// This file is auto-generated, don't edit it. Thanks.`, level); this.emitln(`package ${this.ctx.package};`, level); this.emitln(); if (this.ast.extends) { this.emitln(`import ${this.ctx.extendsClass.package + '.' + this.ctx.extendsClass.className};`); } this.emitln(); this.emit(`public final class ${this.ctx.className}Builder`, level); if (this.ast.extends) { this.emit(` extends ${this.ctx.extendsClass.className}<${this.ctx.className}Builder, ${this.ctx.implements || this.ctx.className}>`); } this.emitln(` {`); this.emitln(); this.emitServiceName(level + 1); this.emitln(); this.emitln('@Override', level + 1); this.emitln(`protected final ${this.ctx.implements || this.ctx.className} buildClient() {`, level + 1); this.emitln(`return new ${this.ctx.className}(super.applyClientConfiguration());`, level + 2); this.emitln('}', level + 1); this.emitln(); this.emitln('}', level); } emitServiceName(level) { this.emitln('@Override', level); this.emitln(`protected String serviceName() {`, level); this.emitln(`return "${this.ctx.productId}${this.ctx.apiVersions}";`, level + 1); this.emitln(`}`, level); } } class Generator extends CommonGenerator { constructor(ast, ctx) { super(ast, ctx); this.ctx.extendParam = {}; this.ctx.extendsClass = {}; } codegen(level = 0) { const nonStaticWraps = this.ast.moduleBody.nodes.filter((item) => { return item.type === 'function' && !item.isStatic; }); const apis = this.ast.moduleBody.nodes.filter((item) => { return item.type === 'api'; }); const models = this.ast.moduleBody.nodes.filter((item) => { return item.type === 'model'; }); const types = this.ast.moduleBody.nodes.filter((item) => { return item.type === 'type'; }); const init = this.ast.moduleBody.nodes.filter((item) => { return item.type === 'init'; }); const functions = this.ast.moduleBody.nodes.filter((item) => { return item.type === 'function'; }); if (nonStaticWraps.length > 0 || apis.length > 0) { this.ctx.extendParam.writeConstruct = true; } if (models.length > 0) { this.ctx.extendParam.writeImport = true; } if (this.ast.extends) { this.ctx.extendsName = _name(this.ast.extends); this.ctx.extendsClass.className = this.ctx.imports[this.ctx.extendsName].className; this.ctx.extendsClass.package = this.ctx.imports[this.ctx.extendsName].package; } this.emitHeader(level); this.emitln(); types.forEach(type => { this.emitEachType(type, level + 1); }); this.emitln(); if (this.ctx.exec) { init.forEach(i => { this.emitEachInit(i, level + 1); }); } else if (init.length === 1) { init.forEach(i => { this.emitConstructor(i, level + 1); }); } // this.emitln(); // apis.forEach(api => { this.emitEachAPI(api, level + 1); }); functions.forEach(func => { if (_name(func.functionName) === 'close' && !this.ctx.exec && this.ctx.implements) { this.ctx.closable = true; } this.emitln(); this.emitEachFunction(func, level + 1); }); this.emitln(); this.emitFooter(level); const sseIterators = this.ctx.iterators.filter((item) => { return item.iteratorStyle === SSE; }); if (sseIterators.length <= 0) { this.output = this.output.replace('import darabonba.core.sse.SSEHttpResponseHandler;\n', ''); } if (!this.ctx.exec && init.length === 1) { const builder = new BuilderGenerator(this.ast, this.ctx); builder.codegen(level); save(path.join(this.ctx.outputDir, `${this.ctx.className}Builder.java`), builder.output); } if (!this.ctx.exec && this.ctx.implements) { const inter = new InterfaceGenerator(this.ast, this.ctx); inter.emitInterface(nonStaticWraps, apis, level); save(path.join(this.ctx.outputDir, `${this.ctx.implements}.java`), inter.output); } } emitHeader(level) { this.emitln(`// This file is auto-generated, don't edit it. Thanks.`, level); this.emitln(`package ${this.ctx.package};`, level); this.emitln(); if (this.ctx.extendParam.writeImport) { this.emitln(`import com.aliyun.core.http.*;`, level); this.emitln(`import ${this.ctx.package}.models.*;`, level); this.emitln(`import darabonba.core.sse.SSEHttpResponseHandler;`, level); this.emitln(`import darabonba.core.utils.*;`, level); this.emitExternImport(level); this.emitln(); this.emitln(`import java.util.concurrent.CompletableFuture;`, level); this.emitln(); } this.emitln(); this.emitln(`/**`, level); this.emitln(` * <p>${this.ctx.description ? this.ctx.description : 'Main client'}.</p>`, level); this.emitln(` */`, level); if (!this.ctx.exec) { this.emit(`public final class ${this.ctx.className}`, level); } else { this.emit(`public class ${this.ctx.className}`, level); } if (!this.ctx.exec && this.ctx.implements) { this.emit(` implements ${this.ctx.implements}`); } this.emitln(` {`); } emitExternImport(level) { const importList = []; for (var item of this.ctx.usedExternModel.entries()) { if (!importList.includes(this.ctx.imports[item[0]].package)) { this.emitln(`import ${this.ctx.imports[item[0]].package}.*;`, level); importList.push(this.ctx.imports[item[0]].package); if (item[1].size > 0) { this.emitln(`import ${this.ctx.imports[item[0]].package}.models.*;`, level); } } } } emitEachType(ast, level) { this.emit('protected final ', level); this.visitType(ast.value); this.emitln(` ${_name(ast.vid).substr(1)};`); } emitConstructor(ast, level) { this.visitAnnotation(ast.annotation, level); var className = this.ctx.className || 'DefaultAsyncClient'; this.emit(`protected ${className}`, level); this.visitParams(ast.params, level); this.emitln(' {'); if (ast.initBody) { this.visitStmts(ast.initBody, false, level + 1); } this.emitln('this.REQUEST = TeaRequest.create().setProduct(product).setEndpointRule(endpointRule).setEndpointMap(endpointMap).setVersion(version);', level + 1); this.emitln(`}`, level); } emitEachInit(ast, level) { var className = this.ctx.className || 'DefaultAsyncClient'; this.visitAnnotation(ast.annotation, level); this.emit(`public ${className}`, level); this.visitParams(ast.params, level); this.emitln(' {'); if (ast.initBody) { this.visitStmts(ast.initBody, false, level + 1); } this.emitln('}', level); } visitReturnBody(ast, level) { assert.equal(ast.type, 'returnBody'); this.emit('\n'); this.visitStmts(ast.stmts, false, level); } emitEachFunction(ast, level) { this.visitAnnotation(ast.annotation, level); if (!this.ctx.exec && this.ctx.implements && !ast.isStatic) { this.emitln(`@Override`, level); } this.emit('public ', level); if (ast.isStatic) { this.emit('static '); } if (_name(ast.functionName).endsWith('WithAsyncResponseHandler')) { this.emit(`<ReturnT> CompletableFuture<ReturnT>`); this.emit(` ${_name(ast.functionName)}`); } else { this.emit(`${_name(ast.functionName) === 'main' ? '' : ast.isAsync ? 'CompletableFuture<' : ''}`); if (ast.isAsync && (ast.returnType.lexeme === 'void' || ast.returnType.name === 'void')) { this.emit(`Void`); } else { this.visitType(ast.returnType); } this.emit(`${_name(ast.functionName) === 'main' ? '' : ast.isAsync ? '>' : ''}`); this.emit(` ${_name(ast.functionName)}`); } if (_name(ast.functionName) === 'main') { if (!ast.params.params || ast.params.params.length === 0) { throw new Error('static function main must have a argument'); } this.emit(`(String[] ${_name(ast.params.params[0].paramName)}_)`); } else if (_name(ast.functionName).endsWith('WithAsyncResponseHandler')) { const params = JSON.parse(JSON.stringify(ast.params)); params.params.pop(); this.emit('('); this.visitPureParams(params, level); this.emit(`, AsyncResponseHandler<`); this.visitType(ast.returnType); this.emit(`, ReturnT> responseHandler)`); } else { this.visitParams(ast.params, level); } this.emitln(' {'); if (ast.functionBody) { if (_name(ast.functionName) === 'main') { const args = _name(ast.params.params[0].paramName); this.emitln(`java.util.List<String> ${args} = java.util.Arrays.asList(${args}_);`, level + 1); this.visitFunctionBody(ast.functionBody, false, level + 1); } else { if (ast.isAsync) { this.emitln(`try {`, level + 1); this.visitFunctionBody(ast.functionBody, true, level + 2); this.emitln(`} catch (Exception e) {`, level + 1); this.emit(`CompletableFuture<`, level + 2); if (_name(ast.functionName).endsWith('WithAsyncResponseHandler')) { this.emit(`ReturnT`); } else if (ast.returnType.lexeme === 'void' || ast.returnType.name === 'void') { this.emit(`Void`); } else { this.visitType(ast.returnType); } this.emitln(`> future = new CompletableFuture<>();`); this.emitln(`future.completeExceptionally(e);`, level + 2); this.emitln(`return future;`, level + 2); this.emitln(`}`, level + 1); } else { this.visitFunctionBody(ast.functionBody, false, level + 1); } } } this.emitln('}', level); } visitFunctionBody(ast, isAsync, level) { assert.equal(ast.type, 'functionBody'); this.visitStmts(ast.stmts, isAsync, level); } emitFooter(level) { this.emitln('}', level); } } module.exports = Generator;