UNPKG

emfular

Version:

Pure TS library with basic types and parser to move from XPath references to runtime models

299 lines (286 loc) 8.67 kB
import { v4 } from 'uuid'; class RefHandler { static pathDivider = '/@'; static getIndexFromString(ref) { let substrings = ref.split('.'); return parseInt(substrings[substrings.length - 1]); } static computePrefix(formerPrefix, ownHeader) { return formerPrefix + RefHandler.pathDivider + ownHeader; } static getParentAddress(ref) { let pos = ref.lastIndexOf(this.pathDivider); return ref.substring(0, pos); } static mixWithIndex(prefix, index) { return prefix + '.' + index; } static createRef(ref, eClass) { return { $ref: ref, eClass: eClass }; } static createRefIfMissing(eClass, ref) { return ref ? ref : { $ref: '', eClass: eClass }; } } class ListUpdater { // ************** Helper ********************* static removeFromList(elem, list) { if (!list) { return false; } const index = list.indexOf(elem); if (index > -1) { list.splice(index, 1); return true; } return false; } static addToList(elem, list) { if (!list) { return false; } const index = list.indexOf(elem); if (index > -1) { return false; } else { list.push(elem); return true; } } static destructAllFromChangingList(list) { while (list?.length > 0) { list[0].destruct(); } } } /** base class for CORE models. * */ class Referencable { ref; gId; /* todo two open points: 1) we could enforce eClass already here and use it instead if deferring that to the constructor, 2) we could allow the parent to set the refpath so that we coul avoid the parameter of prepare * */ singleChildren = new Map(); listChildren = new Map(); getTreeParent() { return undefined; } constructor(ref) { this.ref = ref; this.gId = v4(); } getRef() { return this.ref; } setRef(ownPos) { this.ref.$ref = ownPos; } prepare(ownPos) { this.setRef(ownPos); for (let single of this.singleChildren) { single[1].prepare(RefHandler.computePrefix(ownPos, single[0])); } for (let list of this.listChildren) { Referencable.prepareList(RefHandler.computePrefix(ownPos, list[0]), list[1]); } } static prepareList(prefix, list) { if (list?.length > 0) { list.map((ref, index) => { ref.prepare(RefHandler.mixWithIndex(prefix, index)); }); } } removeFromListChild(elem, list) { // todo should I use the index on list children rather than the explicit list? if (elem.getTreeParent() == undefined || elem.getTreeParent() != this) { ListUpdater.removeFromList(elem, list); } else { console.log("Cannot remove from list, since I am currently the tree parent"); } } static listToRefs(list) { if (list) return list.map(elem => elem.getRef()); else return []; } destruct() { this.singleChildren.forEach(child => { child.destruct(); }); this.listChildren.forEach(list => { ListUpdater.destructAllFromChangingList(list); }); } getAttr(name) { let refContainers = Object.entries(this); let refContainer = refContainers.find((v) => v[0] == '_' + name); if (refContainer) { return refContainer[1]; } else throw new Error("Attribute _" + name + " not found on " + refContainers); } addToReferencableContainer(name, item) { return this.getAttr(name).add(item); } removeFromReferencableContainer(name, item) { return this.getAttr(name).remove(item); } } class ReferencableContainer { _parent; referenceName; inverseName; constructor(parent, referenceName, inverseName) { this._parent = parent; this.referenceName = referenceName; this.inverseName = inverseName; } } class ReferencableListContainer extends ReferencableContainer { _instance = []; constructor(parent, name, inverse) { super(parent, name, inverse); } add(item) { const index = this._instance.indexOf(item); if (index > -1) { return false; } else { this._instance.push(item); if (this.inverseName !== undefined) { return item.addToReferencableContainer(this.inverseName, this._parent); } return true; } } get() { return this._instance; } remove(item) { const index = this._instance.indexOf(item); if (index > -1) { this._instance.splice(index, 1); if (this.inverseName !== undefined) { return item.removeFromReferencableContainer(this.inverseName, this._parent); } return true; } return false; } } class ReferencableSingletonContainer extends ReferencableContainer { _instance; constructor(parent, referenceName, inverseName) { super(parent, referenceName, inverseName); } get() { return this._instance; } set(instance) { if (this.inverseName !== undefined) { this._instance?.removeFromReferencableContainer(this.inverseName, this._parent); instance.addToReferencableContainer(this.inverseName, this._parent); } this._instance = instance; } add(item) { if (this._instance == item) { return false; } else { this.set(item); return true; } } remove(item) { if (this._instance == item) { if (this.inverseName != undefined) { item.removeFromReferencableContainer(this.inverseName, this._parent); } this._instance = undefined; return true; } else { return false; } } } class ReferencableTreeListContainer extends ReferencableListContainer { } class ReferencableTreeParentContainer extends ReferencableSingletonContainer { } class Deserializer { completeJSON; constructorPointers; // all so far parsed objects context = new Map(); constructor(json, constructorPointers) { this.completeJSON = json; this.constructorPointers = constructorPointers; } getJsonFromTree($ref) { //first replace index access (.) by normal $ref divider, since they are all finally [] accesses const accessPaths = $ref.replaceAll('.', RefHandler.pathDivider).split(RefHandler.pathDivider); let res = this.completeJSON; for (let i = 1; i < accessPaths.length; i++) { res = res[(accessPaths[i])]; } return res; } getOrCreate(ref) { //get constructor from ref.eClass let res = this.get(ref.$ref); if (res) return res; else { // construct via pointer.... return this.create(ref); } } get($ref) { return this.context.get($ref); } create(ref) { let constrPointer = this.constructorPointers.get(ref.eClass); if (constrPointer) { let constr = constrPointer(ref.$ref); return constr(this); } else { throw (`Constructor pointer for ${ref} not set.`); } } put(elem) { this.context.set(elem.getRef().$ref, elem); } // for setting eClass assignment (i.e. on subtypes) static createRefList(formerPrefix, ownHeader, eClasses = []) { const prefix = RefHandler.computePrefix(formerPrefix, ownHeader); let res = eClasses ? eClasses : []; return res.map((eClass, index) => RefHandler.createRef(RefHandler.mixWithIndex(prefix, index), eClass)); } static createSingleRef(formerPrefix, ownHeader, eClass) { const ref = RefHandler.computePrefix(formerPrefix, ownHeader); return RefHandler.createRef(ref, eClass); } } /* * Public API Surface of emfular */ /** * Generated bundle index. Do not edit. */ export { Deserializer, ListUpdater, RefHandler, Referencable, ReferencableContainer, ReferencableListContainer, ReferencableSingletonContainer, ReferencableTreeListContainer, ReferencableTreeParentContainer }; //# sourceMappingURL=emfular.mjs.map