lib/generator.js (1,743 lines of code) (raw):

'use strict'; const assert = require('assert'); const fs = require('fs'); const path = require('path'); const DSL = require('@darabonba/parser'); const xml2js = require('xml2js'); const Entities = require('html-entities').XmlEntities; const Annotation = require('@darabonba/annotation-parser'); const REQUEST = 'request_'; const RESPONSE = 'response_'; const RUNTIME = 'runtime_'; const { _name, _type, _escape, _string, _lowerFirst, _subModelName, remove, _upperFirst, md2Html, _doc } = require('./helper'); function collectionType(name) { if (name === 'Object') { return '?'; } return name; } function shouldChange(pomVersion, importVersion) { if (pomVersion.indexOf(',') > 0) { return false; } try { var sourceVersion = pomVersion.split('.'); var targetVersion = importVersion.split('.'); if (parseInt(sourceVersion[0]) < parseInt(targetVersion[0])) { return true; } if (parseInt(sourceVersion[1]) < parseInt(targetVersion[1])) { return true; } if (parseInt(sourceVersion[2]) < parseInt(targetVersion[2])) { return true; } } catch (error) { // empty } return false; } function avoidReserveName(name) { const reserves = [ 'public' ]; if (reserves.indexOf(name) !== -1) { return `${name}_`; } return name; } function render(template, params = {}) { const needParamsKeys = ['description', 'url', 'licenseName', 'developerId', 'licenseUrl', 'developerName', 'developerEmail', 'scmConnection', 'scmDeveloperConnection', 'scmUrl', 'groupId', 'artifactId', 'version' ]; needParamsKeys.forEach(key => { if (params[key] === undefined) { params[key] = ''; } }); if (params) { Object.keys(params).forEach((key) => { template = template.split('${' + key + '}').join(params[key]); }); } return template; } function getAttr(node, attrName) { for (let i = 0; i < node.attrs.length; i++) { if (_name(node.attrs[i].attrName) === attrName) { return node.attrs[i].attrValue.string || node.attrs[i].attrValue.lexeme || node.attrs[i].attrValue.value; } } } function parse(xml) { return new Promise((resolve, reject) => { xml2js.parseString(xml, function (err, result) { if (err) { return reject(err); } return resolve(result); }); }); } class Visitor { constructor(option = {}) { const javaPackage = option && option.package; if (!javaPackage) { throw new Error('Darafile -> java -> javaPackage should not empty, please add java option into Darafile.example:\n' + '"java": {"package": "com.aliyun.test"}'); } this.pomOutputDir = option.outputDir; option.outputDir = path.join(option.outputDir, 'src/main/java', javaPackage.split('.').join('/')); if (option.java) { this.className = option.java.className; this.implements = option.java.implements; this.NoException = option.java.NoException; } this.baseClient = option && option.baseClient || javaPackage + '.BaseClient'; this.package = javaPackage; this.packageInfo = option.java.packageInfo; this.packageManager = option.java.packageManager; this.config = Object.assign({ outputDir: '', indent: ' ', clientPath: 'Client.java' }, option); this.output = ''; this.outputDir = this.config.outputDir; this.exec = option.exec; this.editable = option.editable; this.enableMinimizeModelName = option.enableMinimizeModelName || option.java.enableMinimizeModelName; this.typedef = option.java.typedef || {}; if (!this.outputDir) { throw new Error('Darafile -> java -> javaPackage should not empty, please add java option into Darafile.example:\n' + '"java": {"package": "com.aliyun.test"}'); } fs.mkdirSync(this.outputDir, { recursive: true }); this.conflictModelNameMap = {}; this.allModleNameMap = {}; remove(path.join(this.outputDir, 'models/')); } visit(ast, level = 0) { this.usedExternModel = ast.usedExternModel; this.conflictModels = ast.conflictModels; this.visitModule(ast, level); } emit(str, level) { this.output += this.config.indent.repeat(level) + str; } save(filepath) { const targetPath = path.join(this.outputDir, filepath); fs.mkdirSync(path.dirname(targetPath), { recursive: true }); fs.writeFileSync(targetPath, this.output); this.output = ''; } visitWhile(ast, level) { assert.equal(ast.type, 'while'); this.emit('\n'); this.emit('while (', level); this.visitExpr(ast.condition, level + 1); this.emit(') {\n'); this.visitStmts(ast.stmts, level + 1); this.emit('}\n', level); } visitFor(ast, level) { assert.equal(ast.type, 'for'); this.emit(`for (`, level); this.visitType(ast.list.inferred.itemType); this.emit(` ${_name(ast.id)} : `); this.visitExpr(ast.list, level + 1); this.emit(') {\n'); this.visitStmts(ast.stmts, level + 1); this.emit('}\n', level); } visitInterface(functions, apis, level) { this.emit(`package ${this.package};\n\n`); this.emit(`import ${this.package}.models.*;\n\n`); this.emit(`public interface ${this.implements} {\n\n`); this.visitApiInterface(apis, level + 1); this.visitFunctionInterface(functions, level + 1); this.emit('}'); this.save(this.implements + '.java'); } async visitPom() { var entities = new Entities(); var targetFile = this.pomOutputDir + '/pom.xml'; let pomName = '/pom.xml'; if (this.exec) { pomName = '/pomWithMain.xml'; } var pomFile; var dependenciesClass = []; var havePom = false; if (fs.existsSync(targetFile)) { havePom = true; pomFile = fs.readFileSync(targetFile); } else { pomFile = fs.readFileSync(path.join(__dirname, pomName)); } Object.keys(this.typedef).forEach((type) => { if (!dependenciesClass.includes(this.typedef[type].package)) { dependenciesClass.push(this.typedef[type].package); } }); Object.keys(this.imports).forEach((key) => { dependenciesClass.push(this.imports[key].release); let moduleTypedef = this.imports[key].typedef || {}; Object.keys(moduleTypedef).forEach((type) => { if (!dependenciesClass.includes(moduleTypedef[type].package)) { dependenciesClass.push(moduleTypedef[type].package); } }); }); const json = await parse(pomFile); var needAddDependencies = []; if (!havePom) { json.project.dependencies = { dependency: [] }; dependenciesClass.push('com.aliyun:tea:1.1.14'); dependenciesClass.forEach((value) => { if (value) { let dependency = value.split(':'); var dependencyObject = {}; dependencyObject.groupId = dependency[0]; dependencyObject.artifactId = dependency[1]; dependencyObject.version = dependency[2]; json.project.dependencies.dependency.push(dependencyObject); } }); } else { dependenciesClass.forEach((value) => { if (value) { let needAdd = true; let dependency = value.split(':'); var dependencyObject = {}; dependencyObject.groupId = dependency[0]; dependencyObject.artifactId = dependency[1]; dependencyObject.version = dependency[2]; json.project.dependencies[0].dependency.forEach((dependency) => { if (dependency.artifactId[0] === dependencyObject.artifactId) { if (shouldChange(dependency.version[0], dependencyObject.version)) { dependency.version[0] = dependencyObject.version; } needAdd = false; } }); if (needAdd) { needAddDependencies.push(dependencyObject); } } }); needAddDependencies.forEach((value) => { json.project.dependencies[0].dependency.push(value); }); } const builder = new xml2js.Builder(); pomFile = builder.buildObject(json); const newJson = await parse(pomFile); needAddDependencies = []; Object.keys(this.packageManager || {}).forEach((value) => { let needAdd = true; let dependency = value.split(':'); var dependencyObject = {}; dependencyObject.groupId = dependency[0]; dependencyObject.artifactId = dependency[1]; dependencyObject.version = this.packageManager[`${dependency[0]}:${dependency[1]}`]; newJson.project.dependencies[0].dependency.forEach((dependency) => { if (dependency.groupId[0] === dependencyObject.groupId && dependency.artifactId[0] === dependencyObject.artifactId) { dependency.version[0] = dependencyObject.version; needAdd = false; } }); if (needAdd) { needAddDependencies.push(dependencyObject); } }); needAddDependencies.forEach((value) => { newJson.project.dependencies[0].dependency.push(value); }); let newPom = builder.buildObject(newJson); if (this.exec) { let mainClassPath = this.package + '.' + (this.className || 'Client'); newPom = newPom.split('${mainClass}').join(mainClassPath); } newPom = render(newPom, this.packageInfo); fs.writeFileSync(targetFile, entities.decode(newPom)); } visitModule(ast, level) { assert.equal(ast.type, 'module'); const nonStaticWraps = ast.moduleBody.nodes.filter((item) => { return item.type === 'function' && !item.isStatic; }); const apis = ast.moduleBody.nodes.filter((item) => { return item.type === 'api'; }); const models = ast.moduleBody.nodes.filter((item) => { return item.type === 'model'; }); const types = ast.moduleBody.nodes.filter((item) => { return item.type === 'type'; }); const init = ast.moduleBody.nodes.filter((item) => { return item.type === 'init'; }); this.comments = ast.comments; var extendParam = {}; if (nonStaticWraps.length > 0 || apis.length > 0) { extendParam.writeConstruct = true; } if (models.length > 0) { extendParam.writeImport = true; } this.predefined = ast.predefined; this.eachImport(ast.imports, ast.usedExternModel, level); this.visitPom(); if (this.implements) { this.visitInterface(nonStaticWraps, apis, level); } // global definition for (let i = 0; i < models.length; i++) { const modelName = models[i].modelName.lexeme; this.modelBefore(level, modelName); this.eachModel(models[i], level); if (ast.models) { const subModels = Object.keys(ast.models).filter((key) => { return key.startsWith(modelName + '.'); }).map((key) => { return ast.models[key]; }); for (let i = 0; i < subModels.length; i++) { this.eachSubModel(subModels[i], level + 1); } } this.emit('}\n', level); this.save('models/' + modelName + '.java'); } // models definition var extendsClass; if (ast.extends) { var extendsName = _name(ast.extends); extendsClass = {}; extendsClass.className = this.imports[extendsName].className || 'Client'; extendsClass.package = this.imports[extendsName].package; } this.apiBefore(extendParam, extendsClass, apis.length > 0, level); // creat class field for (let i = 0; i < types.length; i++) { this.emit('\n'); this.eachType(types[i], level + 1); } // creat contructor for (let i = 0; i < init.length; i++) { this.emit('\n'); this.eachInit(init[i], level + 1); } for (let i = 0; i < apis.length; i++) { if (i !== 0) { this.emit('\n'); } this.eachAPI(apis[i], level + 1); } this.apiAfter(apis.length > 0, level + 1); this.wrapBefore(level); const functions = ast.moduleBody.nodes.filter((item) => { return item.type === 'function'; }); for (let i = 0; i < functions.length; i++) { if (i !== 0) { this.emit('\n'); } this.eachFunction(functions[i], level + 1); } this.emit('}\n'); let outPutclassName = this.className || 'Client'; this.save(outPutclassName + '.java'); } eachImport(imports) { this.imports = {}; if (imports.length === 0) { return; } if (!this.config.pkgDir) { throw new Error(`Must specific pkgDir when have imports`); } const lockPath = path.join(this.config.pkgDir, '.libraries.json'); const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8')); for (let i = 0; i < imports.length; i++) { const item = imports[i]; const aliasId = item.lexeme; const moduleDir = this.config.libraries[aliasId]; let targetPath = ''; if (moduleDir.startsWith('./') || moduleDir.startsWith('../')) { targetPath = path.join(this.config.pkgDir, moduleDir); } else if (moduleDir.startsWith('/')) { targetPath = moduleDir; } else { targetPath = path.join(this.config.pkgDir, lock[moduleDir]); } const pkgPath = fs.existsSync(path.join(targetPath, 'Teafile')) ? path.join(targetPath, 'Teafile') : path.join(targetPath, 'Darafile'); const pkg = JSON.parse(fs.readFileSync(pkgPath)); const releaseJava = pkg.releases && pkg.releases.java; const javaPkg = pkg.java; if (!javaPkg) { throw new Error(`The '${aliasId}' has no Java supported.`); } else { javaPkg.release = releaseJava; } this.imports[aliasId] = javaPkg; } } visitParams(ast) { assert.equal(ast.type, 'params'); this.emit('('); for (var i = 0; i < ast.params.length; i++) { if (i !== 0) { this.emit(', '); } const node = ast.params[i]; assert.equal(node.type, 'param'); this.visitType(node.paramType); this.emit(` ${avoidReserveName(_name(node.paramName))}`); } this.emit(')'); } getSubFieldClassName(className, hasModel) { if (className.indexOf('.') > 0) { var names = className.split('.'); var name = ''; if (hasModel) { name = names[0] + '.'; } name += _subModelName(className, this.conflictModelNameMap, this.allModleNameMap, this.enableMinimizeModelName); return name; } return className; } getSubModelClassName(names, index, currentName) { if (index < names.length) { names[index] = currentName + _upperFirst(names[index]); return this.getSubModelClassName(names, index + 1, names[index]); } return names.join('.'); } visitType(ast, isSubType = false) { if (ast.type === 'map') { this.emit(`java.util.Map<`); this.visitType(ast.keyType, true); this.emit(`, `); this.visitType(ast.valueType, true); this.emit(`>`); } else if (ast.type === 'array') { this.emit(`java.util.List<`); this.visitType(ast.subType || ast.itemType, true); this.emit(`>`); } else if (ast.type === 'model') { if (ast.moduleName) { this.emit(`${this.imports[ast.moduleName].package}.models.`); } else { const modelMap = `${_name(ast)}`; if (this.conflictModels.get(modelMap)) { this.emit(`${this.package}.models.`); } } this.emit(`${_type(this.getSubFieldClassName(ast.name, true))}`); } else if (ast.type === 'subModel') { let className = ''; for (let i = 0; i < ast.path.length; i++) { const item = ast.path[i]; if (i > 0) { className += '.'; } className += item.lexeme; } let resultName = this.getSubModelClassName(className.split('.'), 0, ''); this.emit(resultName); } else if (ast.type === 'moduleModel') { const [moduleId, ...rest] = ast.path; let pathName = rest.map((item) => { return item.lexeme; }).join('.'); let subModelName = ''; if (rest.length > 1) { subModelName = `.${_subModelName(pathName, this.conflictModelNameMap, this.allModleNameMap, this.enableMinimizeModelName)}`; } var modelName = rest[0].lexeme; var moduleName = moduleId.lexeme; var packageName = `${this.imports[moduleName].package}.models.`; this.emit(packageName + modelName + subModelName); } else if (ast.idType === 'typedef') { this.emit(this.typeRelover(ast)); } else if (ast.type === 'moduleTypedef') { for (let i = 1; i < ast.path.length; i++) { this.emit(this.typeRelover(ast.path[i], ast.path[0])); } } else if (ast.type === 'basic') { this.emit(_type(ast.name)); } else if (this.predefined && this.predefined[`module:${_name(ast)}`]) { var className = this.imports[ast.lexeme].className || 'Client'; this.emit(`${this.imports[_name(ast)]}.${className}`); } else if (ast.idType === 'module') { let className = this.imports[ast.lexeme].className || 'Client'; this.emit(`${this.imports[ast.lexeme].package}.${className}`); } else if (ast.idType === 'model') { const modelMap = `${_name(ast)}`; if (this.conflictModels.get(modelMap)) { this.emit(`${this.package}.models.`); } this.emit(modelMap); } else if (ast.type === 'module_instance') { let className = this.imports[_name(ast)].className || 'Client'; this.emit(`${this.imports[_name(ast)].package}.${className}`); } else { if (isSubType) { this.emit(collectionType(_type(ast.lexeme || ast.name))); } else { this.emit(_type(ast.lexeme || ast.name)); } } } visitAnnotation(annotation, level) { if (!annotation || !annotation.value) { return; } let comments = DSL.comment.getFrontComments(this.comments, annotation.index); this.visitComments(comments, level); var ast = Annotation.parse(annotation.value); var description = ast.items.find((item) => { return item.type === 'description'; }); var summary = ast.items.find((item) => { return item.type === 'summary'; }); var _return = ast.items.find((item) => { return item.type === 'return'; }); var deprecated = ast.items.find((item) => { return item.type === 'deprecated'; }); var params = ast.items.filter((item) => { return item.type === 'param'; }).map((item) => { return { name: item.name.id, text: item.text.text.trimEnd() }; }); var throws = ast.items.filter((item) => { return item.type === 'throws'; }).map((item) => { return item.text.text.trimEnd(); }); let hasNextSection = false; this.emit(`/**\n`, level); const descriptionText = description ? description.text.text : ''; const summaryText = summary ? summary.text.text : ''; const returnText = _return ? _return.text.text.trimEnd() : ''; if (descriptionText !== '') { this.emit(` * <b>description</b> :\n`, level); const descriptionTexts = md2Html(descriptionText).trimEnd(); descriptionTexts.split('\n').forEach((line) => { this.emit(` * ${line}\n`, level); }); hasNextSection = true; } if (summaryText !== '') { if (hasNextSection) { this.emit(` * \n`, level); } this.emit(` * <b>summary</b> : \n`, level); const summaryTexts = md2Html(summaryText).trimEnd(); summaryTexts.split('\n').forEach((line) => { this.emit(` * ${line}\n`, level); }); hasNextSection = true; } if (deprecated) { if (hasNextSection) { this.emit(` * \n`, level); } const deprecatedText = deprecated.text.text.trimEnd(); this.emit(` * @deprecated `, level); deprecatedText.split('\n').forEach((line, index) => { if (index === 0) { this.emit(`${line}\n`); } else { this.emit(` * ${line}\n`, level); } }); hasNextSection = true; } if (params.length > 0) { if (hasNextSection) { this.emit(` * \n`, level); } params.forEach((item) => { this.emit(` * @param ${item.name} `, level); const items = item.text.trimEnd().split('\n'); items.forEach((line, index) => { if (index === 0) { this.emit(`${line}\n`); } else { this.emit(` * ${line}\n`, level); } }); }); hasNextSection = true; } if (returnText !== '') { this.emit(` * @return `, level); const returns = returnText.split('\n'); returns.forEach((line, index) => { if (index === 0) { this.emit(`${line}\n`); } else { this.emit(` * ${line}\n`, level); } }); hasNextSection = true; } if (throws.length > 0) { if (hasNextSection) { this.emit(` * \n`, level); } throws.forEach((item) => { this.emit(` * @throws `, level); const items = item.trimEnd().split('\n'); items.forEach((line, index) => { if (index === 0) { this.emit(`${line}\n`); } else { this.emit(` * ${line}\n`, level); } }); }); } this.emit(` */`, level); this.emit(`\n`); if (deprecated) { this.emit(`@Deprecated\n`, level); } } visitAPIBody(ast, level) { assert.equal(ast.type, 'apiBody'); this.emit(`TeaRequest ${REQUEST} = new TeaRequest();\n`, level); if (ast.stmts.stmts) { for (var i = 0; i < ast.stmts.stmts.length; i++) { this.visitStmt(ast.stmts.stmts[i], level); } } } visitRuntimeBefore(ast, level) { assert.equal(ast.type, 'object'); let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]); this.visitComments(comments, level + 1); this.emit(`java.util.Map<String, Object> ${RUNTIME} = `, level); this.visitObject(ast, level); this.emit(';\n'); this.emit('\n'); this.emit('TeaRequest _lastRequest = null;\n', level); this.emit('Exception _lastException = null;\n', level); this.emit('long _now = System.currentTimeMillis();\n', level); this.emit('int _retryTimes = 0;\n', level); this.emit(`while (Tea.allowRetry((java.util.Map<String, Object>) ${RUNTIME}.get("retry"), _retryTimes, _now)) {\n`, level); this.emit('if (_retryTimes > 0) {\n', level + 1); this.emit(`int backoffTime = Tea.getBackoffTime(${RUNTIME}.get("backoff"), _retryTimes);\n`, level + 2); this.emit('if (backoffTime > 0) {\n', level + 2); this.emit('Tea.sleep(backoffTime);\n', level + 3); this.emit('}\n', level + 2); this.emit('}\n', level + 1); this.emit('_retryTimes = _retryTimes + 1;\n', level + 1); this.emit('try {\n', level + 1); } visitStmt(ast, level) { let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]); this.visitComments(comments, level); if (ast.type === 'return') { this.visitReturn(ast, level); } else if (ast.type === 'if') { this.visitIf(ast, level); } else if (ast.type === 'throw') { this.visitThrow(ast, level); } else if (ast.type === 'assign') { this.visitAssign(ast, level); } else if (ast.type === 'retry') { this.visitRetry(ast, level); } else if (ast.type === 'break') { this.emit(`break;\n`, level); } else if (ast.type === 'declare') { this.visitDeclare(ast, level); } else if (ast.type === 'while') { this.visitWhile(ast, level); } else if (ast.type === 'for') { this.visitFor(ast, level); } else if (ast.type === 'try') { this.visitTry(ast, level); } else { this.emit(``, level); this.visitExpr(ast, level); this.emit(';\n'); } } visitTry(ast, level) { this.emit('try {\n', level); this.visitStmts(ast.tryBlock, level + 1); this.emit('}', level); if (ast.catchBlock && ast.catchBlock.stmts.length > 0) { let errorName = _name(ast.catchId); this.emit(` catch (TeaException ${errorName}) {\n`); this.visitStmts(ast.catchBlock, level + 1); this.emit(`} catch (Exception _${errorName}) {`, level); this.emit('\n'); this.emit(`TeaException ${errorName} = new TeaException(_${errorName}.getMessage(), _${errorName});\n`, level + 1); this.visitStmts(ast.catchBlock, level + 1); this.emit('}', level); } if (ast.finallyBlock && ast.finallyBlock.stmts.length > 0) { this.emit(' finally {\n'); this.visitStmts(ast.finallyBlock, level + 1); this.emit('}', level); } this.emit('\n', level); } visitFieldType(value, node, modelName) { if (value.fieldType === 'array') { // basic type this.emit(`java.util.List<`); if (value.fieldItemType.tag === 8) { this.emit(`${collectionType(_type(value.fieldItemType.lexeme))}`); } else if (value.fieldItemType.type === 'map') { this.visitType(value.fieldItemType); } else if (value.fieldItemType.fieldType === 'array') { this.visitFieldType(value.fieldItemType, node, modelName); } else { if (node.fieldValue.itemType) { this.emit(_subModelName(node.fieldValue.itemType, this.conflictModelNameMap, this.allModleNameMap, this.enableMinimizeModelName)); } else if (value.fieldItemType) { this.emit(`${_name(value.fieldItemType)}`); } else { this.emit(`${_name(node.fieldValue.fieldItemType)}`); } } this.emit(`>`); } else if (value.fieldType === 'map') { this.emit(`java.util.Map<${collectionType(_type(value.keyType.lexeme))}, `); if (value.valueType.type) { this.visitType(value.valueType); } else { this.emit(`${collectionType(_type(value.valueType.lexeme))}`); } this.emit('>'); } else if (typeof value.fieldType === 'string') { this.emit(`${_type(value.fieldType)}`); } else if (value.fieldType) { if (value.fieldType.idType && value.fieldType.idType === 'module') { var className = this.imports[`${_type(value.fieldType.lexeme)}`].className || 'Client'; this.emit(this.imports[`${_type(value.fieldType.lexeme)}`].package); this.emit(`.${className}`); } else if (value.fieldType.idType && value.fieldType.idType === 'typedef') { this.emit(this.typeRelover(value.fieldType)); } else if (value.fieldType.type && value.fieldType.type === 'moduleModel') { this.emit(this.imports[_name(value.fieldType.path[0])].package); this.emit(`.models.${_name(value.fieldType.path[1])}`); } else if (value.fieldType.type && value.fieldType.type === 'moduleTypedef') { for (let i = 1; i < value.fieldType.path.length; i++) { this.emit(this.typeRelover(value.fieldType.path[i], value.fieldType.path[0])); } } else { this.emit(`${_type(value.fieldType.lexeme)}`); } } else { this.emit(_subModelName([modelName, _name(node.fieldName)].join('.'), this.conflictModelNameMap, this.allModleNameMap, this.enableMinimizeModelName)); } } visitModelBody(ast, level, modelName) { assert.equal(ast.type, 'modelBody'); let node; for (let i = 0; i < ast.nodes.length; i++) { node = ast.nodes[i]; let comments = DSL.comment.getFrontComments(this.comments, node.tokenRange[0]); this.visitComments(comments, level); const value = node.fieldValue; const realName = getAttr(node, 'name') || _name(node.fieldName); const description = getAttr(node, 'description'); const example = getAttr(node, 'example'); const checkBlank = getAttr(node, 'checkBlank'); const nullable = getAttr(node, 'nullable'); const sensitive = getAttr(node, 'sensitive'); const pattern = getAttr(node, 'pattern') || ''; const maxLength = getAttr(node, 'maxLength') || 0; const minLength = getAttr(node, 'minLength') || 0; const maximum = getAttr(node, 'maximum') || 0; const minimum = getAttr(node, 'minimum') || 0; const required = node.required || false; const deprecated = getAttr(node, 'deprecated'); let hasNextSection = false; if (description || example || typeof checkBlank !== 'undefined' || typeof nullable !== 'undefined' || typeof sensitive !== 'undefined') { this.emit('/**\n', level); if (description) { const descriptions = md2Html(description).trimEnd().split('\n'); for (let j = 0; j < descriptions.length; j++) { this.emit(` * ${_doc(descriptions[j])}\n`, level); } hasNextSection = true; } if (example) { if (hasNextSection) { this.emit(' * \n', level); } const examples = md2Html(example).trimEnd().split('\n'); this.emit(' * <strong>example:</strong>\n', level); for (let j = 0; j < examples.length; j++) { this.emit(` * ${_doc(examples[j])}\n`, level); } hasNextSection = true; } if (typeof checkBlank !== 'undefined') { if (hasNextSection) { this.emit(' * \n', level); } this.emit(' * <strong>check if is blank:</strong>\n', level); this.emit(` * <p>${checkBlank}</p>\n`, level); hasNextSection = true; } if (typeof nullable !== 'undefined') { if (hasNextSection) { this.emit(' * \n', level); } this.emit(' * <strong>if can be null:</strong>\n', level); this.emit(` * <p>${nullable}</p>\n`, level); hasNextSection = true; } if (typeof sensitive !== 'undefined') { if (hasNextSection) { this.emit(' * \n', level); } this.emit(' * <strong>if sensitive:</strong>\n', level); this.emit(` * <p>${sensitive}</p>\n`, level); } this.emit(' */\n', level); } this.emit(`@NameInMap("${_doc(realName)}")\n`, level); if (deprecated === 'true') { this.emit(`@Deprecated\n`, level); } if (required || maxLength > 0 || maximum > 0 || pattern !== '') { var validationAnnotation = '@Validation('; if (required) { validationAnnotation += `required = ${required}`; } if (pattern !== '') { if (!validationAnnotation.endsWith('(')) { validationAnnotation += ', '; } validationAnnotation += `pattern = "${pattern}"`; } // 不能超过Java中Integer最大值 if (maxLength > 0 && maxLength <= 2147483647) { if (!validationAnnotation.endsWith('(')) { validationAnnotation += ', '; } validationAnnotation += `maxLength = ${maxLength}`; } // 不能超过Java中Integer最大值 if (minLength > 0 && minLength <= 2147483647) { if (!validationAnnotation.endsWith('(')) { validationAnnotation += ', '; } validationAnnotation += `minLength = ${minLength}`; } // 不能超过JS中最大安全整数 if (maximum > 0 && maximum <= Number.MAX_SAFE_INTEGER) { if (!validationAnnotation.endsWith('(')) { validationAnnotation += ', '; } validationAnnotation += `maximum = ${maximum}`; if (maximum > 2147483647) { validationAnnotation += 'D'; } } // 不能超过JS中最大安全整数 if (minimum > 0 && minimum <= Number.MAX_SAFE_INTEGER) { if (!validationAnnotation.endsWith('(')) { validationAnnotation += ', '; } validationAnnotation += `minimum = ${minimum}`; if (minimum > 2147483647) { validationAnnotation += 'D'; } } this.emit(validationAnnotation, level); this.emit(')\n'); } this.emit('public ', level); this.visitFieldType(value, node, modelName); this.emit(` ${avoidReserveName(_name(node.fieldName))};\n`); this.emit('\n'); } if (node) { //find the last node's back comment let comments = DSL.comment.getBetweenComments(this.comments, node.tokenRange[0], ast.tokenRange[1]); this.visitComments(comments, level); } if (ast.nodes.length === 0) { //empty block's comment let comments = DSL.comment.getBetweenComments(this.comments, ast.tokenRange[0], ast.tokenRange[1]); this.visitComments(comments, level); } } createGetSetMethod(ast, level, modelName) { assert.equal(ast.type, 'modelBody'); let node; for (let i = 0; i < ast.nodes.length; i++) { node = ast.nodes[i]; const value = node.fieldValue; const deprecated = getAttr(node, 'deprecated'); let fieldName = _name(node.fieldName); if (deprecated === 'true') { this.emit(`@Deprecated\n`, level); } this.emit(`public ${_subModelName(modelName, this.conflictModelNameMap, this.allModleNameMap, this.enableMinimizeModelName)} set`, level); this.emit(`${_upperFirst(fieldName)}(`); this.visitFieldType(value, node, modelName); this.emit(` ${fieldName}) {\n`); this.emit(`this.${fieldName} = ${fieldName};\n`, level + 1); this.emit('return this;\n', level + 1); this.emit('}\n', level); this.emit('public ', level); this.visitFieldType(value, node, modelName); this.emit(' get'); this.emit(`${_upperFirst(fieldName)}() {\n`); this.emit(`return this.${fieldName};\n`, level + 1); this.emit('}\n\n', level); } } eachModel(ast, level) { assert.equal(ast.type, 'model'); const modelName = _name(ast.modelName); this.visitAnnotation(ast.annotation, level); this.emit(`public class ${_subModelName(modelName, this.conflictModelNameMap, this.allModleNameMap, this.enableMinimizeModelName)} extends TeaModel {\n`, level); this.visitModelBody(ast.modelBody, level + 1, modelName); this.visitBuildMethod(ast, level + 1); this.createGetSetMethod(ast.modelBody, level + 1, modelName); } visitBuildMethod(ast, level) { var className = this.getSubFieldClassName(ast.modelName.lexeme); this.emit(`public static ${className} build(java.util.Map<String, ?> map)`, level); if (!this.NoException) { this.emit(` throws Exception`); } this.emit(` {\n`); this.emit(`${className} self = new ${className}();\n`, level + 1); this.emit('return TeaModel.build(map, self);\n', level + 1); this.emit(`}\n\n`, level); } eachSubModel(ast, level) { assert.equal(ast.type, 'model'); const modelName = _name(ast.modelName); this.visitAnnotation(ast.annotation, level); this.emit(`public static class ${_subModelName(modelName, this.conflictModelNameMap, this.allModleNameMap, this.enableMinimizeModelName)} extends TeaModel {\n`, level); this.visitModelBody(ast.modelBody, level + 1, modelName); this.visitBuildMethod(ast, level + 1); this.createGetSetMethod(ast.modelBody, level + 1, modelName); this.emit('}\n\n', level); } visitObjectFieldValue(ast, level) { this.visitExpr(ast, level); } visitObjectField(ast, level) { let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]); this.visitComments(comments, level); if (ast.type === 'objectField') { var key = _escape(_name(ast.fieldName) || _string(ast.fieldName)); this.emit(`new TeaPair("${key}", `, level); this.visitObjectFieldValue(ast.expr, level); } else { throw new Error('unimpelemented'); } this.emit(')'); } visitObject(ast, level) { assert.equal(ast.type, 'object'); if (ast.fields.length === 0) { this.emit('new java.util.HashMap<>()'); return; } var hasExpandField = false; var hasNotExpandField = false; for (let i = 0; i < ast.fields.length; i++) { const field = ast.fields[i]; if (field.type === 'expandField') { hasExpandField = true; break; } else { hasNotExpandField = true; } } if (!hasExpandField) { this.emit('TeaConverter.buildMap(\n'); for (let i = 0; i < ast.fields.length; i++) { this.visitObjectField(ast.fields[i], level + 1); if (i < ast.fields.length - 1) { this.emit(','); } this.emit('\n'); } this.emit(')', level); return; } var all = []; // 分段 var current = []; for (let i = 0; i < ast.fields.length; i++) { const field = ast.fields[i]; if (field.type === 'objectField') { current.push(field); } else { if (current.length > 0) { all.push(current); } all.push(field); current = []; } } if (current.length > 0) { all.push(current); } this.emit('TeaConverter.merge('); if (ast.inferred && ast.inferred.valueType.name === 'string') { this.emit('String.class'); } else { this.emit('Object.class'); } var hasExpandFieldBuildMap = false; if (hasExpandField && hasNotExpandField) { hasExpandFieldBuildMap = true; this.emit(',\n'); this.emit('TeaConverter.buildMap(\n', level + 1); } else { this.emit(',\n'); } for (let i = 0; i < all.length; i++) { const item = all[i]; if (Array.isArray(item)) { for (var j = 0; j < item.length; j++) { this.visitObjectField(item[j], level + 2); if (item[j + 1]) { this.emit(',\n'); } else { this.emit('\n'); } } } else { this.emit('', level + 1); this.visitExpr(item.expr, level); if (all[i + 1]) { this.emit(','); } this.emit('\n'); } if (hasExpandFieldBuildMap) { this.emit(')', level + 1); if (all[i + 1]) { this.emit(',\n'); } else { this.emit('\n'); } hasExpandFieldBuildMap = false; } } this.emit(')', level); } visitCall(ast, level) { assert.equal(ast.type, 'call'); if (ast.left.type === 'method_call') { this.visitMethodCall(ast, level); } else if (ast.left.type === 'instance_call') { this.visitInstanceCall(ast, level); } else if (ast.left.type === 'static_call') { this.visitStaticCall(ast, level); } else { throw new Error('unimplemented'); } } visitStaticCall(ast, level) { assert.equal(ast.left.type, 'static_call'); var className = this.imports[ast.left.id.lexeme].className || 'Client'; this.emit(`${this.imports[ast.left.id.lexeme].package}.${className}.${_name(ast.left.propertyPath[0])}(`); for (let i = 0; i < ast.args.length; i++) { const expr = ast.args[i]; if (expr.needCast) { this.emit('TeaModel.buildMap('); } this.visitExpr(expr, level); if (expr.needCast) { this.emit(')'); } if (i !== ast.args.length - 1) { this.emit(', '); } } this.emit(')'); } visitInstanceCall(ast, level) { assert.equal(ast.left.type, 'instance_call'); const method = ast.left.propertyPath[0]; var id = _name(ast.left.id); if (id.indexOf('@') > -1) { id = `_${_lowerFirst(id.substr(1))}`; } this.emit(`${id}.${_name(method)}(`); for (let i = 0; i < ast.args.length; i++) { const expr = ast.args[i]; this.visitExpr(expr, level); if (i !== ast.args.length - 1) { this.emit(', '); } } this.emit(')'); } visitMethodCall(ast, level) { assert.equal(ast.left.type, 'method_call'); if (ast.isStatic) { var className = this.className || 'Client'; this.emit(`${className}.${_name(ast.left.id)}(`); } else { this.emit(`this.${_name(ast.left.id)}(`); } for (let i = 0; i < ast.args.length; i++) { const expr = ast.args[i]; if (expr.needCast) { this.emit('TeaModel.buildMap('); } this.visitExpr(expr, level); if (expr.needCast) { this.emit(')'); } if (i !== ast.args.length - 1) { this.emit(', '); } } this.emit(')'); } visitPropertyAccess(ast) { assert.equal(ast.type, 'property_access'); var id = _name(ast.id); var expr = ''; if (id === '__response') { expr += RESPONSE; } else if (id === '__request') { expr += REQUEST; } else { expr += avoidReserveName(id); } var current = ast.id.inferred; for (var i = 0; i < ast.propertyPath.length; i++) { var name = _name(ast.propertyPath[i]); if (current.type === 'model') { expr += `.${name}`; } else { expr += `.get("${name}")`; } current = ast.propertyPathTypes[i]; } this.emit(expr); } emitNumber(ast, level) { this.emit(ast.value.value, level); if (ast.value.type === 'long') { this.emit('L'); } if (ast.value.type === 'double') { this.emit('D'); } if (ast.value.type === 'float') { this.emit('F'); } } visitExpr(ast, level) { if (ast.type === 'boolean') { this.emit(`${ast.value}`); } else if (ast.type === 'property_access') { this.visitPropertyAccess(ast, level); } else if (ast.type === 'string') { this.emit(`"${ast.value.string.replace(new RegExp('"', 'g'), '\\"')}"`); } else if (ast.type === 'null') { this.emit('null'); } else if (ast.type === 'number') { this.emitNumber(ast); } else if (ast.type === 'object') { this.visitObject(ast, level); } else if (ast.type === 'variable') { var id = _name(ast.id); if (id === '__response') { this.emit(RESPONSE); } else if (id === '__request') { this.emit(REQUEST); } else if (ast.inferred && ast.inferred.name === 'class') { this.emit(avoidReserveName(id) + '.class'); } else { this.emit(avoidReserveName(id)); } } else if (ast.type === 'virtualVariable') { const vid = `_${_lowerFirst(_name(ast.vid).substr(1))}`; this.emit(`${vid}`); } else if (ast.type === 'template_string') { for (let i = 0; i < ast.elements.length; i++) { var item = ast.elements[i]; if (item.type === 'element') { this.emit('"'); this.emit(item.value.string); this.emit('"'); } else if (item.type === 'expr') { if (item.expr.type === 'property_access' && _name(item.expr.id) === '__module') { var value = this.__module; for (let i = 0; i < item.expr.propertyPath.length; i++) { value = value[_name(item.expr.propertyPath[i])]; } this.emit('"'); this.emit(value); this.emit('"'); } else { this.visitExpr(item.expr, level); } } else { throw new Error('unimpelemented'); } if (i < ast.elements.length - 1) { this.emit(' + '); } } } else if (ast.type === 'call') { this.visitCall(ast, level); } else if (ast.type === 'construct') { this.visitConstruct(ast, level); } else if (ast.type === 'array') { this.visitArray(ast, level); } else if (ast.type === 'and') { this.visitExpr(ast.left, level); this.emit(' && '); this.visitExpr(ast.right, level); } else if (ast.type === 'or') { this.visitExpr(ast.left, level); this.emit(' || '); this.visitExpr(ast.right, level); } else if (ast.type === 'null') { this.emit('null'); } else if (ast.type === 'not') { this.emit('!'); this.visitExpr(ast.expr, level); } else if (ast.type === 'construct_model') { this.visitConstructModel(ast, level); } else if (ast.type === 'super') { this.emit('super('); if (ast.args) { for (let i = 0; i < ast.args.length; i++) { if (i > 0) { this.emit(', '); } this.visitExpr(ast.args[i], level); } } this.emit(')'); } else if (ast.type === 'map_access') { this.visitMapAccess(ast, true); } else if (ast.type === 'array_access') { this.visitArrayAccess(ast, true); } else { throw new Error('unimpelemented'); } } visitMapAccess(ast, isExpr, level) { assert.equal(ast.type, 'map_access'); let expr = _name(ast.id); if (expr.indexOf('@') > -1) { expr = `_${_lowerFirst(expr.substr(1))}`; } if (ast.propertyPath && ast.propertyPath.length) { var current = ast.id.inferred; for (var i = 0; i < ast.propertyPath.length; i++) { var name = _name(ast.propertyPath[i]); if (current.type === 'model') { expr += `.${name}`; } else { expr += `.get("${name}")`; } current = ast.propertyPathTypes[i]; } } if (isExpr) { this.emit(`${expr}.get(`); this.visitExpr(ast.accessKey); this.emit(`)`); } else { this.emit(`${expr}.put(`, level); this.visitExpr(ast.accessKey); this.emit(`, `); } } visitArrayAccess(ast, isExpr, level) { assert.equal(ast.type, 'array_access'); let expr = _name(ast.id); if (expr.indexOf('@') > -1) { expr = `_${_lowerFirst(expr.substr(1))}`; } if (ast.propertyPath && ast.propertyPath.length) { var current = ast.id.inferred; for (var i = 0; i < ast.propertyPath.length; i++) { var name = _name(ast.propertyPath[i]); if (current.type === 'model') { expr += `.${name}`; } else { expr += `.get("${name}")`; } current = ast.propertyPathTypes[i]; } } if (isExpr) { this.emit(`${expr}.get(`); this.visitExpr(ast.accessKey); this.emit(`)`); } else { this.emit(`${expr}.set(`, level); this.visitExpr(ast.accessKey); this.emit(', '); } } visitConstruct(ast, level) { assert.equal(ast.type, 'construct'); this.emit('new '); let className = this.imports[ast.inferred.name].className || 'Client'; let pathName = this.imports[ast.inferred.name].package; this.emit(`${pathName}.${className}`); this.visitArgs(ast.args, level); } visitArgs(args, level) { this.emit('('); for (let i = 0; i < args.length; i++) { const expr = args[i]; this.visitExpr(expr, level); if (i !== args.length - 1) { this.emit(', '); } } this.emit(')'); } visitArray(ast, level) { assert.equal(ast.type, 'array'); if (ast.items.length === 0) { this.emit('new java.util.ArrayList<>()'); return; } this.emit('java.util.Arrays.asList(\n'); for (let i = 0; i < ast.items.length; i++) { const item = ast.items[i]; var comments = DSL.comment.getFrontComments(this.comments, item.tokenRange[0]); this.visitComments(comments, level + 1); this.emit('', level + 1); this.visitExpr(item, level + 1); if (i < ast.items.length - 1) { this.emit(','); } this.emit('\n'); } this.emit(')', level); } visitConstructModel(ast, level) { assert.equal(ast.type, 'construct_model'); if (ast.object && ast.object.fields && ast.object.fields.length > 0 && !this.exec) { this.visitType(ast.inferred); this.emit(`.build(`); this.visitObject(ast.object, level); this.emit(`)`); return; } this.emit(`new `); this.visitType(ast.inferred); this.emit(`()`); if (this.exec) { this.visitSetMethod(ast.object, level); } } visitSetMethod(ast, level) { let classFieldName = ''; for (let i = 0; i < ast.fields.length; i++) { classFieldName = _name(ast.fields[i].fieldName); this.emit('\n'); let comments = DSL.comment.getFrontComments(this.comments, ast.fields[i].tokenRange[0]); this.visitComments(comments, level + 2); this.emit('', level + 2); this.emit(`.set${_upperFirst(classFieldName)}(`); this.visitObjectFieldValue(ast.fields[i].expr, level + 2); this.emit(')'); } } visitReturn(ast, level) { assert.equal(ast.type, 'return'); this.emit('return ', level); if (!ast.expr) { this.emit(';\n'); return; } if (ast.needCast) { this.emit('TeaModel.toModel('); } this.visitExpr(ast.expr, level); if (ast.needCast) { this.emit(`, new `); this.visitType(ast.expectedType); this.emit(`())`); } this.emit(';\n'); } visitRetry(ast, level) { assert.equal(ast.type, 'retry'); this.emit(`throw new TeaRetryableException();\n`, level); } visitIf(ast, level) { assert.equal(ast.type, 'if'); this.emit('if (', level); this.visitExpr(ast.condition, level + 1); this.emit(') {\n'); this.visitStmts(ast.stmts, level + 1); this.emit('}', level); if (ast.elseIfs) { for (let i = 0; i < ast.elseIfs.length; i++) { const branch = ast.elseIfs[i]; this.emit(' else if ('); this.visitExpr(branch.condition, level + 1); this.emit(') {\n'); this.visitStmts(branch.stmts, level + 1); this.emit('}', level); } } if (ast.elseStmts) { this.emit(' else {\n'); for (let i = 0; i < ast.elseStmts.stmts.length; i++) { this.visitStmt(ast.elseStmts.stmts[i], level + 1); } this.emit('}', level); } this.emit('\n'); this.emit('\n'); } visitThrow(ast, level) { this.emit('throw new TeaException(', level); this.visitObject(ast.expr, level); this.emit(');\n'); } visitAssign(ast, level) { var isCollection = false; if (ast.left.type === 'id') { this.emit(`${_name(ast.left.id)}`, level); } else if (ast.left.type === 'property_assign' || ast.left.type === 'property') { var id = _name(ast.left.id); if (id === '__request') { id = 'request_'; } this.emit(`${id}`, level); for (var i = 0; i < ast.left.propertyPath.length; i++) { if ((i === ast.left.propertyPath.length - 1 && ast.left.propertyPathTypes[i - 1] && ast.left.propertyPathTypes[i - 1].type === 'map') || (ast.left.id.inferred && ast.left.id.inferred.type === 'map')) { this.emit(`.put("${_name(ast.left.propertyPath[i])}", `); isCollection = true; } else { this.emit(`.${_name(ast.left.propertyPath[i])}`); } } } else if (ast.left.type === 'virtualVariable') { this.emit(`this._${_name(ast.left.vid).substr(1)}`, level); } else if (ast.left.type === 'variable') { this.emit(`${_name(ast.left.id)}`, level); } else if (ast.left.type === 'map_access') { isCollection = true; this.visitMapAccess(ast.left, false, level); } else if (ast.left.type === 'array_access') { isCollection = true; this.visitArrayAccess(ast.left, false, level); } else { throw new Error('unimpelemented'); } if (!isCollection) { this.emit(' = '); } if (ast.expr.needToReadable) { this.emit('Tea.toReadable('); } this.visitExpr(ast.expr, level); if (isCollection) { this.emit(')'); } if (ast.expr.needToReadable) { this.emit(')'); } this.emit(';\n'); } visitDeclare(ast, level) { var id = _name(ast.id); this.emit(``, level); this.visitType(ast.expr.inferred); this.emit(` ${id} = `); this.visitExpr(ast.expr, level); this.emit(';\n'); } visitComments(comments, level) { comments.forEach(comment => { this.emit(`${comment.value}`, level); this.emit(`\n`); }); } visitStmts(ast, level) { assert.equal(ast.type, 'stmts'); let node; for (var i = 0; i < ast.stmts.length; i++) { node = ast.stmts[i]; this.visitStmt(node, level); } if (node) { //find the last node's back comment let comments = DSL.comment.getBackComments(this.comments, node.tokenRange[1]); this.visitComments(comments, level); } if (ast.stmts.length === 0) { //empty block's comment let comments = DSL.comment.getBetweenComments(this.comments, ast.tokenRange[0], ast.tokenRange[1]); this.visitComments(comments, level); } } visitReturnBody(ast, level) { assert.equal(ast.type, 'returnBody'); this.emit('\n'); this.visitStmts(ast.stmts, level); } visitFunctionBody(ast, level) { assert.equal(ast.type, 'functionBody'); this.visitStmts(ast.stmts, level); } eachFunction(ast, level) { this.visitAnnotation(ast.annotation, level); let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]); this.visitComments(comments, level); if (this.implements && !ast.isStatic) { this.emit(`@Override\n`, level); } this.emit('public ', level); if (ast.isStatic) { this.emit('static '); } this.visitType(ast.returnType); 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 { this.visitParams(ast.params, level); } if (!this.NoException) { this.emit(' throws Exception'); } this.emit(' {\n'); if (ast.functionBody) { if (_name(ast.functionName) === 'main') { const args = _name(ast.params.params[0].paramName); this.emit(`java.util.List<String> ${args} = java.util.Arrays.asList(${args}_);\n`, level + 1); } this.visitFunctionBody(ast.functionBody, level + 1); } this.emit('}\n', level); } eachType(ast, level) { this.emit('public ', level); this.visitType(ast.value); this.emit(` _${_lowerFirst(_name(ast.vid).substr(1))};`); } eachInit(ast, level) { var className = this.className || 'Client'; this.visitAnnotation(ast.annotation, level); let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]); this.visitComments(comments, level); this.emit(`public ${className}`, level); this.visitParams(ast.params, level); if (!this.NoException) { this.emit(' throws Exception'); } this.emit(' {\n'); if (ast.initBody) { this.visitStmts(ast.initBody, level + 1); } this.emit('}\n\n', level); } visitApiInterface(apis, level) { for (let i = 0; i < apis.length; i++) { this.visitAnnotation(apis[i].annotation, level); this.emit('', level); this.visitType(apis[i].returnType); this.emit(` ${_name(apis[i].apiName)}`); this.visitParams(apis[i].params, level); this.emit(';\n\n'); } } visitFunctionInterface(functions, level) { for (let i = 0; i < functions.length; i++) { if (_name(functions[i].functionName) === 'main') { continue; } this.visitAnnotation(functions[i].annotation, level); let comments = DSL.comment.getFrontComments(this.comments, functions[i].tokenRange[0]); this.visitComments(comments, level); this.emit('', level); this.visitType(functions[i].returnType); this.emit(` ${_name(functions[i].functionName)}`); this.visitParams(functions[i].params, level); this.emit(';\n\n'); } } eachAPI(ast, level) { this.visitAnnotation(ast.annotation, level); let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]); this.visitComments(comments, level); this.emit('public ', level); this.visitType(ast.returnType); this.emit(` ${_name(ast.apiName)}`); this.visitParams(ast.params, level); if (!this.NoException) { this.emit(' throws Exception'); } this.emit(' {\n'); for (var i = 0; i < ast.params.params.length; i++) { const param = ast.params.params[i]; if (_name(param.paramType) && !DSL.util.isBasicType(_name(param.paramType)) && param.paramName.lexeme !== 'client') { this.emit(`TeaModel.validateParams(${param.paramName.lexeme}, "${param.paramName.lexeme}");\n`, level + 1); } } let baseLevel = ast.runtimeBody ? level + 2 : level; // api level if (ast.runtimeBody) { this.visitRuntimeBefore(ast.runtimeBody, level + 1); } this.visitAPIBody(ast.apiBody, baseLevel + 1); if (ast.runtimeBody) { this.emit(`_lastRequest = ${REQUEST};\n`, baseLevel + 1); } this.emit(`TeaResponse ${RESPONSE} = Tea.doAction(${REQUEST}`, baseLevel + 1); if (ast.runtimeBody) { this.emit(`, ${RUNTIME}`); } else { this.emit(`, new java.util.HashMap<String, Object>()`); } this.emit(', interceptorChain);\n'); if (ast.returns) { this.visitReturnBody(ast.returns, baseLevel + 1); } if (ast.runtimeBody) { this.visitRuntimeAfter(ast.runtimeBody, level + 1); } this.emit('}\n', level); } visitRuntimeAfter(ast, level) { this.emit('} catch (Exception e) {\n', level + 1); this.emit('if (Tea.isRetryable(e)) {\n', level + 2); this.emit('_lastException = e;\n', level + 3); this.emit('continue;\n', level + 3); this.emit('}\n', level + 2); if (!this.NoException) { this.emit('throw e;\n', level + 2); } else { this.emit('if (e instanceof TeaException) {\n', level + 2); this.emit('throw e;\n', level + 3); this.emit('}\n', level + 2); this.emit('throw new TeaException(e.getMessage(), e);\n', level + 2); } this.emit('}\n', level + 1); this.emit('}\n', level); this.emit('throw new TeaUnretryableException(_lastRequest, _lastException);\n', level); } visitImport() { } importBefore(level) { if (this.editable !== true) { this.emit(`// This file is auto-generated, don't edit it. Thanks.\n`, level); } } modelBefore() { if (this.editable !== true) { this.emit(`// This file is auto-generated, don't edit it. Thanks.\n`); } this.emit(`package ${this.package}.models; import com.aliyun.tea.*; `); } apiBefore(extendParam, extendsClass, hasAPI, level) { if (this.editable !== true) { this.emit(`// This file is auto-generated, don't edit it. Thanks.\n`); } this.emit(`package ${this.package}; import com.aliyun.tea.*; `); if (hasAPI) { this.emit('import com.aliyun.tea.interceptor.InterceptorChain;\n'); this.emit('import com.aliyun.tea.interceptor.RuntimeOptionsInterceptor;\n'); this.emit('import com.aliyun.tea.interceptor.RequestInterceptor;\n'); this.emit('import com.aliyun.tea.interceptor.ResponseInterceptor;\n'); } if (extendParam.writeImport) { this.emit(`import ${this.package}.models.*;\n`); } this.visitImport(); this.emit(` public class ${this.className || 'Client'}`); if (extendsClass) { this.emit(` extends ${extendsClass.package + '.' + extendsClass.className}`); } if (this.implements) { this.emit(` implements ${this.implements}`); } this.emit(` {\n`); if (hasAPI) { this.emit(`\n`); this.emit('private final static InterceptorChain interceptorChain = InterceptorChain.create();\n', 1); } } wrapBefore() { this.emit(`\n`); } apiAfter(hasAPI, level) { if (hasAPI) { this.emit(`\n`); this.emit('public void addRuntimeOptionsInterceptor(RuntimeOptionsInterceptor interceptor) {\n', level); this.emit('interceptorChain.addRuntimeOptionsInterceptor(interceptor);\n', level + 1); this.emit('}\n', level); this.emit(`\n`); this.emit('public void addRequestInterceptor(RequestInterceptor interceptor) {\n', level); this.emit('interceptorChain.addRequestInterceptor(interceptor);\n', level + 1); this.emit('}\n', level); this.emit(`\n`); this.emit('public void addResponseInterceptor(ResponseInterceptor interceptor) {\n', level); this.emit('interceptorChain.addResponseInterceptor(interceptor);\n', level + 1); this.emit('}\n', level); } } typeRelover(type, module) { if (module && module.idType === 'module') { const aliasId = _name(module); if (this.imports[aliasId] && this.imports[aliasId].typedef && this.imports[aliasId].typedef[type.lexeme]) { let reslut = this.imports[aliasId].typedef[type.lexeme].import; if (this.imports[aliasId].typedef[type.lexeme].type) { reslut = `${reslut}.${this.imports[aliasId].typedef[type.lexeme].type}`; } return reslut; } } if (type.idType === 'typedef' && this.typedef[type.lexeme]) { let reslut = this.typedef[type.lexeme].import; if (this.typedef[type.lexeme].type) { reslut = `${reslut}.${this.typedef[type.lexeme].type}`; } return reslut; } return _type(type); } } module.exports = Visitor;