@tripsnek/tmf
Version:
TypeScript Modeling Framework - A TypeScript port of the Eclipse Modeling Framework (EMF)
277 lines • 13.8 kB
JavaScript
;
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