UNPKG

@tripsnek/tmf

Version:

TypeScript Modeling Framework - A TypeScript port of the Eclipse Modeling Framework (EMF)

540 lines (528 loc) 20.1 kB
import { TGenUtils as DU } from './tgen-utils'; import { TUtils } from '../tutils'; import { EReferenceImpl } from '../metamodel/ereference-impl'; import { EClassImpl } from '../metamodel/eclass-impl'; /** * Source code generation for *gen.ts files for EClasses. * * @author dtuohy */ export class TGeneratorGen { _pkg; _packageName; /** * Generates a template string containing all source code for the *gen.ts * file for the given EClass. * * @param eClass * @param toImport * @param genToImport * @param pkgToImport */ generate(eClass, toImport, genToImport, pkgToImport) { this._pkg = eClass.getEPackage(); this._packageName = DU.genPackageClassName(this._pkg); const genClassName = DU.genGenClassName(eClass); const apiClassName = eClass.getName(); return `${this.generateAllImportStatements(eClass, toImport, genToImport, pkgToImport)} /** * This file is source-code generated and should never be edited. It implements * the core TMF functionality for ${eClass.getName()}. */ export abstract class ${genClassName} ${this.generateExtends(eClass)} implements ${apiClassName} { /** feature declarations */ ${this.genFeatureDeclarations(eClass)} ${this.genConstructor(eClass)} ${this.genGettersAndSetters(eClass)} ${this.genEoperations(eClass)} //====================================================================== // Standard EObject behavior ${this.genEGet(eClass)} ${this.genESet(eClass)} ${this.genEIsSet(eClass)} ${this.genEUnset(eClass)} ${this.genBasicSetters(eClass)} ${this.genEInverseAdd(eClass)} ${this.genEInverseRemove(eClass)} //====================================================================== // eClass() public override eClass(): EClass { return ${this._packageName}.Literals.${DU.snakeUpperCase(eClass.getName())}; } }`; } /** * Generates import statements for all APIs, Gen classes * and packages to import. * * @param eClass * @param toImport * @param genToImport * @param pkgToImport */ generateAllImportStatements(eClass, toImport, genToImport, pkgToImport, async) { let className = eClass.getName(); if (async) className += 'Async'; const isEcore = eClass.getEPackage().getName() == 'ecore'; let result = `${isEcore ? DU.ECORE_DEFAULT_IMPORTS : DU.DEFAULT_IMPORTS} ${DU.genApiImports(eClass, toImport, `..${DU.API_PATH}`)} import { ${this._packageName} } from '../${DU.genPackageFileName(this._pkg)}'; import { ${className} } from '..${DU.API_PATH}/${DU.genClassApiName(eClass, async)}'; `; //gen and impl classes that need to be imported for (const ec of genToImport) { if (ec) { let pathToImport = './'; //only import Gen and Impl if it is an EClass (not an EEnum) if (ec instanceof EClassImpl) { if (ec.getEPackage() !== eClass.getEPackage()) { //if root package is shared, the classes are assumed to exist in the same lib, and can be imported with relative paths if (ec.getRootPackage() === eClass.getRootPackage()) { pathToImport = `${DU.getPathToTypeInOtherPkg(eClass, ec)}/`; result += `import { ${ec.getName()}Gen } from '${pathToImport + 'gen/' + DU.genClassGenName(ec)}';\n`; result += `import { ${ec.getName()}Impl } from '${pathToImport + 'impl/' + DU.genClassImplName(ec)}';\n`; } //otherwise, use non-relative paths else { console.log('WARNING: CROSS-PACKAGE IMPORTS NOT SUPPORTED'); } } else { result += `import { ${ec.getName()}Gen } from './${DU.genClassGenName(ec)}';\n`; result += `import { ${ec.getName()}Impl } from '../impl/${DU.genClassImplName(ec)}';\n`; } } } } //add package imports (e.g. for references to types in external packages) result += DU.generateImportStatementsForExternalPackages(pkgToImport, this._pkg, '../'); return result; } generateExtends(eclass) { if (eclass.getName() == 'EObject') return ''; let result = `extends EObjectImpl`; let sti = 0; for (const superType of eclass.getESuperTypes()) { //TODO: Add support for multiple-inheritance on implementation class? if (sti === 0) { if (!superType.isInterface()) { result = ` extends ${superType.getName()}Impl`; } } sti++; } return result; } genFeatureDeclarations(eclass) { let sourceContent = ``; for (const f of eclass.getEStructuralFeatures()) { const typeName = DU.getTypeName(f); let initializer = ''; if (f.isMany()) { //get feature ID for inverse reference, if specified let inverseFeatureId = 'undefined'; if (f instanceof EReferenceImpl) { if (f.getEOpposite()) { const oppRef = f.getEOpposite(); if (oppRef) { inverseFeatureId = `${DU.genPackageClassName(oppRef.getEContainingClass().getEPackage())}.${DU.genFeatureIdFieldName(oppRef)}`; } } } //list is initialized with information about owning object, the //field it represents, and any inverse field initializer += ` = new Basic${typeName}( undefined, this, ${this._packageName}.${DU.genFeatureIdFieldName(f)}, ${inverseFeatureId} )`; } //TODO: only add '!' is feature is marked as required (lowerBound = 1) let optionalityOperator = f.isMany() ? '' : '!'; sourceContent += ` protected ${f.getName()}${optionalityOperator}: ${typeName}${initializer};\n`; } return sourceContent; } genConstructor(eClass) { // Build sorted list of all constructor arguments let argFields = []; // Build sorted list constructor arguments from possible superType const superFields = []; let result = ''; if (argFields.length > 0) { // Now build up the constructor code... result += ` //====================================================================== // Constructor public constructor(`; for (const field of argFields) { if (field.isMany()) { const type = TUtils.getTypescriptName(field.getEType()); result += `_${field.getName()}?: EList<${type}>, `; } else { const type = TUtils.getTypescriptName(field.getEType()); result += `_${field.getName()}?: ${type}, `; } } result += ') {'; // Deal with invoking 'super(....)' if necessary if (superFields) { result += 'super('; for (const field of superFields) { result += `_${field.getName()}, `; } result += ');'; argFields = argFields.filter((f) => !superFields.includes(f)); } else { result += 'super();'; } // Deal with setting the remaining (locally defined) fields for (const field of argFields) { if (field.isMany()) { const eListVar = field.getName() + 'EList'; result += `if (_${field.getName()}) { const ${eListVar} = this.${DU.getterName(field)}(); for(const val of _${field.getName()}) { ${eListVar}.add(val); } }`; } else { result += `this.${DU.setterName(field)}(_${field.getName()});`; } } result += '}'; } return result; } genGettersAndSetters(eClass) { let result = ` //====================================================================== // Getters and Setters `; for (const field of eClass.getEStructuralFeatures()) { result += this.genGetter(eClass, field); result += this.genSetter(eClass, field); } return result; } genGetter(eClass, field) { const visibility = 'public'; let result = ` ${visibility} ${DU.getterSig(field)} {`; if (field.isVolatile()) { result += ` throw new Error( 'Unsupported operation on volatile field. Override in ${eClass.getName()}Impl.' );`; } else { result += ` return this.${field.getName()};`; } result += ` }`; // End of Getter definition return result; } genSetter(eClass, field) { let result = ``; if (!field.isMany()) { const paramName = DU.setterParamName(field); const visibility = field.isChangeable() ? 'public' : 'private'; result += ` ${visibility} ${DU.setterSig(field)} {`; if (field.isVolatile()) { result += ` throw new Error( 'Unsupported operation on volatile field. Override in ${eClass.getName()}Impl.' );`; } else { //handle containment enforcement if appropriate if (field instanceof EReferenceImpl && field.isContainment()) { const oldVar = 'old' + DU.capitalize(field.getName()); result += ` const ${oldVar} = this.${field.getName()}; if (${oldVar}) ${oldVar}.setEContainer(undefined, undefined); if (${paramName}) ${paramName}.setEContainer(this, ${DU.genPackageClassName(this._pkg)}.${DU.genFeatureIdFieldName(field)});`; } //handle inverse references if appropriate if (field instanceof EReferenceImpl && field.getEOpposite()) { //TODO: handle opposites in other packages? const oppositeIdField = `${this._packageName}.${DU.genFeatureIdFieldName(field.getEOpposite())}`; result += ` if (this.${field.getName()} !== ${paramName}) { if (this.${field.getName()}) { this.${field.getName()}.eInverseRemove(this, ${oppositeIdField}); } if (${paramName}) { ${paramName}.eInverseAdd(this, ${oppositeIdField}); } }`; } result += ` this.basic${DU.capitalize(DU.setterName(field))}(${paramName});`; // sourceContent += `this.${f.getName()} = ${paramName};}`; } result += ` }`; // End of Setter definition } return result; } genBasicSetters(eClass) { let result = ` //====================================================================== // Basic setters (allow EOpposite enforcement without triggering infinite cycles) `; for (const field of eClass.getEStructuralFeatures()) { //only include basic setters for internal references if (!field.isMany() && !field.isVolatile()) { const visibility = field.isChangeable() ? 'public' : 'private'; const paramName = DU.setterParamName(field); result += ` ${visibility} basic${DU.capitalize(DU.setterSig(field))} {`; if (field instanceof EReferenceImpl && field.getEOpposite() && field.getEOpposite().isContainment()) { result += ` this.eBasicSetContainer(${paramName}, ${DU.genPackageClassName(eClass.getEPackage())}.${DU.genFeatureIdFieldName(field.getEOpposite())});`; } result += ` this.${field.getName()} = ${paramName}; }`; } } return result; } genEoperations(eClass) { let result = ` //====================================================================== // API Operations `; for (const eop of eClass.getEOperations()) { result += ` public ${DU.eopSignature(eop)} { throw new Error('Not implemented'); }`; } //if this class directly inherits from an interface, need to add implementations of those as well for (const st of eClass.getESuperTypes()) { if (st.isInterface()) { for (const eop of st.getEOperations()) { result += ` public ${DU.eopSignature(eop)} { throw new Error('Not implemented'); }`; } } } return result; } genEGet(eClass) { let result = ` /** * eGet() - provides reflective access to all features. */ public override eGet(feature: number | EStructuralFeature): any { const featureID: number = typeof feature === 'number' ? feature : (<EStructuralFeature>feature).getFeatureID(); switch (featureID) {`; for (const feature of eClass.getEStructuralFeatures()) { const featureIdField = DU.genFeatureIdFieldName(feature); result += ` case ${this._packageName}.${featureIdField}: return this.${DU.getterName(feature)}();`; } result += ` } return super.eGet(featureID); }`; return result; } genESet(eClass) { let result = ` /** * eSet() - provides ability to reflectively set all features. */ public override eSet(feature: number | EStructuralFeature, newValue: any): void { const featureID: number = typeof feature === 'number' ? feature : (<EStructuralFeature>feature).getFeatureID(); switch (featureID) {`; for (const feature of eClass.getEStructuralFeatures()) { const featureIdField = DU.genFeatureIdFieldName(feature); result += ` case ${this._packageName}.${featureIdField}:`; if (!feature.isMany()) { result += ` this.${DU.setterName(feature)}(newValue); return;`; } else { const getter = DU.getterName(feature); result += ` this.${getter}().clear(); this.${getter}().addAll(newValue); return;`; } } result += ` } return super.eSet(featureID, newValue); }`; return result; } genEIsSet(eClass) { let result = ` /** * eIsSet() - provides ability to reflectively check if any feature is set. */ public override eIsSet(feature: number | EStructuralFeature): boolean { const featureID: number = typeof feature === 'number' ? feature : (<EStructuralFeature>feature).getFeatureID(); switch (featureID) {`; for (const feature of eClass.getEStructuralFeatures()) { const featureIdField = DU.genFeatureIdFieldName(feature); result += ` case ${this._packageName}.${featureIdField}:`; if (!feature.isMany()) { result += ` return this.${DU.getterName(feature)}() != null;`; } else { const getter = DU.getterName(feature); result += ` return !this.${getter}().isEmpty();`; } } result += ` } return super.eIsSet(featureID); }`; return result; } genEUnset(eClass) { let result = ` /** * eUnset() - provides ability to reflectively unset any feature. */ public override eUnset(feature: number | EStructuralFeature): void { const featureID: number = typeof feature === 'number' ? feature : (<EStructuralFeature>feature).getFeatureID(); switch (featureID) {`; for (const feature of eClass.getEStructuralFeatures()) { const featureIdField = DU.genFeatureIdFieldName(feature); result += ` case ${this._packageName}.${featureIdField}:`; if (!feature.isMany()) { result += ` this.${DU.setterName(feature)}(undefined!); return;`; } else { const getter = DU.getterName(feature); result += ` this.${getter}().clear(); return;`; } } result += ` } return super.eUnset(featureID); }`; return result; } genEInverseAdd(eClass) { let result = ` //====================================================================== // Inverse Adders (if needed)`; let numSwitches = 0; for (const f of eClass.getEStructuralFeatures()) { if (f instanceof EReferenceImpl) { if (!f.isVolatile() && f.getEOpposite()) { if (numSwitches === 0) { result += ` public override eInverseAdd(otherEnd: EObject, featureID: number): void { switch (featureID) {`; } //TODO: Do I also need to do a basic remove from container if this sets a new container? result += ` case ${this._packageName}.${DU.genFeatureIdFieldName(f)}:`; if (f.isMany()) { result += ` return (<EList<EObject>>this.${DU.getterName(f)}()).basicAdd(otherEnd);`; } else { if (f.getEOpposite()) { const oppositeFeatureField = `${this._packageName}.${DU.genFeatureIdFieldName(f.getEOpposite())}`; result += ` if (this.${f.getName()}) this.${f.getName()}.eInverseRemove( this, ${oppositeFeatureField} );`; } result += ` return this.basic${DU.capitalize(DU.setterName(f))}(<${f .getEType() .getName()}>otherEnd);`; } numSwitches++; } } } if (numSwitches > 0) { result += ` } return super.eInverseAdd(otherEnd, featureID); }`; } return result; } genEInverseRemove(eClass) { let result = ` //====================================================================== // Inverse Removers (if needed)`; let numSwitches = 0; for (const f of eClass.getEStructuralFeatures()) { if (f instanceof EReferenceImpl) { if (!f.isVolatile() && f.getEOpposite()) { if (numSwitches === 0) { result += ` public override eInverseRemove(otherEnd: EObject, featureID: number): void { switch (featureID) {`; } result += ` case ${this._packageName}.${DU.genFeatureIdFieldName(f)}:`; if (f.isMany()) { result += ` return (<EList<EObject>>this.${DU.getterName(f)}()).basicRemove(otherEnd);`; } else { result += ` return this.basic${DU.capitalize(DU.setterName(f))}(undefined!);`; } numSwitches++; } } } if (numSwitches > 0) { result += ` } return super.eInverseRemove(otherEnd, featureID); }`; } return result; } } //# sourceMappingURL=tgenerator-gen.js.map