UNPKG

percli

Version:
307 lines (275 loc) 11.3 kB
const fs = require('fs') const path = require('path') /** Class representing the sling model generator */ class Generator { /** * Constructs the generator * @param {string} source - the source * @param {string} dest - the destination * @param {string} info - the info */ constructor(source, dest, info) { this.src = source; this.dst = dest; this.data = info; } } /** * Substitutes all the values into the template * @param {string} srcData - the source data string * @param {string} generator - the generator object * @return {string} the template */ function makeInitial(srcData, generator) { let definitions = generator.data.definitions[generator.data.modelName] let properties = definitions.properties var data = { imports: genImports(properties), ...generator.data } var template = srcData.replace(/{{.*?}}/g, function(str, _, _, _) { var key = str.trim().slice(2,str.trim().length-2).trim() var ret = data[key] if(ret === null || ret === undefined) { console.log('missing property:', key) ret = `!!!${key}!!` } return ret }) return template } function genImportsSet(properties, target) { for(var propName in properties) { var props = properties[propName] var annotate = props['x-annotate'] if (propName !== 'children') { if(props['x-source'] == 'inject' && annotate) { forEachSizeAnnotation(annotate, _ => {console.log(`${propName}: ${JSON.stringify(props)}`) target.add('com.peregrine.model.api.ImageInfo') target.add('java.awt.Dimension') }) } else if(props['x-form-type'] === 'reference') { genImportsSet(props.properties, target) } } } return target } function genImports(properties) { return Array.from(genImportsSet(properties, new Set())) .reduce((all, next) => `${all}import ${next};\n`, '') } function forEachSizeAnnotation(crudeValues, callback) { (typeof crudeValues === 'string' ? [crudeValues] : crudeValues) .filter(a => a === 'size') .forEach(callback) } /** * Used in the gen function and applies all of the inject properties * @param {string} properties - the properties from the model JSON * @return {string} the inject string block */ function genNames(properties) { inject = '' for(var propName in properties) { if (propName !== 'children') { var props = properties[propName] if(props['x-source'] === 'inject') { inject += `\t/* ${JSON.stringify(props)} */\n` inject += '\t@Inject\n' var sourceName = props['x-sourceName']; if(sourceName) { inject += `\t@Named(value ="${sourceName}")\n` } var x_default = props['x-default']; if(x_default === "" || x_default) { inject += `\t@Default(values ="${x_default}")\n` } if(props['x-form-type'] === 'collection') { if (Object.keys(props['properties']).length > 1 || props['x-form-multifield'] === "true" || props['x-form-multifield'] === true) { var collectionType = props['x-collection-type'] if (collectionType) { inject += `\tprivate List<${collectionType}Model> ` } else { inject += '\tprivate List<IComponent> ' } } else { inject += '\tprivate String[] ' } } else { inject += '\tprivate String ' } inject += `${propName};\n\n` var annotate = props['x-annotate'] if (annotate) { inject += genNamesAnnotates(propName, annotate) } } else if(props['x-form-type'] === 'reference') { inject += genNames(props.properties) } } } return inject } function genNamesAnnotates(ownerName, annotate) { var result = '' forEachSizeAnnotation(annotate, _ => { result = '\t@Inject\n' result += `\t@ImageInfo(name="${ownerName}")\n` result += `\tprivate Dimension ${ownerName}Size;\n\n` }) return result } /** * Used in the gen function and applies all of the getter methods. Has additional functionality to retain any custom getter methods * @param {string} properties - the properties from the model JSON * @param {string} customGetters - the custom getters block * @return {string} the getters string block */ function genGetters(properties, customGetters) { inject = '' for(var propName in properties) { var getterName = propName.charAt(0).toUpperCase()+propName.slice(1) if (propName !== 'children' && (customGetters === null || !customGetters.includes(`public String get${getterName}()`))) { var props = properties[propName] if(props['x-source'] == 'inject') { inject += `\t/* ${JSON.stringify(props)} */\n` if(props['x-form-type'] === 'collection') { if (Object.keys(props['properties']).length > 1 || props['x-form-multifield'] === "true" || props['x-form-multifield'] === true) { var collectionType = props['x-collection-type'] if (collectionType) { inject += `\tpublic List<${collectionType}Model> get` } else { inject += '\tpublic List<IComponent> get' } } else { inject += '\tpublic String[] get' } } else { inject += '\tpublic String get' } inject += `${getterName}() {\n` inject += `\t\treturn ${propName};\n` inject += '\t}\n\n' var annotate = props['x-annotate'] if (annotate) { inject += genGettersAnnotates(propName, annotate) } } else if(props['x-form-type'] === ('reference')) { inject += genGetters(props.properties, customGetters) } } } return inject } function genGettersAnnotates(ownerName, annotate) { var result = '' forEachSizeAnnotation(annotate, _ => { result += `\tpublic Dimension get${ownerName.charAt(0).toUpperCase()}${ownerName.slice(1)}Size() {\n` result += `\t\treturn ${ownerName}Size;\n` result += '\t}\n\n' }) return result } function genTypes(properties, generator) { for(var propName in properties) { if (propName !== 'children') { var props = properties[propName] if(props['x-source'] === 'inject') { if(props['x-form-type'] === 'collection') { if (Object.keys(props['properties']).length > 1 || props['x-form-multifield'] === "true" || props['x-form-multifield'] === true) { var collectionType = props['x-collection-type'] if (collectionType) { var { data } = generator var { componentPath, package, classNameParent } = data gen(new Generator(generator.src, path.join(path.dirname(generator.dst), `${collectionType}Model.java`), { name: collectionType, modelName: collectionType, componentPath: `${componentPath}/${propName}`, package, classNameParent, definitions: { [collectionType]: props } })) } } } } } } } /** * Loads in the sling model template and fills in the data, inject, and getters content to make the components model * @param {string} generator - the generator object */ function gen(generator) { // load the template var srcData = fs.readFileSync(generator.src).toString() // substitute all the values we know into the template srcData = makeInitial(srcData, generator) // if the generated file does not exist yet, create it with the template we just created if(!fs.existsSync(generator.dst)) { console.log('initial run for', generator.dst) fs.writeFileSync(generator.dst, srcData) } // read the current file in the file system var dstCurrent = fs.readFileSync(generator.dst).toString() //retrieve the custom getters block from the current file var customGetters = dstCurrent.match(/\/\/GEN\[:CUSTOMGETTERS[\s\S]*?\/\/GEN\]/g) if (customGetters !== null) { customGetters = customGetters[0] } // get all //GEN[: //GEN] content from the template and store it in a fragments list var fragments = new Array() srcData.replace(/\/\/GEN\[[\s\S]*?\/\/GEN\]/g, function(str) { var name = ('name',str.match(/\/\/GEN\[:.*/)[0]) fragments[name] = str return str }) let definitions = generator.data.definitions[generator.data.modelName]; let properties = definitions.properties; for(key in fragments) { var name = key.split(':')[1] var inject = '' if('DATA' === name) { inject = JSON.stringify(generator.data, true, 2) } else if('INJECT' === name) { inject = genNames(properties) } else if('GETTERS' === name) { inject = genGetters(properties, customGetters) } fragments[key] = fragments[key].replace('//GEN]', inject+'\n//GEN]') } // now replace the old fragments with the new fragments in the destination file dstCurrent = dstCurrent.replace(/\/\/GEN\[[\s\S]*?\/\/GEN\]/g, function(str, offset, s) { var name = ('name',str.match(/\/\/GEN\[:.*/)[0]) if ('CUSTOMGETTERS' === name.split(':')[1]) { return customGetters } else { return fragments[name] } }) // and write the file back to the file system try { const original = fs.readFileSync(generator.dst).toString() // only write file if not different from original if(original.valueOf() !== dstCurrent.valueOf()) { fs.writeFileSync(generator.dst, dstCurrent) } } catch(error) { fs.writeFileSync(generator.dst, dstCurrent) } genTypes(properties, generator) } module.exports = { /** * Generates the sling model for the component based on the provided data * @param {string} src - the source * @param {string} dst - the destination * @param {string} data - the data */ generate: function(src, dst, data) { gen(new Generator(src, dst, data)) } }