lib/generator.js (170 lines of code) (raw):
/* eslint-disable max-len */
'use strict';
// system modules
const fs = require('fs');
const path = require('path');
const xml2js = require('xml2js');
const Entities = require('html-entities').XmlEntities;
const assert = require('assert');
const { _name, remove, save, mkdir, render, parse, _type } = require('./util');
// inner modules
const ClientGenerator = require('./client_generator');
const ModelGenerator = require('./model_generator');
const EnumGenerator = require('./enum_generator');
const IteratorGenerator = require('./iterator_generator');
class Generator {
constructor(option = {}) {
const javaPackage = option && option.java && option.java.package;
if (!javaPackage) {
throw new Error(`Darafile -> java -> javaPackage should not empty, please add java option into Darafile.example:
"java": {"package": "com.aliyun.test"}`);
}
this.exec = option.exec || option.java.exec;
this.package = javaPackage;
this.pomOutputDir = option.outputDir;
this.packageInfo = option.java.packageInfo;
this.baseClient = option && option.baseClient || javaPackage + '.Client';
mkdir(path.join(option.outputDir, 'src/it/java', ...javaPackage.split('.')));
mkdir(path.join(option.outputDir, 'src/test'));
option.outputDir = path.join(option.outputDir, 'src/main/java', ...javaPackage.split('.'));
this.outputDir = option.outputDir;
this.conflictModelNameMap = {};
this.allModleNameMap = {};
if (option.java) {
this.className = option.java.className || 'DefaultAsyncClient';
this.implements = option.java.implements;
}
this.productId = option.productId;
this.apiVersions = option.apiVersions;
this.config = Object.assign({
outputDir: '',
}, option);
remove(path.join(this.outputDir, 'models/'));
}
visit(ast, level = 0) {
assert.equal(ast.type, 'module');
this.comments = ast.comments;
this.usedExternModel = ast.usedExternModel;
this.conflictModels = ast.conflictModels;
this.eachImport(ast.imports);
this.visitPom();
if (ast.extends) {
const extendsName = _name(ast.extends);
this.extendsPackage = this.imports[extendsName].package;
}
const models = ast.moduleBody.nodes.filter((item) => {
return item.type === 'model';
});
const enums = ast.moduleBody.nodes.filter((item) => {
return item.type === 'enum';
});
this.enumMap = {};
for (let i = 0; i < enums.length; i++) {
const enumName = enums[i].enumName.lexeme;
const enumType = _type(enums[i].enumType.lexeme);
this.enumMap[enumName] = enumType;
// enum generator
const enum_generator = new EnumGenerator(enums[i], this);
enum_generator.codegen();
save(path.join(this.outputDir, 'models', `${enumName}.java`), enum_generator.output);
}
// const baseModel = new ModelGenerator(ast, this);
// baseModel.codegenRequest();
// save(path.join(this.outputDir, 'models', 'Request.java'), baseModel.output);
// baseModel.output = '';
// baseModel.codegenResponse();
// save(path.join(this.outputDir, 'models', 'Response.java'), baseModel.output);
// global definition
let model;
for (let i = 0; i < models.length; i++) {
const modelName = models[i].modelName.lexeme;
this.subModels = [];
if (ast.models) {
this.subModels = Object.keys(ast.models).filter((key) => {
return key.startsWith(modelName + '.');
}).map((key) => {
return ast.models[key];
});
}
// model generator
model = new ModelGenerator(models[i], this);
model.codegen();
save(path.join(this.outputDir, 'models', `${modelName}.java`), model.output);
}
// client generator
this.iterators = [];
const client = new ClientGenerator(ast, this);
client.codegen();
save(path.join(this.outputDir, `${this.className}.java`), client.output);
let iterator;
for (let i = 0; i < this.iterators.length; i++) {
iterator = new IteratorGenerator(this.iterators[i], this);
iterator.codegen();
save(path.join(this.outputDir, 'models', `${_name(this.iterators[i].iteratorType)}Iterator.java`), iterator.output);
}
}
async visitPom() {
var entities = new Entities();
var targetFile = path.join(this.pomOutputDir, 'pom.xml');
let pomName = 'pom.xml';
if (this.exec) {
pomName = 'pomWithMain.xml';
}
var pomFile = fs.readFileSync(path.join(__dirname, '../template', pomName));
var dependenciesClass = [];
Object.keys(this.imports).forEach((key) => {
let dependency = this.imports[key].release.split(':');
if (dependency[1].startsWith('aliyun-gateway-')) {
if (!dependenciesClass.includes(`${dependency[0]}:${dependency[1]}:` + '${service.version}')) {
dependenciesClass.push(`${dependency[0]}:${dependency[1]}:` + '${service.version}');
}
} else if (dependency[1] !== 'darabonba-java-core') {
dependenciesClass.push(this.imports[key].release);
}
});
const json = await parse(pomFile);
if (!json.project.dependencies || !json.project.dependencies[0].dependency) {
json.project.dependencies = [{ dependency: [] }];
}
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[0].dependency.push(dependencyObject);
}
});
const builder = new xml2js.Builder();
let newPom = builder.buildObject(json);
if (this.exec) {
let mainClassPath = this.package + '.' + (this.className || 'DefaultAsyncClient');
newPom = newPom.split('${mainClass}').join(mainClassPath);
}
newPom = render(newPom, this.packageInfo);
save(targetFile, entities.decode(newPom));
}
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;
}
}
}
module.exports = Generator;