UNPKG

@metacell/geppetto-meta-core

Version:

The core functionality of geppetto-meta to build and simulate neuroscience data and models.

1,236 lines (1,148 loc) 108 kB
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } /** * Factory class with model creation methods. * * @author Giovanni Idili * @author Matteo Cantarelli */ import GeppettoModel from './model/GeppettoModel'; import Library from './model/Library'; import Type from './model/Type'; import Variable from './model/Variable'; import Value from './model/Value'; import Datasource from './model/Datasource'; import Query from './model/Query'; import CompositeType from './model/CompositeType'; import CompositeVisualType from './model/CompositeVisualType'; import ArrayType from './model/ArrayType'; import ImportType from './model/ImportType'; import ImportValue from './model/ImportValue'; import Instance from './model/Instance'; import ExternalInstance from './model/ExternalInstance'; import ArrayInstance from './model/ArrayInstance'; import ArrayElementInstance from './model/ArrayElementInstance'; import VisualGroup from './model/VisualGroup'; import VisualGroupElement from './model/VisualGroupElement'; import Pointer from './model/Pointer'; import PointerElement from './model/PointerElement'; import SimpleInstance from './model/SimpleInstance'; import SimpleConnectionInstance from './model/SimpleConnectionInstance'; import World from './model/World'; import AVisualCapability from './capabilities/AVisualCapability'; import AVisualGroupCapability from './capabilities/AVisualGroupCapability'; import AConnectionCapability from './capabilities/AConnectionCapability'; import AParameterCapability from './capabilities/AParameterCapability'; import AParticlesCapability from './capabilities/AParticlesCapability'; import AStateVariableCapability from './capabilities/AStateVariableCapability'; import ADerivedStateVariableCapability from './capabilities/ADerivedStateVariableCapability'; import Resources from './Resources'; /** * @class ModelFactory */ export var ModelFactory = { /* * Variables to keep track of tree building state go here if needed */ rawGeppetoModel: null, geppettoModel: null, instances: null, allPaths: [], allStaticVarsPaths: {}, allPathsIndexing: [], newPathsIndexing: [], /** * Creates and populates Geppetto model */ cleanModel: function cleanModel() { this.allPaths = []; this.allStaticVarsPaths = {}; this.allPathsIndexing = []; }, /** * Creates and populates Geppetto model * * @param jsonModel * @param storeRaw - store the raw and object models in the model factory * @param populateRefs - populate type references after model creation * * @returns {GeppettoModel} */ createGeppettoModel: function createGeppettoModel(jsonModel, storeModel, populateRefs) { // set defaults for optional flags if (storeModel === undefined) { // default behaviour store model storeModel = true; } if (populateRefs === undefined) { // default behaviour populate type references populateRefs = true; } var geppettoModel = null; if (jsonModel.eClass === 'GeppettoModel') { if (storeModel) { // store raw model for easy access during model building operations this.rawGeppetoModel = jsonModel; } geppettoModel = this.createModel(jsonModel); if (storeModel) { // store raw model for easy access during model building operations this.rawGeppetoModel = jsonModel; // store object model this.geppettoModel = geppettoModel; } // create variables if (jsonModel.variables) { console.warn('Geppetto variables are deprecated: use worlds instead.'); geppettoModel.variables = this.createVariables(jsonModel.variables, geppettoModel); } if (jsonModel.worlds) { this.fillWorldsFromRawModel(geppettoModel, jsonModel); } if (jsonModel.tags) { this.geppettoModel.tags = jsonModel.tags.map(function (wr) { return wr.name; }); } // create libraries for (var i = 0; i < jsonModel.libraries.length; i++) { if (!jsonModel.libraries[i].synched) { var library = this.createLibrary(jsonModel.libraries[i]); library.parent = geppettoModel; library.setTypes(this.createTypes(jsonModel.libraries[i].types, library)); geppettoModel.getLibraries().push(library); } } // create datasources geppettoModel.datasources = this.createDatasources(jsonModel.dataSources, geppettoModel); // create top level queries (potentially cross-datasource) geppettoModel.queries = this.createQueries(jsonModel.queries, geppettoModel); if (populateRefs) { // traverse everything and build shortcuts to children if composite --> containment == true this.populateChildrenShortcuts(geppettoModel); // traverse everything and populate type references in variables this.populateTypeReferences(geppettoModel); if (geppettoModel.getCurrentWorld()) { this.populateInstanceReferences(geppettoModel); // Add instances from the default world to allPaths var staticInstancesPaths = this._getStaticInstancePaths(geppettoModel); this.allPaths = this.allPaths.concat(staticInstancesPaths); this.allPathsIndexing = this.allPathsIndexing.concat(staticInstancesPaths); } } } return geppettoModel; }, createWorld: function createWorld(world) { var w = new World(world, this.createStaticInstances(world.instances)); w.parent = this.geppettoModel; w.variables = this.createVariables(world.variables, w); return w; }, createStaticInstances: function createStaticInstances(instances) { var _this = this; return instances ? instances.filter(function (inst) { return !inst.synched; }).map(function (instance) { return _this.createStaticInstance(instance); }) : []; }, createStaticInstance: function createStaticInstance(rawInstance) { var instance; switch (rawInstance.eClass) { case Resources.SIMPLE_INSTANCE_NODE: instance = new SimpleInstance(rawInstance); break; case Resources.SIMPLE_CONNECTION_INSTANCE_NODE: instance = new SimpleConnectionInstance(rawInstance); break; default: throw instance.eClass + " instance type is not supported"; } if (instance.value) { instance.value = this.createValue(rawInstance, { wrappedObj: rawInstance.value }); } return instance; }, /** * Populate shortcuts of children onto parents */ populateChildrenShortcuts: function populateChildrenShortcuts(node) { // check if getChildren exists, if so add shortcuts based on ids and recurse on each if (typeof node.getChildren === "function") { var children = node.getChildren(); if (children != undefined) { for (var i = 0; i < children.length; i++) { // do not populate shortcuts for array instances - children are accessed as array elements if (node instanceof Variable && children[i] instanceof Type) { // it's an anonymous type we don't want it to be in the path this.populateChildrenShortcuts(children[i]); var grandChildren = children[i].getChildren(); for (var j = 0; j < grandChildren.length; j++) { node[grandChildren[j].getId()] = grandChildren[j]; } continue; } if (node.getMetaType() != Resources.ARRAY_INSTANCE_NODE) { node[children[i].getId()] = children[i]; } this.populateChildrenShortcuts(children[i]); } } } }, populateInstanceReferences: function populateInstanceReferences(geppettoModel) { if (!geppettoModel.getWorlds().length) { return; } var _iterator = _createForOfIteratorHelper(geppettoModel.getWorlds()), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var world = _step.value; var _iterator2 = _createForOfIteratorHelper(world.getInstances()), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var instance = _step2.value; if (instance instanceof SimpleConnectionInstance) { this.populateConnections(instance); } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } }, /** * Populate type references */ populateTypeReferences: function populateTypeReferences(node) { // check if variable, if so populate type references if (node.getMetaType() == Resources.VARIABLE_NODE) { var types = node.getTypes(); var referencedTypes = []; var hasPointerType = false; var swapTypes = true; if (types != undefined) { for (var i = 0; i < types.length; i++) { // check if references are already populated if (types[i] instanceof Type) { swapTypes = false; break; } // get reference string - looks like this --> '//@libraries.1/@types.5'; var refStr = types[i].$ref; // if it's anonymous there's no reference if (refStr != undefined) { // go grab correct type from Geppetto Model var typeObj = this.resolve(refStr); // track if we have pointer type if (typeObj.getMetaType() == Resources.POINTER_TYPE) { hasPointerType = true; } // add to list referencedTypes.push(typeObj); } } if (swapTypes) { // set types to actual object references using backbone setter node.setTypes(referencedTypes); } } // check if pointer type if (hasPointerType) { var initialValues = node.getInitialValues(); if (initialValues != undefined && initialValues.length == 1) { // go to initial values and parse pointer into Pointer with its PointerElements var val = initialValues[0]; var pointer = this.createPointer(val.value); // populate pointerValue on variable node.pointerValue = pointer; } else { throw "The variable " + node.getId() + " does not have initial values. Initial values expected."; } } // add capabilities to variables var resolvedTypes = node.getTypes(); for (var j = 0; j < resolvedTypes.length; j++) { if (resolvedTypes[j].getMetaType() == Resources.PARAMETER_TYPE) { // if a variable has a Parameter type, add AParameterCapability to the variable node.extendApi(AParameterCapability); } else if (resolvedTypes[j].getMetaType() == Resources.CONNECTION_TYPE) { // if a variable has a connection type, add connection capability node.extendApi(AConnectionCapability); this.resolveConnectionValues(node); } } } else if (!(node instanceof ArrayType) && (node instanceof Type || node instanceof CompositeType)) { // take visual type string - looks like this --> '//@libraries.1/@types.5' var vizType = node.getVisualType(); if (vizType != undefined) { // replace with reference to actual type var typeObj = this.resolve(vizType.$ref); node.visualType = typeObj; } // resolve super type var superType = node.getSuperType(); if (superType != undefined) { var typeObjs = []; // convert to array if single element if (!(superType instanceof Array)) { superType = [superType]; } for (var a = 0; a < superType.length; a++) { if (superType[a].$ref) { // replace with reference to actual type typeObjs.push(this.resolve(superType[a].$ref)); } else { // replace with reference to actual type typeObjs.push(superType[a]); } } node.superType = typeObjs; } } else if (node instanceof ArrayType) { // take array type string - looks like this --> '//@libraries.1/@types.5' var arrayType = node.getType(); if (arrayType != undefined) { var typeObj = this.resolve(arrayType.$ref); node.type = typeObj; } // resolve super type var superType = node.getSuperType(); if (superType != undefined) { var typeObjs = []; // convert to array if single element if (!(superType instanceof Array)) { superType = [superType]; } for (var a = 0; a < superType.length; a++) { if (superType[a].$ref) { // replace with reference to actual type typeObjs.push(this.resolve(superType[a].$ref)); } else { // replace with reference to actual type typeObjs.push(superType[a]); } } node.superType = typeObjs; } } else if (node.getMetaType() === Resources.SIMPLE_INSTANCE_NODE || node.getMetaType() === Resources.SIMPLE_CONNECTION_INSTANCE_NODE) { node.type = this.resolve(node.getType().$ref); } // check if getChildren exists, if so recurse over children if (typeof node.getChildren === "function") { var children = node.getChildren(); if (children != undefined) { for (var i = 0; i < children.length; i++) { this.populateTypeReferences(children[i]); } } } }, /** * Creates pointer given a pointer in raw json format */ createPointer: function createPointer(jsonPointer) { // get raw pointer elements var rawElements = jsonPointer.elements; var pointerElements = []; // loop elements and create PointerElements (resolving variables / types) for (var i = 0; i < rawElements.length; i++) { var element = this.createPointerElement(rawElements[i]); pointerElements.push(element); } // create pointer object setting elements var pointer = new Pointer({ "wrappedObj": jsonPointer, "elements": pointerElements }); return pointer; }, /** * Creates pointer given a pointer in raw json format */ createPointerElement: function createPointerElement(jsonPointerElement) { var variable = this.resolve(jsonPointerElement.variable.$ref); var type = this.resolve(jsonPointerElement.type.$ref); var index = jsonPointerElement.index; // create pointer object setting elements var pointerElement = new PointerElement({ "wrappedObj": jsonPointerElement, "variable": variable, "type": type, "index": index }); return pointerElement; }, /** * Creates datasources starting from an array of datasources in the json model format */ createDatasources: function createDatasources(jsonDataSources, parent) { var dataSources = []; if (jsonDataSources != undefined) { for (var i = 0; i < jsonDataSources.length; i++) { var ds = this.createDatasource(jsonDataSources[i]); ds.parent = parent; dataSources.push(ds); } } return dataSources; }, /** * Creates variables starting from an array of variables in the json model format */ createVariables: function createVariables(jsonVariables, parent) { var variables = []; if (jsonVariables != undefined) { for (var i = 0; i < jsonVariables.length; i++) { if (!jsonVariables[i].synched) { var variable = this.createVariable(jsonVariables[i]); variable.parent = parent; // check if it has an anonymous type if (jsonVariables[i].anonymousTypes != undefined) { variable.anonymousTypes = this.createTypes(jsonVariables[i].anonymousTypes, variable); } variables.push(variable); } } } return variables; }, /** * Creates type objects starting from an array of types in the json model format */ createTypes: function createTypes(jsonTypes, parent) { var types = []; if (jsonTypes != undefined) { for (var i = 0; i < jsonTypes.length; i++) { if (!jsonTypes[i].synched) { var type = null; // check if it's composite type, visual type, array type or simple type if (jsonTypes[i].eClass == 'CompositeType' || jsonTypes[i].eClass == 'ConnectionType') { type = this.createCompositeType(jsonTypes[i]); } else if (jsonTypes[i].eClass == 'CompositeVisualType') { type = this.createCompositeVisualType(jsonTypes[i]); // inject visual capability to all CompositeVisualType type.extendApi(AVisualCapability); } else if (jsonTypes[i].eClass == 'ImportType') { type = this.createImportType(jsonTypes[i], null); // we store the index of the importType to speed up swapping procedures type._index = i; } else if (jsonTypes[i].eClass == 'ArrayType') { type = this.createArrayType(jsonTypes[i]); } else { type = this.createType(jsonTypes[i]); // inject visual capability if MetaType == VisualType if (type.getMetaType() == Resources.VISUAL_TYPE_NODE) { type.extendApi(AVisualCapability); } } // if getVisualType != null also inject visual capability if (type.getVisualType() != undefined) { type.extendApi(AVisualCapability); } // set parent type.parent = parent; types.push(type); } } } return types; }, /** * Creates and populates initial instance tree skeleton with any instance that needs to be visualized */ instantiateVariables: function instantiateVariables(geppettoModel) { var instances = []; // we need to explode instances for variables with visual types var varsWithVizTypes = []; // we need to fetch all potential instance paths (even for not exploded instances) var allPotentialInstancePaths = []; var allPotentialInstancePathsForIndexing = []; // builds list of vars with visual types and connection types - start traversing from top level variables var vars = geppettoModel.getAllVariables(); for (var i = 0; i < vars.length; i++) { this.fetchVarsWithVisualTypes(vars[i], varsWithVizTypes, ''); this.fetchAllPotentialInstancePaths(vars[i], allPotentialInstancePaths, allPotentialInstancePathsForIndexing, ''); } this.allPaths = this.allPaths.concat(allPotentialInstancePaths); this.allPathsIndexing = allPotentialInstancePathsForIndexing; var varsToInstantiate = varsWithVizTypes; // based on list, traverse again and build instance objects for (var j = 0; j < varsToInstantiate.length; j++) { this.buildInstanceHierarchy(varsToInstantiate[j], null, geppettoModel, instances); } // set instances to internal cache of the factory this.instances = instances; // populate shortcuts / populate connection references for (var k = 0; k < instances.length; k++) { this.populateChildrenShortcuts(instances[k]); this.populateConnections(instances[k]); } return instances; }, /** * Checks if new instances need to be created * * @param diffReport - lists variables and types that we need to check instances for */ createInstancesFromDiffReport: function createInstancesFromDiffReport(diffReport) { // get initial instance count (used to figure out if we added instances at the end) var instanceCount = this.getInstanceCount(window.Instances); var newInstancePaths = []; /* * shortcut function to get potential instance paths given a set types * NOTE: defined as a nested function to avoid polluting the visible API of ModelFactory */ var that = this; var getPotentialInstancePaths = function getPotentialInstancePaths(types) { var paths = []; for (var l = 0; l < types.length; l++) { if (types[l].hasCapability(Resources.VISUAL_CAPABILITY)) { // get potential instances with that type paths = paths.concat(that.getAllPotentialInstancesOfType(types[l].getPath())); } } return paths; }; // STEP 1: check new variables to see if any new instances are needed var varsWithVizTypes = []; var variables = this.getVariables(diffReport); for (var i = 0; i < variables; i++) { ModelFactory.fetchVarsWithVisualTypes(variables, varsWithVizTypes, ''); } // for each variable, get types and potential instances of those types for (var j = 0; j < varsWithVizTypes.length; j++) { // var must exist since we just fetched it from the geppettoModel var variable = eval(varsWithVizTypes[j]); var varTypes = variable.getTypes(); newInstancePaths = newInstancePaths.concat(getPotentialInstancePaths(varTypes)); } // STEP 2: check types and create new instances if need be var diffTypes = diffReport.types; newInstancePaths = newInstancePaths.concat(getPotentialInstancePaths(diffTypes)); // STEP 3: call getInstance to create the instances var newInstances = window.Instances.getInstance(newInstancePaths); // STEP 4: If instances were added, re-populate shortcuts for (var k = 0; k < newInstances.length; k++) { ModelFactory.populateChildrenShortcuts(newInstances[k]); } for (var k = 0; k < window.Instances.length; k++) { ModelFactory.populateConnections(window.Instances[k]); } return newInstances; }, /** * Populate connections */ populateConnections: function populateConnections(instance) { // check if it's a connection if (instance.getMetaType() === Resources.SIMPLE_CONNECTION_INSTANCE_NODE) { var _instance$a, _instance$b; if (!instance.a) { console.error("Error while adding connection ".concat(instance.getId(), ": cannot find first connection"), instance); } if (((_instance$a = instance.a) === null || _instance$a === void 0 ? void 0 : _instance$a.$ref) !== undefined) { var ref = instance.a.$ref; instance.a = this.resolve(ref); if (instance.a) { instance.a.addConnection(instance); } else { console.error("Error resolving reference ".concat(ref, " while adding connection ").concat(instance.getId(), " ")); } } if (!instance.b) { console.error("Error while adding connection ".concat(instance.getId(), ": cannot find second connection"), instance); } if (((_instance$b = instance.b) === null || _instance$b === void 0 ? void 0 : _instance$b.$ref) !== undefined) { var _ref = instance.b.$ref; instance.b = this.resolve(_ref); if (instance.b) { instance.b.addConnection(instance); } else { console.error("Error resolving reference ".concat(_ref, " while adding connection ").concat(instance.getId(), " ")); } } return; } { if (instance.getType().getMetaType() == Resources.CONNECTION_TYPE) { // do the bit of bidness this.resolveConnectionValues(instance); } } // check if getChildren exists, if so add shortcuts based on ids and recurse on each if (typeof instance.getChildren === "function") { var children = instance.getChildren(); if (children != undefined) { for (var i = 0; i < children.length; i++) { // recurse like no tomorrow this.populateConnections(children[i]); } } } }, /** * Merge Geppetto model parameter into existing Geppetto model * * @param rawModel - raw model to be merged, by deault only adds new vars / libs / types * @param overrideTypes - bool, mergeModel overrides type */ mergeModel: function mergeModel(rawModel, overrideTypes) { if (overrideTypes == undefined) { overrideTypes = false; } this.newPathsIndexing = []; // diff object to report back what changed / has been added var diffReport = { variables: [], types: [], libraries: [], worlds: [] }; // STEP 1: create new geppetto model to merge into existing one var diffModel = this.createGeppettoModel(rawModel, false, false); // STEP 2: add libraries/types if any are different (both to object model and json model) var diffLibs = diffModel.getLibraries(); var libs = this.geppettoModel.getLibraries(); for (var i = 0; i < diffLibs.length; i++) { if (diffLibs[i].getWrappedObj().synched == true) { // if synch placeholder lib, skip it continue; } var libMatch = false; for (var j = 0; j < libs.length; j++) { // if the library exists, go in and check for types diff if (diffLibs[i].getPath() == libs[j].getPath()) { libMatch = true; var diffTypes = diffLibs[i].getTypes(); var existingTypes = libs[j].getTypes(); // first loop on types - add new ones var addedTypes = []; /* * the types that need to be swapped in in the first array, the ImportTypes that need to be swapped out in the second one * these two arrays are synched by their index */ var typeMatched = []; var importTypeMatched = []; for (var k = 0; k < diffTypes.length; k++) { if (diffTypes[k].getWrappedObj().synched == true) { // if synch placeholder type, skip it continue; } var typeMatch = false; for (var m = 0; m < existingTypes.length; m++) { // check if the given diff type already exists if (diffTypes[k].getPath() == existingTypes[m].getPath()) { typeMatch = true; typeMatched.push(diffTypes[k]); importTypeMatched.push(existingTypes[m]); break; } } // if the type doesn't exist, append it to the library if (!typeMatch) { // add to list of types on raw library object if (libs[j].getWrappedObj().types == undefined) { libs[j].getWrappedObj().types = []; } libs[j].getWrappedObj().types.push(diffTypes[k].getWrappedObj()); // add to library in geppetto object model libs[j].addType(diffTypes[k]); addedTypes.push(diffTypes[k]); /* * TODO: add potential instance paths * NOTE: maybe not needed? the path will be added if a variable uses the type */ // add to diff report diffReport.types.push(diffTypes[k]); // populate the shortcuts for the added type this.populateChildrenShortcuts(diffTypes[k]); // let's populate the shortcut in the parent of the type, this might not exist if it was a fetch diffTypes[k].getParent()[diffTypes[k].getId()] = diffTypes[k]; } } for (var k = 0; k < addedTypes.length; k++) { // populate references for the new type this.populateTypeReferences(addedTypes[k]); } // second loop on types - override (if flag is set) if (overrideTypes) { for (var k = 0; k < typeMatched.length; k++) { // populate references for the swapped type this.populateTypeReferences(typeMatched[k]); var index = importTypeMatched[k]._index; var variablesToUpdate = importTypeMatched[k].getVariableReferences(); // swap type reference in ALL variables that point to it for (var x = 0; x < variablesToUpdate.length; x++) { this.swapTypeInVariable(variablesToUpdate[x], importTypeMatched[k], typeMatched[k]); } // swap type in raw model libs[j].getWrappedObj().types[index] = typeMatched[k].getWrappedObj(); // store overridden type (so that unresolve type can swap it back) typeMatched[k].overrideType = importTypeMatched[k]; // swap in object model typeMatched[k].parent = libs[j]; libs[j].getTypes()[index] = typeMatched[k]; // libs[j].removeImportType(importTypeMatched[k]); // add potential instance paths this.addPotentialInstancePathsForTypeSwap(typeMatched[k]); // update capabilities for variables and instances if any this.updateCapabilities(variablesToUpdate); // add to diff report diffReport.types.push(typeMatched[k]); // populate the shortcuts for the swapped type this.populateChildrenShortcuts(typeMatched[k]); // let's populate the shortcut in the parent of the type, this might not exist if it was a fetch typeMatched[k].getParent()[typeMatched[k].getId()] = typeMatched[k]; } } } } // if the library doesn't exist yet, append it to the model with everything that's in it if (!libMatch) { if (this.geppettoModel.getWrappedObj().libraries == undefined) { this.geppettoModel.getWrappedObj().libraries = []; } // add to raw model this.geppettoModel.getWrappedObj().libraries.push(diffLibs[i].getWrappedObj()); // add to geppetto object model diffLibs[i].parent = this.geppettoModel; this.geppettoModel.getLibraries().push(diffLibs[i]); // add to diff report diffReport.libraries.push(diffLibs[i]); // populate the shortcuts for the added library this.populateChildrenShortcuts(diffLibs[i]); // let's populate the shortcut in the parent of the library, this might not exist if it was a fetch diffLibs[i].getParent()[diffLibs[i].getId()] = diffLibs[i]; } } // STEP 3: add variables if any new ones are found (both to object model and json model) // STEP 3a: merge old geppettoModel.variables var diffVars = diffModel.variables; diffReport.variables = this._mergeVariables(diffVars, this.geppettoModel); var currentWorld = this.geppettoModel.getCurrentWorld(); // STEP 3b: merge world.variables and instances if (currentWorld) { diffVars = diffModel.getCurrentWorld().getVariables(); diffReport.worlds = rawModel.worlds.map(function (world) { return _objectSpread(_objectSpread({}, world), {}, { variables: [], instances: [] }); }); // TODO handle multiple worlds diffReport.worlds[0].variables = diffReport.worlds[0].variables.concat(this._mergeVariables(diffVars, currentWorld)); // TODO handle multiple worlds diffReport.worlds[0].instances = this._mergeInstances(diffModel.getCurrentWorld().getInstances(), currentWorld); this.populateInstanceReferences(diffModel); } return diffReport; }, _mergeVariables: function _mergeVariables(diffVars, parent) { var currentModelVars = parent.getVariables(true); var wrappedObj = parent.wrappedObj; var diffReportVars = []; for (var x = 0; x < diffVars.length; x++) { if (diffVars[x].getWrappedObj().synched == true) { // if synch placeholder var, skip it continue; } var match = currentModelVars.find(function (currModelVar) { return diffVars[x].getPath() == currModelVar.getPath(); }); // if no match, add it, it's actually new if (!match) { if (wrappedObj.variables == undefined) { wrappedObj.variables = []; } // append variable to raw model wrappedObj.variables.push(diffVars[x].getWrappedObj()); // add variable to geppetto object model diffVars[x].parent = parent; currentModelVars.push(diffVars[x]); // populate references for new vars this.populateTypeReferences(diffVars[x]); // find new potential instance paths and add to the list this.addPotentialInstancePaths([diffVars[x]]); diffReportVars.push(diffVars[x]); // populate the shortcuts for the added variable this.populateChildrenShortcuts(diffVars[x]); // let's populate the shortcut in the parent of the variable, this might not exist if it was a fetch diffVars[x].getParent()[diffVars[x].getId()] = diffVars[x]; } } return diffReportVars; }, /** * Merge simple instances * @param {*} diffInst wrapped instance objects to be added * @param {*} diffReportInst diff report list to be filled * @param {World} parent - parent container: the world in which the instances are defined */ _mergeInstances: function _mergeInstances(diffInst, parent) { var currentModelInst = parent.getInstances(); var wrappedObj = parent.wrappedObj; var diffReportInst = []; if (wrappedObj.instances == undefined) { wrappedObj.instances = []; } for (var x = 0; x < diffInst.length; x++) { if (diffInst[x].getWrappedObj().synched == true) { // if synch placeholder var, skip it continue; } diffInst[x].parent = this.geppettoModel; this.populateTypeReferences(diffInst[x]); var match = currentModelInst[diffInst[x].getId()]; if (match) { var matchIdx = currentModelInst.findIndex(function (currModelVar) { return diffInst[x].getPath() == currModelVar.getPath(); }); currentModelInst[matchIdx] = diffInst[x]; currentModelInst[match.getId()] = diffInst[x]; Instances[match.getId()] = diffInst[x]; } else { // if no match, add it, it's actually new diffReportInst.push(diffInst[x]); // append variable to raw model wrappedObj.instances.push(diffInst[x].getWrappedObj()); // add variable to geppetto object model currentModelInst.push(diffInst[x]); // find new potential instance paths and add to the list var newInstancePath = createInstancePathObj(diffInst[x]); this.allPaths.push(newInstancePath); this.allPathsIndexing.push(newInstancePath); // let's populate the shortcut in the parent of the variable, this might not exist if it was a fetch // window.Instances.push(diffInst[x]); this.geppettoModel[diffInst[x].getId()] = diffInst[x]; } } return diffReportInst; }, mergeValue: function mergeValue(rawModel, overrideTypes) { if (overrideTypes == undefined) { overrideTypes = false; } this.newPathsIndexing = []; // diff object to report back what changed / has been added var diffReport = { variables: [], types: [], libraries: [], worlds: [] }; var diffVars = diffReport.variables; // STEP 1: create new geppetto model to merge into existing one var diffModel = this.createGeppettoModel(rawModel, false, false); // STEP 1.5: add world if (rawModel.worlds && rawModel.worlds.length) { var _iterator3 = _createForOfIteratorHelper(rawModel.worlds), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var world = _step3.value; if (!world.synched) { diffReport.worlds.push(world); diffVars = world.variables; } } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } } // STEP 2: add libraries/types if any are different (both to object model and json model) var diffLibs = diffModel.getLibraries(); var libs = this.geppettoModel.getLibraries(); var libMatch = false; var i = 0, j = 0; for (i = 0; i < diffLibs.length; i++) { if (diffLibs[i].getWrappedObj().synched == true) { continue; } for (j = 0; j < libs.length; j++) { if (diffLibs[i].getPath() == libs[j].getPath()) { libMatch = true; break; } } if (libMatch) { break; } } // diffReport.libraries.push(diffLibs[i]); var diffTypes = diffLibs[i].getTypes(); var existingTypes = libs[j].getTypes(); var typeMatch = false; var k = 0, m = 0; for (k = 0; k < diffTypes.length; k++) { if (diffTypes[k].getWrappedObj().synched == true) { continue; } for (m = 0; m < existingTypes.length; m++) { if (diffTypes[k].getPath() == existingTypes[m].getPath()) { typeMatch = true; break; } } if (typeMatch) { break; } } // diffReport.types.push(diffTypes[k]); var diffVars = diffTypes[k].getVariables(); var vars = existingTypes[m].getVariables(); var varMatch = false; for (var x = 0; x < diffVars.length; x++) { if (diffVars[x].getWrappedObj().synched == true) { continue; } for (var y = 0; y < vars.length; y++) { if (diffVars[x].getPath() == vars[y].getPath()) { varMatch = true; this.populateTypeReferences(diffVars[x]); vars[y] = diffVars[x]; diffVars.push(vars[y]); // FIXME variables to worlds break; } } if (varMatch) { break; } } return diffReport; }, /** * Updates capabilities of variables and their instances if any * * @param variables */ updateCapabilities: function updateCapabilities(variables) { // some bit of code encapsulated for private re-use var that = this; var _updateInstancesCapabilities = function updateInstancesCapabilities(instances) { for (var j = 0; j < instances.length; j++) { // check if visual type and inject AVisualCapability var visualType = instances[j].getVisualType(); // check if visual type and inject AVisualCapability if (!(visualType instanceof Array) && visualType != null && visualType != undefined || visualType instanceof Array && visualType.length > 0) { if (!instances[j].hasCapability(Resources.VISUAL_CAPABILITY)) { instances[j].extendApi(AVisualCapability); that.propagateCapabilityToParents(AVisualCapability, instances[j]); if (visualType instanceof Array && visualType.length > 1) { throw "Support for more than one visual type is not implemented."; } // check if it has visual groups - if so add visual group capability if (typeof visualType.getVisualGroups === "function" && visualType.getVisualGroups() != null && visualType.getVisualGroups().length > 0) { instances[j].extendApi(AVisualGroupCapability); instances[j].setVisualGroups(visualType.getVisualGroups()); } } } // check if it has connections and inject AConnectionCapability if (instances[j].getType().getMetaType() == Resources.CONNECTION_TYPE) { if (!instances[j].hasCapability(Resources.CONNECTION_CAPABILITY)) { instances[j].extendApi(AConnectionCapability); that.resolveConnectionValues(instances[j]); } } if (instances[j].getType().getMetaType() == Resources.STATE_VARIABLE_TYPE) { if (!instances[j].hasCapability(Resources.STATE_VARIABLE_CAPABILITY)) { instances[j].extendApi(AStateVariableCapability); } } if (instances[j].getType().getMetaType() == Resources.DERIVED_STATE_VARIABLE_TYPE) { if (!instances[j].hasCapability(Resources.DERIVED_STATE_VARIABLE_CAPABILITY)) { instances[j].extendApi(ADerivedStateVariableCapability); } } if (instances[j].getType().getMetaType() == Resources.PARAMETER_TYPE) { if (!instances[j].hasCapability(Resources.PARAMETER_CAPABILITY)) { instances[j].extendApi(AParameterCapability); } } // getChildren of instance and recurse by the power of greyskull! _updateInstancesCapabilities(instances[j].getChildren()); } }; // update capabilities for variables for (var i = 0; i < variables.length; i++) { var resolvedTypes = variables[i].getTypes(); for (var j = 0; j < resolvedTypes.length; j++) { if (resolvedTypes[j].getMetaType() == Resources.PARAMETER_TYPE) { // if a variable has a Parameter type, add AParameterCapability to the variable if (!variables[i].hasCapability(Resources.PARAMETER_CAPABILITY)) { variables[i].extendApi(AParameterCapability); } } else if (resolvedTypes[j].getMetaType() == Resources.CONNECTION_TYPE) { // if a variable has a connection type, add connection capability if (!variables[i].hasCapability(Resources.CONNECTION_CAPABILITY)) { variables[i].extendApi(AConnectionCapability); } } } var varInstances = this.getAllInstancesOf(variables[i]); // update instances capabilities _updateInstancesCapabilities(varInstances); } }, /** * Adds potential instance paths to internal cache * * @param variables */ addPotentialInstancePaths: function addPotentialInstancePaths(variables) { var potentialInstancePaths = []; var potentialInstancePathsForIndexing = []; for (var i = 0; i < variables.length; i++) { this.fetchAllPotentialInstancePaths(variables[i], potentialInstancePaths, potentialInstancePathsForIndexing, ''); } // add to allPaths and to allPathsIndexing (assumes they are new paths) this.allPaths = this.allPaths.concat(potentialInstancePaths); this.allPathsIndexing = this.allPathsIndexing.concat(potentialInstancePathsForIndexing); this.newPathsIndexing = this.newPathsIndexing.concat(potentialInstancePathsForIndexing); }, /** * Add potential instance paths to internal cache given a new type * * @param type */ addPotentialInstancePathsForTypeSwap: function addPotentialInstancePathsForTypeSwap(type) { var typePath = type.getPath(); // Get all paths for the new type var partialPathsForNewType = []; var partialPathsForNewTypeIndexing = []; this.fetchAllPotentialInstancePathsForType(type, partialPathsForNewType, partialPathsForNewTypeIndexing, []); // Get all potential instances for the type we are swapping var potentialInstancesForNewtype = ModelFactory.getAllPotentialInstancesOfType(typePath); var potentialInstancesForNewtypeIndexing = ModelFactory.getAllPotentialInstancesOfType(typePath, this.allPathsIndexing); this.allPaths.replace = []; // Generate new paths and add for (var i = 0; i < potentialInstancesForNewtype.length; i++) { for (var j = 0; j < partialPathsForNewType.length; j++) { // figure out is we are dealing with statics var path = undefined; if (partialPathsForNewType[j]["static"] === true) { path = partialPathsForNewType[j].path; } else { path = potentialInstancesForNewtype[i] + '.' + partialPathsForNewType[j].path; } var entry = { path: path, metaType: partialPathsForNewType[j].metaType, type: partialPathsForNewType[j].type }; this.allPaths.replace.push(entry); } } this.allPathsIndexing.replace = []; this.newPathsIndexing.replace = []; // same as above for indexing paths for (var i = 0; i < potentialInstancesForNewtypeIndexing.length; i++) { for (var j = 0; j < partialPathsForNewTypeIndexing.length; j++) { // figure out is we are dealing with statics var path = undefined; if (partialPathsForNewTypeIndexing[j]["static"] === true) { path = partialPathsForNewTypeIndexing[j].path; } else { path = potentialInstancesForNewtypeIndexing[i] + '.' + partialPathsForNewTypeIndexing[j].path; } var entry = { path: path, metaType: partialPathsForNewType[j].metaType, type: partialPathsForNewType[j].type }; this.allPathsIndexing.replace.push(entry); this.newPathsIndexing.replace.push(entry); } } // If variable already in allPathsIndexing, newPathsIndexing and allPaths, remove it before adding the new variable for (var _i = 0, _arr = [this.allPathsIndexing, this.newPathsIndexing, this.allPaths]; _i < _arr.length; _i++) { var list = _arr[_i]; var is = []; for (var i = 0; i < list.length; ++i) { if (list.replace.indexOf(list[i].path) > -1) { is.push(i); } } for (var i = 0; i < list.replace.length; ++i) { if (is[i] > -1) { list.splice(is[i], 1); } list.push(list.replace[i]); } delete list.replace; } // look for import type references and amend type for (var _i2 = 0, _arr2 = [this.allPaths, this.allPathsIndexing]; _i2 < _arr2.length; _i2++) { var list = _arr2[_i2]; for (var i = 0; i < list.length; ++i) { if (list[i].type == typePath) { list[i].metaType = type.getMetaType(); } } } }, /** * Given a variable, swap a given type out for another type (recursive on nested types and vars) * * @param variable * @param typeToSwapOut * @param typeToSwapIn */ swapTypeInVariable: function swapTypeInVariable(variable, typeToSwapOut, typeToSwapIn) { // ugly but we need the actual arrays stored in the variable as we'll be altering them var types = variable.types; var anonTypes = variable.anonymousTypes; if (types && types.length > 0) { this.swapTypeInTypes(types, typeToSwapOut, typeToSwapIn); } if (anonTypes && anonTypes.length > 0) { this.swapTypeInTypes(anonTypes, typeToSwapOut, typeToSwapIn); } }, /** * Given a set of types, swap a given type out for another type (recursive on nested variables) * * @param types * @param typeToSwapOut * @param typeToSwapIn */ swapTypeInTypes: function swapTypeInTypes(types, typeToSwapOut, typeToSwapIn) { for (var y = 0; y < types.length; y++) { if (types[y].getMetaType() == typeToSwapOut.getMetaType() && types[y].getId() == typeToSwapOut.getId()) { // swap type referenced with the override one types[y] = typeToSwapIn; } else if (types[y].getMetaType() == Resources.COMPOSITE_TYPE_NODE) { // if composite - recurse for each var var nestedVars = types[y].getVariables(); for (var x = 0; x < nestedVars.length; x++) { this.swapTypeInVariable(nestedVars[x], typeToSwapOut, typeToSwapIn); } } } }, /** * Adds instances to a list of existing instances. I