UNPKG

@tripsnek/tmf

Version:

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

454 lines (439 loc) 19.9 kB
import { TGenUtils as DU } from './tgen-utils'; import { EAttributeImpl } from '../metamodel/eattribute-impl'; import { EClassImpl } from '../metamodel/eclass-impl'; import { EReferenceImpl } from '../metamodel/ereference-impl'; import { EEnumImpl } from '../metamodel/eenum-impl'; import { EDataTypeImpl } from '../metamodel/edata-type-impl'; import { TGeneratorPackageInitializer } from './tgenerator-package-initializer'; /** * Responsible for generating the interface and implementation of an * EPackage source file, which provides static access to all the elements * of a package in a TMF model. * * Note that generation of several swathes of the package content * are ganged together into a single pass over the model. This owes * to the fact that sufficient logic is shared across their computations, largely * related to the tracking of IDs for individual elements, that it would * be difficult to separate them out. */ export class TGeneratorPackage { rootPackage; generatePackageContents(pkg) { const className = DU.genPackageClassName(pkg); this.rootPackage = pkg; while (this.rootPackage.getESuperPackage()) this.rootPackage = this.rootPackage.getESuperPackage(); //track package dependencies, import them at the end const pkgToImport = new Set(); //static library of all package contents let literalsContent = ` /** Provides static access to EClass and EStructuralFeature instances */ public static Literals = class {`; let fieldDeclarationsContent = ''; let gettersContent = ''; //insert create/initialize methods let createContent = ` public createPackageContents(): void { if (this.isCreated) return; this.isCreated = true;`; let initContent = ` public initializePackageContents(): void { if (this.isInitialized) return; this.isInitialized = true; //reusable handle for eoperations, used for adding parameters let op: EOperation;`; let idFieldsContent = ''; //iterate over all classes and features, create and initialize them AND create getters using the feature IDs let ecIdCounter = 0; //sort eclassifiers in such a way that base types are guaranteed to precede derived types so //that we can reference feature IDs in correct order const sortedClassifiers = this.sortClassifiersWithBaseTypesFirst(pkg); //NOTE: This is the 'single-pass' loop for generating IDs, declaring element fields //and initializing each and hooking up their references where appropriate. The //content for all of these is then integrated into the full template of the package //file in the return statement for this method for (const eclassifier of sortedClassifiers) { const eclassIdField = DU.genClassIdFieldName(eclassifier); idFieldsContent += ` public static ${eclassIdField} = ${ecIdCounter};`; ecIdCounter++; //add eclassifier field let type = 'EClass'; if (eclassifier instanceof EEnumImpl) type = 'EEnum'; else if (eclassifier instanceof EDataTypeImpl) type = 'EDataType'; const eclassFieldName = DU.uncapitalize(eclassifier.getName()) + type; fieldDeclarationsContent += ` private ${eclassFieldName}!: ${type};`; //add eclass getter const eclassGetter = DU.genEclassGetterName(eclassifier); gettersContent += ` public ${eclassGetter}(): ${type} { return this.${eclassFieldName}; }`; //add creation createContent += ` this.${eclassFieldName} = this.create${type}(${className}.${eclassIdField});`; //add Literals entry for EClassifier literalsContent += ` static ${eclassIdField}: ${type} = ${className}._eINSTANCE.${DU.genEclassGetterName(eclassifier)}();`; //add class initialization if (eclassifier instanceof EClassImpl) { const eclass = eclassifier; for (const superType of eclassifier.getESuperTypes()) { const pkgRef = DU.getReferenceToPackageInstance(superType, eclassifier, pkgToImport); initContent += ` this.${eclassFieldName}.getESuperTypes().add(${pkgRef}.${DU.genEclassGetterName(superType)}());`; } initContent += ` this.init${type}( this.${eclassFieldName}, '${eclassifier.getName()}', ${eclassifier.isAbstract()}, ${eclassifier.isInterface()}, true );`; } else { initContent += ` this.init${type}( this.${eclassFieldName}, '${eclassifier.getName()}');`; if (eclassifier instanceof EEnumImpl) { let literalInd = 0; for (const literal of eclassifier.getELiterals()) { const litRef = eclassifier.getName() + '.' + literal.getName(); initContent += ` this.addEEnumLiteral(this.${eclassFieldName}, '${literal.getName()}', ${literalInd});`; literalInd++; } } } //process references and attributes if (eclassifier instanceof EClassImpl) { //find super type const superType = eclassifier.getESuperTypes().size() > 0 ? eclassifier.getESuperTypes().get(0) : null; //write out feature count field (will be the starting point for ID'ing new fields on this eclass) const featureCountField = DU.genFeatureCountFieldName(eclassifier); const superTypeFeatureCount = superType ? superType.getEAllStructuralFeatures().size() : 0; //Eliminating references to super types in static initializers, since they could be in other packages (circular import) // const superTypeCountField = superType // ? DU.genPackageClassName(superType.getEPackage()) + // '.' + // DU.genFeatureCountFieldName(superType) + // ' + ' // : ''; // if (superTypeCountField) { // idFieldsContent += ` // public static ${featureCountField} = // ${superTypeCountField}${eclassifier.getEStructuralFeatures().size()};`; // } else { // idFieldsContent += ` // public static ${featureCountField} = ${eclassifier.getEStructuralFeatures().size()};`; // } //this is the replacement - just a straight count of all features, shoudl be same idFieldsContent += ` public static ${featureCountField} = ${eclassifier.getEAllStructuralFeatures().size()};`; //keeps track of the feature's index in it's eClass (used for feature getter) let thisClassFeatureIndex = 0; for (const feature of eclassifier.getEStructuralFeatures()) { const featureIdField = DU.genFeatureIdFieldName(feature); //Eliminating references to super types in static initializers, since they could be in other packages (circular import) // if (superTypeCountField) { // idFieldsContent += ` // public static ${featureIdField} = ${superTypeCountField}${thisClassFeatureIndex};`; // } else { // idFieldsContent += ` // public static ${featureIdField} = ${thisClassFeatureIndex};`; // } //this is the replacement - just add to the super type feature count idFieldsContent += ` public static ${featureIdField} = ${superTypeFeatureCount + thisClassFeatureIndex};`; const featureType = feature instanceof EAttributeImpl ? 'EAttribute' : 'EReference'; literalsContent += ` static ${featureIdField}: ${featureType} = ${className}._eINSTANCE.get${DU.capitalize(eclassifier.getName())}_${DU.capitalize(feature.getName())}();`; //create features createContent += ` this.create${featureType}( this.${eclassFieldName}, ${className}.${featureIdField} );`; const featureGetterName = DU.genFeatureGetterName(feature); //add feature getter gettersContent += ` public ${featureGetterName}(): ${featureType} { return <${featureType}>this.${eclassFieldName}.getEStructuralFeatures().get(${thisClassFeatureIndex}); }`; //the getter for the feature's type (Eclassifier instance) const featureTypeGetter = this.getterForEType(feature.getEType(), eclassifier, pkgToImport, pkg); //initializers initContent += ` this.init${featureType}( this.${featureGetterName}()`; if (feature instanceof EAttributeImpl) { initContent += `, ${featureTypeGetter}, '${feature.getName()}', ${feature.getDefaultValueLiteral() ? feature.getDefaultValueLiteral() : `''`}, ${feature.getLowerBound()}, ${feature.getUpperBound()}, '', ${feature.isTransient() ?? false}, ${feature.isVolatile() ?? false}, ${feature.isChangeable() ?? true}, true, //TODO: isUnsettable, ${feature.isId() ?? false}, ${feature.isUnique() ?? false}, false, //TODO: isDerived false //TODO: isOrdered;`; } else if (feature instanceof EReferenceImpl) { const pkgRef = DU.getReferenceToPackageInstance(feature.getEType(), eclassifier, pkgToImport); let eopposite = 'undefined'; if (feature.getEOpposite()) { eopposite = `${pkgRef}.${DU.genFeatureGetterName(feature.getEOpposite())}()`; } initContent += `, ${featureTypeGetter}, ${eopposite}, '${feature.getName()}', '', ${feature.getLowerBound()}, ${feature.getUpperBound()}, '', //TODO: Container Class ${feature.isTransient() ?? false}, ${feature.isVolatile() ?? false}, ${feature.isChangeable() ?? false}, ${feature.isContainment() ?? false}, false, true, //TODO: isUnsettable ${feature.isUnique() ?? false}, false, //TODO: isDerived false //TODO: isOrdered`; } initContent += ` );`; thisClassFeatureIndex++; } //logic for creating and initializing EOperations let eopIndex = 0; for (const eop of eclassifier.getEOperations()) { const eopIdField = DU.genOperationIdFieldName(eop); //add ID content idFieldsContent += ` public static ${eopIdField} = ${eopIndex};`; createContent += ` this.createEOperation(this.${eclassFieldName}, ${className}.${eopIdField});`; const featureGetterName = DU.genOperationGetterName(eop); gettersContent += ` public ${featureGetterName}(): EOperation { return this.${eclassFieldName}.getEOperations().get(${eopIndex}); }`; initContent += ` op = this.initEOperation( this.${featureGetterName}(), ${this.getterForEType(eop.getEType(), eclassifier, pkgToImport, pkg)}, '${eop.getName()}', 0, ${eop.isMany() ? -1 : 1}, true, true );`; for (const p of eop.getEParameters()) { //TODO: Create parameters and add them to EOphere initContent += ` this.createEParameter( op, '${p.getName()}', ${p.isMany() ? -1 : 1}, ${this.getterForEType(p.getEType(), eclassifier, pkgToImport, pkg)} );`; } eopIndex++; } } } createContent += ` }`; initContent += ` }`; literalsContent += ` };`; //EcorePackage cannot extend EPackageImpl, as it it would create circular reference const toExtend = pkg.getName().toLowerCase() === 'ecore' ? 'EPackage' : 'EPackageImpl'; let subPkgSetters = ''; // for(const subPkg of pkg.getESubPackages()){ // if(!pkgToImport.has(subPkg)){ // pkgToImport.add(subPkg); // } // subPkgSetters += ` // ${DU.genPackageClassName(subPkg)}.eINSTANCE.setESuperPackage(the${className})`; // } return `${this.generateImports(pkgToImport, pkg)} export class ${className} extends ${toExtend} {${idFieldsContent} /** Singleton */ public static _eINSTANCE: ${className} = ${className}.init(); //if the singleton is initialized private static isInited = false; static eNS_URI = '${pkg.getNsURI()}'; static eNAME = '${pkg.getName()}'; static eNS_PREFIX = '${pkg.getNsPrefix()}'; ${literalsContent} //flags that keep track of whether package is initialized private isCreated = false; private isInitialized = false; ${fieldDeclarationsContent} //causes EPackage.Registry registration event //hard-coded URI, since referring to the static eNS_URI field in constructor can cause issues constructor() { super( '${pkg.getName()}', '${pkg.getNsURI()}', '${pkg.getNsPrefix()}' ); } /** * Invoked once. Initializes the Singleton. * * NOTE: Lots of differences here with the EMF version, which interacts with the package Registry, * other packages from the same model to register interdependencies, and freezes the package meta-data. */ private static init(): ${className} { if (${className}.isInited) return this._eINSTANCE; // Obtain or create and register package const the${className} = new ${className}(); //this is necessary specifically for EcorePackage generation, which needs to refer to itself this._eINSTANCE = the${className}; ${className}.isInited = true; ${subPkgSetters} // Create package meta-data objects the${className}.createPackageContents(); // Initialize created meta-data // the${className}.initializePackageContents(); return this._eINSTANCE; } static get eINSTANCE(): ${className}{ ${TGeneratorPackageInitializer.generateClassName(this.rootPackage)}.registerAll(); return this._eINSTANCE; } //this used to be direct lazy retrieval of the //factory instance from the corresponding .ts factory file, but //that was eliminated to avoid circular imports public override getEFactoryInstance(): EFactory { return this._eFactoryInstance; } /** * This will be invoked by the Factory when it is initialized, any invocations * afterwards will have no effect. */ public override setEFactoryInstance(factoryInst: EFactory): void { if (!this._eFactoryInstance) this._eFactoryInstance = factoryInst; } ${gettersContent} ${createContent} ${initContent} } `; } /** * User for generating 'get' on the EType for a feature or operation. * * @param etype * @param owningEClass * @param pkgToImport * @param pkg * @returns */ getterForEType(etype, owningEClass, pkgToImport, pkg) { let featureTypeGetter = undefined; if (etype) { const pkgRef = DU.getReferenceToPackageInstance(etype, owningEClass, pkgToImport); if (etype instanceof EEnumImpl) { featureTypeGetter = `${pkgRef}.get${etype.getName()}()`; } else if (etype instanceof EClassImpl) { const pkgRef = DU.getReferenceToPackageInstance(etype, owningEClass, pkgToImport); if (etype) { featureTypeGetter = `${pkgRef}.${DU.genEclassGetterName(etype)}()`; } } else { let ecorePkgGetter = 'this.getEcorePackage()'; if (pkg.getName().toLowerCase() === 'ecore') ecorePkgGetter = 'this'; featureTypeGetter = ecorePkgGetter + '.get' + etype.getName() + '()'; } } return featureTypeGetter; } generateImports(pkgToImport, pkg) { //build path to root package initializer const pathToRoot = DU.getPathToRoot(pkg); let imports = `${DU.generateImportStatementsForExternalPackages(pkgToImport, pkg, './')} ${DU.DEFAULT_IMPORTS} import { ${TGeneratorPackageInitializer.generateClassName(this.rootPackage)}} from '${pathToRoot}${TGeneratorPackageInitializer.generateFileName(this.rootPackage)}'; import { EPackage } from '@tripsnek/tmf'; import { EPackageImpl } from '@tripsnek/tmf'; import { EAttribute } from '@tripsnek/tmf'; import { EFactory } from '@tripsnek/tmf'; import { EReference } from '@tripsnek/tmf'; import { EOperation } from '@tripsnek/tmf';`; //we use relative imports for the EcorePackage, since it is in the same lib as the metamodel if (pkg.getName().toLowerCase() === 'ecore') { imports = `${DU.generateImportStatementsForExternalPackages(pkgToImport, pkg, '')} import { EPackage } from './epackage'; import { EDataType } from './edata-type'; import { EClass } from './eclass'; import { EReference } from './ereference'; import { EAttribute } from './eattribute'; import { EFactory } from './efactory';`; } else { imports += ` import { EcorePackage } from '@tripsnek/tmf';`; } //imports of Enum classes for (const ec of pkg.getEClassifiers()) { if (ec instanceof EEnumImpl) { imports += ` import { ${ec.getName()} } from './api/${DU.genClassApiName(ec)}';`; } } return imports; } /** * Sort eclassifiers in such a way that base types are guaranteed to precede derived types so * that we can reference feature IDs in correct order */ sortClassifiersWithBaseTypesFirst(pkg) { const sortedClassifiers = new Array(); for (const eclassifier of pkg.getEClassifiers()) { if (eclassifier instanceof EClassImpl) { const eclass = eclassifier; if (sortedClassifiers.indexOf(eclass) === -1) { //insert eclass after it's last super type let idxLastSuperType = -1; for (const st of eclass.getEAllSuperTypes()) { if (sortedClassifiers.indexOf(st) > -1) { const index = sortedClassifiers.indexOf(st); if (index > idxLastSuperType) idxLastSuperType = index; } } if (idxLastSuperType >= 0) sortedClassifiers.splice(idxLastSuperType + 1, 0, eclass); else sortedClassifiers.splice(0, 0, eclass); } } //enums and other EClassifiers can go wherever else { sortedClassifiers.push(eclassifier); } } return sortedClassifiers; } } //# sourceMappingURL=tgenerator-package.js.map