@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
JavaScript
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