UNPKG

@tripsnek/tmf

Version:

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

429 lines 17.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TUtils = void 0; const uuid_1 = require("uuid"); const ecorepackage_1 = require("./metamodel/ecorepackage"); const tjson_1 = require("./json/tjson"); const eenum_impl_1 = require("./metamodel/eenum-impl"); const edata_type_impl_1 = require("./metamodel/edata-type-impl"); const eclass_impl_1 = require("./metamodel/eclass-impl"); /** * Various utilities for interacting with TMF-modeled data objects, some * of which are relied upon by core TMF components (e.g. TJson) and generated * source code. */ class TUtils { //All primitive EDataTypes supported by TMF static PRIMITIVES = [ 'EString', 'EBoolean', 'EDouble', 'EDoubleObject', 'EFloat', 'EFloatObject', 'EInt', 'EIntegerObject', 'EDate', ]; /** * Ensures all objects have IDs. * @param obj */ static generateIdsForAllContained(obj) { for (const e of obj.eAllContents()) this.genIdIfNotExists(e); } /** * Sets an ID for the given object on it's ID EAttribute. If * an ID is already specified, does nothing. * * @param obj */ static genIdIfNotExists(obj) { //TODO: Support IDs other than string uuids if (obj && obj.eClass().getEIDAttribute() && !obj.fullId()) { obj.eSet(obj.eClass().getEIDAttribute(), (0, uuid_1.v4)()); } } /** * If an ID is specified on the object, returns a new TId instance with that ID value. If not, a * new ID is assigned and that TId returned. * * @param obj */ static getOrCreateIdForObject(obj) { this.genIdIfNotExists(obj); return obj.fullId(); } /** * Gets the Typescript name for a given EClassifier, including * translation of primitive EDataType names (e.g. EBoolean -> boolean). * @param type */ static getTypescriptName(type) { if (!type) return 'null'; let typeStr = type.getName(); if (!(type instanceof eenum_impl_1.EEnumImpl) && type instanceof edata_type_impl_1.EDataTypeImpl) typeStr = TUtils.toTypeScriptPrimitive(type); return typeStr; } /** * Gets the TypeScript primitive type for a given TMF EClassifier that represents a * primitive type. * @param classifier */ static toTypeScriptPrimitive(classifier) { if (!classifier || !classifier.getName()) return 'any'; switch (classifier.getName().toLowerCase()) { case 'estring': { return 'string'; break; } case 'eboolean': { return 'boolean'; break; } case 'edouble': { return 'number'; break; } case 'edoubleobject': { return 'number'; break; } case 'efloat': { return 'number'; break; } case 'efloatobject': { return 'number'; break; } case 'eint': { return 'number'; break; } case 'eintegerobject': { return 'number'; break; } case 'edate': { return 'Date'; break; } case 'earray': { return '[]'; } default: { return 'any'; } } } static lookupNamedField(object, fieldName) { const curClass = object.eClass(); const field = curClass.getEStructuralFeature(fieldName); if (!field) { console.log(curClass.getName(), ' has no field named', fieldName); return undefined; } return field; } static getNamedFieldValue(object, fieldName) { const field = this.lookupNamedField(object, fieldName); if (field) return object.eGet(field); return undefined; } static setNamedFieldValue(object, fieldName, newValue) { const field = this.lookupNamedField(object, fieldName); if (field) object.eSet(field, newValue); return undefined; } /** * Parses a value for an EAttribute from it's JSON representation. * * @param attr * @param jsonVal */ static parseAttrValFromString(attr, stringVal) { return this.parseValueFromString(attr.getEType(), stringVal + ''); } static parseValueFromString(attrType, stringVal) { let tVal = null; //if an empty string was set for the field, just return null if (!stringVal || (stringVal.toString().trim() === '' && attrType !== ecorepackage_1.EcorePackage.Literals.E_STRING)) { return null; } if (attrType === ecorepackage_1.EcorePackage.Literals.E_STRING) { tVal = stringVal.toString(); } else if (attrType === ecorepackage_1.EcorePackage.Literals.E_DOUBLE || attrType === ecorepackage_1.EcorePackage.Literals.E_DOUBLE_OBJECT) { tVal = Number.parseFloat(stringVal.toString()); } else if (attrType === ecorepackage_1.EcorePackage.Literals.E_FLOAT || attrType === ecorepackage_1.EcorePackage.Literals.E_FLOAT_OBJECT) { tVal = Number.parseFloat(stringVal.toString()); } else if (attrType === ecorepackage_1.EcorePackage.Literals.E_BOOLEAN || attrType === ecorepackage_1.EcorePackage.Literals.E_BOOLEAN_OBJECT) { tVal = stringVal.toString() === 'true'; } else if (attrType === ecorepackage_1.EcorePackage.Literals.E_SHORT || attrType === ecorepackage_1.EcorePackage.Literals.E_SHORT_OBJECT) { tVal = Number.parseInt(stringVal.toString(), 10); } else if (attrType === ecorepackage_1.EcorePackage.Literals.E_INT || attrType === ecorepackage_1.EcorePackage.Literals.E_INTEGER_OBJECT) { tVal = Number.parseInt(stringVal.toString(), 10); } else if (attrType === ecorepackage_1.EcorePackage.Literals.E_LONG || attrType === ecorepackage_1.EcorePackage.Literals.E_LONG_OBJECT) { tVal = Number.parseInt(stringVal.toString(), 10); } else if (attrType === ecorepackage_1.EcorePackage.Literals.E_DATE) { tVal = new Date(Date.parse(stringVal)); } else if (attrType instanceof eenum_impl_1.EEnumImpl) { const eenum = attrType; const literal = eenum.getEEnumLiteral(stringVal.toString()); if (literal != null) { tVal = literal.getLiteral(); } else { console.warn('Could not parse literal value ' + stringVal.toString() + ' for ' + eenum.getName()); } } return tVal; } /** * Creates a deep copy of an Root and it's containment hierarchy, with optional * lists for specifying specific EReferences to traverse or prune during the copy. New * IDs will be assigned to all copied entities. Internal references will be preserved where possible. * * @param toClone - root of container containment hierarchy to copy * @param prune - EReferences not to traverse during copy. If specified, these * references will not be traversed during copy * @param traverse - EReferences to traverse during copy. If specified, no EReference * will be traversed unless it is in this list. * @param target - Allows specification of an entity into which to copy data, which does not have * to be of the same type as the copied entity (non-overlapping structure will be ignored). * @returns the copy */ static clone(toClone, prune, traverse, target, oldToNewEntities) { //create new instance const newContainer = target ? target : (toClone .eClass() .getEPackage() .getEFactoryInstance() .create(toClone.eClass())); //map of old entities to new entities that will be filled in during the duplication const oldToNew = oldToNewEntities ? oldToNewEntities : new Map(); //recursive duplication of containment hierachy this.cloneInto(toClone, newContainer, prune ? prune : [], traverse ? traverse : [], oldToNew); //re-establish container internal references for all entities in the //container, including the container root itself const allOldEntities = toClone.eAllContents(); allOldEntities.push(toClone); for (const oldEntity of allOldEntities) { const newEntity = oldToNew.get(oldEntity); if (newEntity) { for (const eref of newEntity.eClass().getEAllReferences()) { //only look at container internal references if (!eref.isContainment() && !eref.isTransient()) { const oldRefVal = oldEntity.eGet(eref); if (oldRefVal) { //handle many-valued references if (eref.isMany()) { const newRefList = newEntity.eGet(eref); if (newRefList) { for (const oldRef of oldRefVal) { const newRef = oldToNew.get(oldRef); if (newRef) newRefList.add(newRef); else if (oldRef) newRefList.add(oldRef); } } } //handle single-valued references else { const newVal = oldToNew.get(oldRefVal); if (newVal) newEntity.eSet(eref, newVal); else newEntity.eSet(eref, oldRefVal); } } } } } } return newContainer; } static cloneInto(copyFrom, copyInto, pruneOnlyTheseReferences, traverseOnlyTheseReferences, oldToNew) { for (const attr of copyInto.eClass().getEAllAttributes()) { if (!attr.isVolatile()) { copyInto.eSet(attr, copyFrom.eGet(attr)); } } //assign new ID to entity if (copyInto.eClass().getEIDAttribute()) copyInto.eSet(copyInto.eClass().getEIDAttribute(), (0, uuid_1.v4)()); //register old to new entity in Map, if specified if (oldToNew) oldToNew.set(copyFrom, copyInto); //recurse containments for (const ref of copyInto.eClass().getEAllReferences()) { //determine if reference should be traversed let traverse = true; //do not traverse pruned references if (traverse && pruneOnlyTheseReferences) { for (const toPrune of pruneOnlyTheseReferences) { if (toPrune === ref) traverse = false; } } //do not traverse references not in the traverse list (if it is specified) if (traverse && traverseOnlyTheseReferences) { traverse = false; for (const toPrune of traverseOnlyTheseReferences) { if (toPrune === ref) traverse = true; } } //recursively copy the containment if (traverse) { if (ref.isContainment()) { //many-valued containment if (ref.isMany()) { const list = copyInto.eGet(ref); for (const toCopy of copyFrom.eGet(ref)) { list.add(this.clone(toCopy, pruneOnlyTheseReferences, traverseOnlyTheseReferences, null, oldToNew)); } } //single valued containment else { const toCopy = copyFrom.eGet(ref); if (toCopy) { copyInto.eSet(ref, this.clone(toCopy, pruneOnlyTheseReferences, traverseOnlyTheseReferences, null, oldToNew)); } } } //simply assign non-containment references if they have no eopposite else if (!ref.getEOpposite() && !ref.isContainer() && !ref.isVolatile()) { if (ref.isMany()) { const list = copyInto.eGet(ref); for (const member of copyFrom.eGet(ref)) list.add(member); } else copyInto.eSet(ref, copyFrom.eGet(ref)); } } } return copyInto; } static carbonCopy(obj) { return tjson_1.TJson.makeEObject(tjson_1.TJson.makeJson(obj)); } /** * Overwrites all attributes values, and replaces all containments with * carbon copies. Non-containment references are left alone. * * @param currentState * @param newState */ static shallowUpdate(currentState, newState) { //copy all EAttributes at the roote level for (const attr of newState.eClass().getEAllAttributes()) { if (!attr.isVolatile()) { currentState.eSet(attr, newState.eGet(attr)); } } //replace all contained instances for (const ref of newState.eClass().getEAllContainments()) { if (ref.isMany()) { const newContained = newState.eGet(ref); if (newContained) currentState.eSet(ref, this.carbonCopy(newContained)); else currentState.eSet(ref, null); } else { const currentContained = currentState.eGet(ref); currentContained.clear(); const newContainedList = newState.eGet(ref); for (const newContained of newContainedList) { currentContained.add(this.carbonCopy(newContained)); } } } } /** * Returns the package and transitive closure of all contained subpackages. * * @param pkg * @returns */ static allPackagesRecursive(pkg, addTo) { let pkgs = addTo ? addTo : []; pkgs.push(pkg); for (const sp of pkg.getESubPackages()) { this.allPackagesRecursive(sp, pkgs); } return pkgs; } static getRootEClasses(root) { if (!root) return []; const allPkgs = this.allPackagesRecursive(root); const allClasses = []; const containedClasses = new Set(); // First, collect all non-abstract, non-interface classes for (const ePackage of allPkgs) { ePackage.getEClassifiers().forEach((classifier) => { if (classifier instanceof eclass_impl_1.EClassImpl && !classifier.isAbstract() && !classifier.isInterface()) { allClasses.push(classifier); } }); } // Then, find which classes are contained by others allClasses.forEach((eClass) => { eClass.getEAllReferences().forEach((ref) => { if (ref.isContainment()) { const targetType = ref.getEType(); if (targetType instanceof eclass_impl_1.EClassImpl) { containedClasses.add(targetType); // Also add all subtypes allClasses.forEach((otherClass) => { if (otherClass.getEAllSuperTypes().contains(targetType)) { containedClasses.add(otherClass); } }); } } }); }); // Return classes that are not contained by any other class return allClasses.filter((c) => !containedClasses.has(c)).reverse(); } } exports.TUtils = TUtils; //# sourceMappingURL=tutils.js.map