UNPKG

@tripsnek/tmf

Version:

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

277 lines 13.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EcoreStringParser = void 0; const edata_type_impl_1 = require("../metamodel/edata-type-impl"); const eenum_literal_impl_1 = require("../metamodel/eenum-literal-impl"); const eclass_impl_1 = require("../metamodel/eclass-impl"); const eenum_impl_1 = require("../metamodel/eenum-impl"); const eattribute_impl_1 = require("../metamodel/eattribute-impl"); const ereference_impl_1 = require("../metamodel/ereference-impl"); const eoperation_impl_1 = require("../metamodel/eoperation-impl"); const eparameter_impl_1 = require("../metamodel/eparameter-impl"); const tutils_1 = require("../tutils"); const epackageimpl_1 = require("../metamodel/epackageimpl"); /** * Parses Ecore XML strings into TMF metamodel instances. * This class handles the core parsing logic without file system dependencies. */ class EcoreStringParser { parseFromJsString(ecoreJsonStr) { return this.parseFromJs(JSON.parse(ecoreJsonStr)); } parseFromJs(ecoreJs) { const ePackage = ecoreJs['ecore:EPackage']; //holds all types and features in a map for reference resolution (e.g. assigning //types to references and attributes, enforcing EOpposites) const typesMap = new Map(); const featuresMap = new Map(); //add primitive types const primitiveTypes = tutils_1.TUtils.PRIMITIVES; for (const type of primitiveTypes) { typesMap.set('ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//' + type, new edata_type_impl_1.EDataTypeImpl(undefined, type)); } // for (const key of typesMap.keys()) { // console.error('KEY ' + key + ' : ' + typeof typesMap[key]); // } //parse all classifiers first, so that we can resolve them later this.instantiateAllClassifiersAndFeatures(ePackage, typesMap, featuresMap, []); //parse the root EPackage and all contents const pkgPath = []; const rootPackage = this.parsePackage(ePackage, undefined, typesMap, featuresMap, pkgPath); return rootPackage; } /** * Instantiates all the EClassifiers, which need to exist in-memory for a full * pass so that features can be assigned types. * * @param pkgJson * @param typesMap * @param path */ instantiateAllClassifiersAndFeatures(pkgJson, typesMap, featuresMap, path) { //keeps track of the path to the current package path.push(pkgJson['$'].name); if (pkgJson['eClassifiers']) { for (const classEntry of pkgJson['eClassifiers']) { let eclassifier = new eclass_impl_1.EClassImpl(); const eType = classEntry['$']['xsi:type']; if (eType === 'ecore:EEnum') { eclassifier = new eenum_impl_1.EEnumImpl(); for (const literalEntry of classEntry['eLiterals']) { const literal = new eenum_literal_impl_1.EEnumLiteralImpl(); if (literalEntry['$']['name']) literal.setName(literalEntry['$']['name']); if (literalEntry['$']['value']) literal.setValue(Number.parseInt(literalEntry['$']['value'])); if (literalEntry['$']['literal']) literal.setLiteral(literalEntry['$']['literal']); //TODO: Would not have to do both if model was source generated eclassifier.addLiteral(literal); literal.setEEnum(eclassifier); } } else if (eType === 'ecore:EDataType') { eclassifier = new edata_type_impl_1.EDataTypeImpl(); } eclassifier.setName(classEntry['$']['name']); if (classEntry['$']['abstract']) { eclassifier.setAbstract(classEntry['$']['abstract']); } if (classEntry['$']['interface']) { eclassifier.setInterface(classEntry['$']['interface']); } const eclassUri = this.getEClassifierUri(path, eclassifier.getName()); typesMap.set(eclassUri, eclassifier); if (classEntry['eStructuralFeatures']) { for (const featureEntry of classEntry['eStructuralFeatures']) { //instantiate the feature const fprops = featureEntry['$']; let efeature; if (fprops['xsi:type'] === 'ecore:EAttribute') { efeature = new eattribute_impl_1.EAttributeImpl(); if (fprops.iD) efeature.setId(true); } else if (fprops['xsi:type'] === 'ecore:EReference') { efeature = new ereference_impl_1.EReferenceImpl(); } else { console.error('Unknown type: ' + fprops['xsi:eType']); console.log(fprops); } //create map entry from uri to the feature if (efeature) featuresMap.set(eclassUri + '/' + fprops['name'], efeature); } } } } //recurse subdirectories if (pkgJson['eSubpackages']) { for (const packageEntry of pkgJson['eSubpackages']) { this.instantiateAllClassifiersAndFeatures(packageEntry, typesMap, featuresMap, path); } } path.pop(); } //builds up the URI by which EClassifiers are referenced (e.g. to type features) getEClassifierUri(path, className) { let uri = '#//'; for (let i = 1; i < path.length; i++) { uri += path[i] + '/'; } uri += className; return uri; } /** * Parses the package represented by pkgJson. * * @param pkgJson * @param parentPkg * @param typesMap * @param path */ parsePackage(pkgJson, parentPkg, typesMap, featuresMap, path) { const thisPkg = new epackageimpl_1.EPackageImpl(pkgJson['$'].name, pkgJson['$'].nsURI); thisPkg.setNsPrefix(pkgJson['$'].nsPrefix); //keeps track of the current path to the package path.push(pkgJson['$'].name); if (parentPkg) { //TODO: Should not have to do both of these, should be inverses parentPkg.getESubPackages().add(thisPkg); thisPkg.setESuperPackage(parentPkg); } if (pkgJson['eClassifiers']) { for (const classEntry of pkgJson['eClassifiers']) { const eclassUri = this.getEClassifierUri(path, classEntry['$']['name']); const eclass = typesMap.get(eclassUri); if (!eclass) { console.error('COULD NOT FIND ECLASS IDENTIFIED BY ' + eclassUri); } //TODO: Should not have to do both of these, should be inverses eclass.setEPackage(thisPkg); thisPkg.getEClassifiers().add(eclass); //parse out super types if (classEntry['$']['eSuperTypes']) { for (const superType of classEntry['$']['eSuperTypes'].split(' ')) { const superTypeEClass = typesMap.get(superType); if (!superTypeEClass) { console.error('COULD NOT FIND ECLASS IDENTIFIED BY ' + superType); } eclass.getESuperTypes().add(superTypeEClass); } } if (eclass instanceof eclass_impl_1.EClassImpl) { //parse out features this.parseFeatures(classEntry, typesMap, featuresMap, eclass, eclassUri); //parse EOperations if ('eOperations' in classEntry) { const nameToEop = new Map(); for (const operationEntry of classEntry['eOperations']) { const fprops = operationEntry['$']; const prevExistingEop = nameToEop.get(fprops.name); const eOperation = prevExistingEop ? prevExistingEop : new eoperation_impl_1.EOperationImpl(); if (!prevExistingEop) { eOperation.setName(fprops.name); nameToEop.set(eOperation.getName(), eOperation); eclass.getEOperations().add(eOperation); eOperation.setEContainingClass(eclass); if (fprops.eType) eOperation.setEType(typesMap.get(fprops.eType)); if (fprops.upperBound) { eOperation.setUpperBound(Number(fprops.upperBound)); } } if ('eParameters' in operationEntry) { for (const paramEntry of operationEntry['eParameters']) { const name = paramEntry.$.name; let paramExists = false; for (const p of eOperation.getEParameters()) if (name == p.getName()) paramExists = true; if (!paramExists) { const param = new eparameter_impl_1.EParameterImpl(); param.setName(paramEntry.$.name); if (paramEntry.$.eType) { param.setEType(typesMap.get(paramEntry.$.eType)); } if (paramEntry.$.upperBound) { param.setUpperBound(Number(paramEntry.$.upperBound)); } //TODO: Would not have to do both if model was source generated eOperation.getEParameters().add(param); param.setEOperation(eOperation); } } } } } } } } //recurse sub-packages if (pkgJson['eSubpackages']) { for (const packageEntry of pkgJson['eSubpackages']) { this.parsePackage(packageEntry, thisPkg, typesMap, featuresMap, path); } } path.pop(); return thisPkg; } /** * Parses eStructuralFeatures elements from Ecore XMI element. * * @param classEntry * @param typesMap * @param featuresMap * @param eclass * @param eclassUri */ parseFeatures(classEntry, typesMap, featuresMap, eclass, eclassUri) { if ('eStructuralFeatures' in classEntry) { for (const featureEntry of classEntry['eStructuralFeatures']) { const fprops = featureEntry['$']; const efeature = featuresMap.get(eclassUri + '/' + fprops['name']); if (efeature) { efeature.setName(fprops['name']); const type = typesMap.get(fprops['eType']); if (!type) { console.error('WARNING: COULD NOT LOCATE TYPE FOR ' + fprops.name + ' with etype ' + fprops.eType); continue; } efeature.setEType(type); if (fprops['upperBound']) { efeature.setUpperBound(Number(fprops['upperBound'])); } else { efeature.setUpperBound(1); } //transient,changeable,volatile efeature.setVolatile(fprops.volatile == null ? false : JSON.parse(fprops.volatile)); efeature.setChangeable(fprops.changeable == null ? true : JSON.parse(fprops.changeable)); efeature.setTransient(fprops.transient == null ? false : JSON.parse(fprops.transient)); //eopposites and containment if (efeature instanceof ereference_impl_1.EReferenceImpl) { if (fprops['eOpposite']) { const eopp = featuresMap.get(fprops['eOpposite']); efeature.setEOpposite(eopp); } efeature.setContainment(fprops.containment == null ? false : JSON.parse(fprops.containment)); } //TODO: This should be handled automatically be einverse enforcement (if tmf were source-generated) eclass.getEStructuralFeatures().add(efeature); efeature.setEContainingClass(eclass); } } } } } exports.EcoreStringParser = EcoreStringParser; //# sourceMappingURL=ecore-string-parser.js.map