webgme-engine
Version:
WebGME server and Client API without a GUI
1,263 lines (1,106 loc) • 59.6 kB
JavaScript
/*globals define*/
/*eslint-env node, browser*/
/**
* @author kecso / https://github.com/kecso
*/
define(['common/util/assert', 'common/core/constants', 'blob/BlobConfig'], function (ASSERT, CONSTANTS, BlobConfig) {
'use strict';
function exportLibraryWithAssets(core, libraryRoot, callback) {
exportLibraryNodeByNode(core, libraryRoot, {withAssets: true}, callback);
}
function exportLibrary(core, libraryRoot, callback) {
exportLibraryNodeByNode(core, libraryRoot, {withAssets: false}, callback);
}
function getMetaDataOfExport(core, libraryRoot, options) {
var result = {},
root = core.getRoot(libraryRoot);
if (root === libraryRoot) {
result.type = CONSTANTS.EXPORT_TYPE_PROJECT;
} else {
result.type = CONSTANTS.EXPORT_TYPE_LIBRARY;
}
//FIXME check why does it changes with each import
// result.rootHash = core.getHash(root);
result.rootGuid = core.getGuid(root);
if (options && options.withAssets) {
result.hasAssets = true;
} else {
result.hasAssets = false;
}
return result;
}
function exportLibraryNodeByNode(core, libraryRoot, options, callback) {
var exportProject = {
_metadata: getMetaDataOfExport(core, libraryRoot, options),
root: {
path: core.getPath(libraryRoot),
guid: core.getGuid(libraryRoot)
},
containment: {},
bases: {},
relids: {},
nodes: {}
},
assetInfos = [],
pathToGuid = {},
ancestorPathToGuid = {},
taskQueue = [{path: core.getPath(libraryRoot), containment: exportProject.containment}],
maxParalelTasks = 100,
ongoingTaskCounter = 0,
timerId,
errorTxt = '',
root = core.getRoot(libraryRoot);
function getAttributes(node) {
var names = core.getOwnAttributeNames(node).sort(),
i,
result = {};
for (i = 0; i < names.length; i++) {
result[names[i]] = core.getAttribute(node, names[i]);
if (BlobConfig.hashRegex.test(result[names[i]])) {
assetInfos.push({
hash: result[names[i]],
attrName: names[i],
nodePath: core.getPath(node)
});
}
}
return result;
}
function getRegistry(node) {
var names = core.getOwnRegistryNames(node).sort(),
i,
result = {};
for (i = 0; i < names.length; i++) {
result[names[i]] = core.getRegistry(node, names[i]);
}
return result;
}
function getPointers(node) {
var names = core.getOwnPointerNames(node).sort(),
i,
result = {};
for (i = 0; i < names.length; i++) {
result[names[i]] = core.getPointerPath(node, names[i]);
}
return result;
}
function getSets(node) {
var setsInfo = {},
setNames = core.getSetNames(node).sort(),
i, j, k,
keys,
members,
memberInfo;
for (i = 0; i < setNames.length; i += 1) {
members = core.getOwnMemberPaths(node, setNames[i]);
setsInfo[setNames[i]] = [];
for (j = 0; j < members.length; j += 1) {
memberInfo = {
attributes: {},
guid: members[j],
registry: {}
};
//attributes
keys = core.getMemberAttributeNames(node, setNames[i], members[j]).sort();
for (k = 0; k < keys.length; k += 1) {
memberInfo.attributes[keys[k]] =
core.getMemberAttribute(node, setNames[i], members[j], keys[k]);
}
//registry
keys = core.getMemberRegistryNames(node, setNames[i], members[j]).sort();
for (k = 0; k < keys.length; k += 1) {
memberInfo.registry[keys[k]] =
core.getMemberRegistry(node, setNames[i], members[j], keys[k]);
}
//overridden flag
if (core.isFullyOverriddenMember(node, setNames[i], members[j])) {
memberInfo.overridden = true;
}
setsInfo[setNames[i]].push(memberInfo);
}
}
return setsInfo;
}
function fillAncestorHashes(node) {
var base = core.getBase(node);
while (base) {
ancestorPathToGuid[core.getPath(base)] = core.getGuid(base);
base = core.getBase(base);
}
}
function addChildrenTasks(node, containment) {
var childrenPaths = core.getOwnChildrenPaths(node),
i;
for (i = 0; i < childrenPaths.length; i += 1) {
taskQueue.push({path: childrenPaths[i], containment: containment});
}
}
function expNode(node, containment) {
var guid = core.getGuid(node);
if (exportProject.relids[guid] || exportProject.nodes[guid]) {
errorTxt += '[' + core.getPath(node) + '] has a colliding guid {' + guid + '} and will be skipped!\n';
return;
}
containment[guid] = {};
addChildrenTasks(node, containment[guid]);
pathToGuid[core.getPath(node)] = guid;
fillAncestorHashes(node);
exportProject.relids[guid] = core.getRelid(node);
exportProject.nodes[guid] = {
attributes: getAttributes(node),
base: core.getBase(node) ? core.getGuid(core.getBase(node)) : null,
meta: JSON.parse(JSON.stringify(core.getOwnJsonMeta(node))) || {},
parent: core.getParent(node) ? core.getGuid(core.getParent(node)) : null,
pointers: getPointers(node),
registry: getRegistry(node),
sets: getSets(node)/*,
constraints: getConstraints(node) this is now part of the meta */
};
}
function pathToGuidInObject(object) {
var keys = Object.keys(object),
i,
pathToCompositeId = function (path) {
var relPath = '',
guid = undefined,
pathArray = path.split('/');
do {
guid = ancestorPathToGuid[path] || pathToGuid[path];
if (!guid) {
relPath = '/' + pathArray.pop() + relPath;
path = pathArray.join('/');
}
} while (!guid && pathArray.length > 0);
return relPath === '' ? guid : guid + '@' + relPath;
};
for (i = 0; i < keys.length; i += 1) {
if (typeof object[keys[i]] === 'string' && object[keys[i]].indexOf('/') === 0) {
object[keys[i]] = pathToCompositeId(object[keys[i]]);
} else if (typeof object[keys[i]] === 'object' && object[keys[i]] !== null) {
pathToGuidInObject(object[keys[i]]);
}
}
}
function replacePathsWithGuid() {
var keys = Object.keys(exportProject.nodes || {}),
i,
node;
for (i = 0; i < keys.length; i += 1) {
node = exportProject.nodes[keys[i]];
pathToGuidInObject(node.pointers || {});
pathToGuidInObject(node.sets || {});
pathToGuidInObject(node.meta || {});
}
}
function gatherAncestorInfo() {
var i,
keys = Object.keys(ancestorPathToGuid),
bases = {};
for (i = 0; i < keys.length; i += 1) {
if (pathToGuid[keys[i]] === undefined && keys[i].indexOf(exportProject.root.path) !== 0) {
bases[ancestorPathToGuid[keys[i]]] = keys[i];
}
}
exportProject.bases = bases;
}
function orderNodesByGuid() {
//TODO this function can be removed if we stop counting on the stringify implicit ordering of keys
var orderedNodes = {},
guids = Object.keys(exportProject.nodes).sort(),
i;
for (i = 0; i < guids.length; i += 1) {
orderedNodes[guids[i]] = exportProject.nodes[guids[i]];
}
delete exportProject.nodes;
exportProject.nodes = orderedNodes;
}
function orderSetMembersByGuid() {
var guids = Object.keys(exportProject.nodes),
i, setNames, j,
node,
sorting = function (aObj, bObj) {
if (aObj.guid === bObj.guid) {
return 0;
}
if (aObj.guid < bObj.guid) {
return -1;
}
return 1;
};
for (i = 0; i < guids.length; i += 1) {
node = exportProject.nodes[guids[i]];
setNames = Object.keys(node.sets || {});
for (j = 0; j < setNames.length; j += 1) {
node.sets[setNames[j]] = node.sets[setNames[j]].sort(sorting);
}
}
}
function orderMetaInformationByGuid() {
var guids = Object.keys(exportProject.nodes),
i, names, j,
sortItemedObjects = function (input) {
var output = {
items: [],
minItems: [],
maxItems: []
},
itemGuids = JSON.parse(JSON.stringify(input.items || [])).sort(),
i;
if (input.max !== undefined) {
output.max = input.max;
}
if (input.min !== undefined) {
output.min = input.min;
}
for (i = 0; i < itemGuids.length; i += 1) {
output.items.push(itemGuids[i]);
output.minItems.push(input.minItems[input.items.indexOf(itemGuids[i])]);
output.maxItems.push(input.maxItems[input.items.indexOf(itemGuids[i])]);
}
return output;
},
node;
for (i = 0; i < guids.length; i += 1) {
node = exportProject.nodes[guids[i]];
if (node.meta) {
if (node.meta.children) {
node.meta.children = sortItemedObjects(node.meta.children);
}
if (node.meta.pointers) {
names = Object.keys(node.meta.pointers);
for (j = 0; j < names.length; j += 1) {
node.meta.pointers[names[j]] = sortItemedObjects(node.meta.pointers[names[j]]);
}
}
if (node.meta.aspects) {
names = Object.keys(node.meta.aspects);
for (j = 0; j < names.length; j += 1) {
node.meta.aspects[names[j]] = node.meta.aspects[names[j]].sort();
}
}
}
}
}
function orderRelidHashByGuids() {
//TODO this function can be removed if we stop counting on the stringify implicit ordering of keys
var orderedNodes = {},
guids = Object.keys(exportProject.relids).sort(),
i;
for (i = 0; i < guids.length; i += 1) {
orderedNodes[guids[i]] = exportProject.relids[guids[i]];
}
delete exportProject.relids;
exportProject.relids = orderedNodes;
}
function removeRootLevelFromContainmentInfo() {
exportProject.containment = exportProject.containment[exportProject.root.guid];
}
function orderContainmentByGuidRecursively(containment) {
var keys = Object.keys(containment).sort(),
i,
orderedContainment = {};
for (i = 0; i < keys.length; i += 1) {
orderedContainment[keys[i]] = orderContainmentByGuidRecursively(containment[keys[i]]);
}
return orderedContainment;
}
function orderContainment() {
//TODO this function can be removed if we stop counting on the stringify implicit ordering of keys
exportProject.containment = orderContainmentByGuidRecursively(exportProject.containment);
}
function getMetaSheetsInformation() {
var registry = core.getRegistry(root, 'MetaSheets'),
getMemberRegistry = function (setname, memberpath) {
var names = core.getMemberRegistryNames(root, setname, memberpath),
i,
registry = {};
for (i = 0; i < names.length; i++) {
registry[names[i]] = core.getMemberRegistry(root, setname, memberpath, names[i]);
}
return registry;
},
getMemberAttributes = function (setname, memberpath) {
var names = core.getMemberAttributeNames(root, setname, memberpath),
i,
attributes = {};
for (i = 0; i < names.length; i++) {
attributes[names[i]] = core.getMemberAttribute(root, setname, memberpath, names[i]);
}
return attributes;
},
getRegistryEntry = function (setname) {
var index = registry.length;
while (--index >= 0) {
if (registry[index].SetID === setname) {
return registry[index];
}
}
return {};
},
sheets = {},
keys = core.getSetNames(root),
elements, guid,
i,
j;
if (core.getParent(libraryRoot) === null) {
exportProject.metaSheets = {};
return;
}
for (i = 0; i < keys.length; i++) {
if (keys[i].indexOf('MetaAspectSet') === 0) {
elements = core.getMemberPaths(root, keys[i]);
sheets[keys[i]] = sheets[keys[i]] || {};
for (j = 0; j < elements.length; j++) {
guid = {guid: elements[j]};
pathToGuidInObject(guid);
guid = guid.guid;
if (guid.indexOf('/') === -1) {
sheets[keys[i]][guid] = {
registry: getMemberRegistry(keys[i], elements[j]),
attributes: getMemberAttributes(keys[i], elements[j])
};
}
}
if (sheets[keys[i]] && keys[i] !== 'MetaAspectSet') {
//we add the global registry values as well
sheets[keys[i]].global = getRegistryEntry(keys[i]);
}
}
}
exportProject.metaSheets = sheets;
}
function finalProcesses() {
replacePathsWithGuid();
gatherAncestorInfo();
orderNodesByGuid();
orderRelidHashByGuids();
orderSetMembersByGuid();
orderMetaInformationByGuid();
removeRootLevelFromContainmentInfo();
orderContainment();
getMetaSheetsInformation();
if (options.withAssets) {
exportProject = {projectJson: exportProject, assets: assetInfos};
}
callback(errorTxt, exportProject);
}
//here starts the export function
timerId = setInterval(function () {
var task;
if (ongoingTaskCounter < maxParalelTasks) {
task = taskQueue.shift();
if (!task && ongoingTaskCounter === 0) {
//we are done
clearInterval(timerId);
finalProcesses();
return;
}
if (task) {
ongoingTaskCounter += 1;
core.loadByPath(root, task.path, function (err, node) {
if (!err && node) {
expNode(node, task.containment);
} else {
errorTxt += '[' + task.path + '] cannot be loaded and will be missing from the export! \n';
}
ongoingTaskCounter -= 1;
});
}
}
}, 1);
}
function exportLibraryCached(core, libraryRoot, options, callback) {
//setting placeholder for cached values
options.cache = {nodes: {}, path2Guid: {}, guids: [], assets: [], extraBasePaths: {}, export: {}};
function gatherAncestors() {
//this function inserts the needed base classes which were not included in the library
var i, base, guid;
for (i = 0; i < options.cache.guids.length; i += 1) {
base = options.cache.nodes[options.cache.guids[i]];
while (base !== null) {
guid = core.getGuid(base);
if (!options.cache.nodes[guid]) {
options.cache.nodes[guid] = base;
options.cache.extraBasePaths[core.getPath(base)] = guid;
} else if (options.cache.guids.indexOf(guid) === -1) {
options.cache.extraBasePaths[core.getPath(base)] = guid;
}
base = core.getBase(base);
}
}
}
function pathsToSortedGuidList(pathsList) { //it will also filter out not wanted elements
var i, guids = [];
for (i = 0; i < pathsList.length; i++) {
if (options.cache.path2Guid[pathsList[i]]) {
guids.push(options.cache.path2Guid[pathsList[i]]);
}
}
return guids.sort();
}
function fillContainmentTree(node, jsonContainment) {
var childrenGuids = pathsToSortedGuidList(core.getChildrenPaths(node)),
i;
for (i = 0; i < childrenGuids.length; i++) {
jsonContainment[childrenGuids[i]] = {};
fillContainmentTree(options.cache.nodes[childrenGuids[i]], jsonContainment[childrenGuids[i]]);
}
}
function getNodeData(node) {
function getAttributesOfNode() {
var names = core.getOwnAttributeNames(node).sort(),
i,
value,
result = {};
for (i = 0; i < names.length; i += 1) {
value = core.getAttribute(node, names[i]);
result[names[i]] = value;
// Just make a simple regex test here
if (BlobConfig.hashRegex.test(value)) {
options.cache.assets.push({
hash: value,
attrName: names[i],
nodePath: core.getPath(node)
});
}
}
return result;
}
function getRegistryOfNode() {
var names = core.getOwnRegistryNames(node).sort(),
i,
result = {};
for (i = 0; i < names.length; i++) {
result[names[i]] = core.getRegistry(node, names[i]);
}
return result;
}
function getPointersOfNode() {
var names = core.getOwnPointerNames(node).sort(),
i,
result = {},
target;
for (i = 0; i < names.length; i++) {
target = core.getPointerPath(node, names[i]);
if (options.cache.path2Guid[target] || options.cache.extraBasePaths[target] || target === null) {
result[names[i]] =
options.cache.path2Guid[target] || options.cache.extraBasePaths[target] || null;
}
}
return result;
}
function getSetsOfNode() {
var names = core.getSetNames(node).sort(),
i, j, k,
result = {},
targetGuids,
attributeNames,
registryNames,
memberInfo,
path;
for (i = 0; i < names.length; i++) {
targetGuids = pathsToSortedGuidList(core.getOwnMemberPaths(node, names[i]));
result[names[i]] = [];
for (j = 0; j < targetGuids.length; j++) {
path = core.getPath(options.cache.nodes[targetGuids[j]]);
memberInfo = {
attributes: {},
guid: targetGuids[j],
registry: {}
};
//attributes
attributeNames = core.getMemberAttributeNames(node, names[i], path).sort();
for (k = 0; k < attributeNames.length; k++) {
memberInfo.attributes[attributeNames[k]] =
core.getMemberAttribute(node, names[i], path, attributeNames[k]);
}
//registry
registryNames = core.getMemberRegistryNames(node, names[i], path).sort();
for (k = 0; k < registryNames.length; k++) {
memberInfo.registry[registryNames[k]] =
core.getMemberRegistry(node, names[i], path, registryNames[k]);
}
//overridden flag
if (core.isFullyOverriddenMember(node, names[i], path)) {
memberInfo.overridden = true;
}
result[names[i]].push(memberInfo);
}
}
return result;
}
return {
attributes: getAttributesOfNode(node),
base: core.getBase(node) ? core.getGuid(core.getBase(node)) : null,
meta: pathsToGuids(JSON.parse(JSON.stringify(core.getOwnJsonMeta(node)) || {})),
parent: core.getParent(node) ? core.getGuid(core.getParent(node)) : null,
pointers: getPointersOfNode(node),
registry: getRegistryOfNode(node),
sets: getSetsOfNode(node)/*,
constraints: getConstraintsOfNode(node) the constraints now part of the meta definition */
};
}
function getMetaSheetInfo(root) {
var registry = core.getRegistry(root, 'MetaSheets'),
getMemberRegistry = function (setname, memberpath) {
var names = core.getMemberRegistryNames(root, setname, memberpath),
i,
registry = {};
for (i = 0; i < names.length; i++) {
registry[names[i]] = core.getMemberRegistry(root, setname, memberpath, names[i]);
}
return registry;
},
getMemberAttributes = function (setname, memberpath) {
var names = core.getMemberAttributeNames(root, setname, memberpath),
i,
attributes = {};
for (i = 0; i < names.length; i++) {
attributes[names[i]] = core.getMemberAttribute(root, setname, memberpath, names[i]);
}
return attributes;
},
getRegistryEntry = function (setname) {
var index = registry.length;
while (--index >= 0) {
if (registry[index].SetID === setname) {
return registry[index];
}
}
return {};
},
sheets = {},
keys = core.getSetNames(root),
elements, guid,
i,
j;
for (i = 0; i < keys.length; i++) {
if (keys[i].indexOf('MetaAspectSet') === 0) {
elements = core.getMemberPaths(root, keys[i]);
for (j = 0; j < elements.length; j++) {
guid = options.cache.path2Guid[elements[j]] || options.cache.extraBasePaths[elements[j]];
if (guid) {
sheets[keys[i]] = sheets[keys[i]] || {};
sheets[keys[i]][guid] = {
registry: getMemberRegistry(keys[i], elements[j]),
attributes: getMemberAttributes(keys[i], elements[j])
};
}
}
if (sheets[keys[i]] && keys[i] !== 'MetaAspectSet') {
//we add the global registry values as well
sheets[keys[i]].global = getRegistryEntry(keys[i]);
}
}
}
return sheets;
}
function getSortedIndex(arr) {
var index = [],
i;
for (i = 0; i < arr.length; i++) {
index.push(i);
}
index = index.sort((function (arr) {
return function (a, b) {
return ((arr[a] > arr[b]) ? 1 : ((arr[a] < arr[b]) ? -1 : 0));
};
})(arr));
return index;
}
function sortMultipleArrays() {
var index = getSortedIndex(arguments[0]),
i, j, arr;
for (j = 0; j < arguments.length; j++) {
arr = arguments[j].slice();
for (i = 0; i < arr.length; i++) {
arguments[j][i] = arr[index[i]];
}
}
}
function pathsToGuids(jsonObject) {
if (jsonObject && typeof jsonObject === 'object') {
var keys = Object.keys(jsonObject),
i, j, k, toDelete, tArray;
for (i = 0; i < keys.length; i++) {
if (keys[i] === 'items') {
//here comes the transformation itself
toDelete = [];
for (j = 0; j < jsonObject.items.length; j++) {
if (options.cache.path2Guid[jsonObject.items[j]]) {
jsonObject.items[j] = options.cache.path2Guid[jsonObject.items[j]];
} else if (options.cache.extraBasePaths[jsonObject.items[j]]) {
jsonObject.items[j] = options.cache.extraBasePaths[jsonObject.items[j]];
} else {
toDelete.push(j);
}
}
if (toDelete.length > 0) {
toDelete = toDelete.sort();
toDelete = toDelete.reverse();
for (j = 0; j < toDelete.length; j++) {
jsonObject.items.splice(toDelete[j], 1);
jsonObject.minItems.splice(toDelete[j], 1);
jsonObject.maxItems.splice(toDelete[j], 1);
}
}
sortMultipleArrays(jsonObject.items, jsonObject.minItems, jsonObject.maxItems);
} else if (keys[i] === 'aspects') {
//aspects are a bunch of named path list, so we have to handle them separately
tArray = Object.keys(jsonObject[keys[i]]);
for (j = 0; j < tArray.length; j++) {
//here comes the transformation itself
toDelete = [];
for (k = 0; k < jsonObject.aspects[tArray[j]].length; k++) {
if (options.cache.path2Guid[jsonObject.aspects[tArray[j]][k]]) {
jsonObject.aspects[tArray[j]][k] =
options.cache.path2Guid[jsonObject.aspects[tArray[j]][k]];
} else if (options.cache.extraBasePaths[jsonObject.aspects[tArray[j]][k]]) {
jsonObject.aspects[tArray[j]][k] =
options.cache.extraBasePaths[jsonObject.aspects[tArray[j]][k]];
} else {
toDelete.push(k);
}
}
if (toDelete.length > 0) {
toDelete = toDelete.sort();
toDelete = toDelete.reverse();
for (k = 0; k < toDelete.length; k++) {
jsonObject.aspects[tArray[j]].splice(toDelete[k], 1);
}
}
jsonObject.aspects[tArray[j]] = jsonObject.aspects[tArray[j]].sort();
}
} else {
if (typeof jsonObject[keys[i]] === 'object') {
jsonObject[keys[i]] = pathsToGuids(jsonObject[keys[i]]);
}
}
}
}
return jsonObject;
}
//--- here starts the function ---
//loading the complete sub-tree
core.loadSubTree(libraryRoot, function (err, loadedNodes) {
var guid, i, keys,
guids = options.cache.guids,
nodes = options.cache.nodes,
jsonExport = options.cache.export,
extraBasePaths = options.cache.extraBasePaths,
path2Guid = options.cache.path2Guid;
if (err) {
callback(err);
return;
}
loadedNodes = loadedNodes || [];
for (i = 0; i < loadedNodes.length; i += 1) {
guid = core.getGuid(loadedNodes[i]);
nodes[guid] = loadedNodes[i];
guids.push(guid);
path2Guid[core.getPath(loadedNodes[i])] = guid;
}
guids.sort();
//filling up extraBasePaths dictionary
gatherAncestors();
//saving extra base information into the export format
keys = Object.keys(extraBasePaths);
jsonExport.bases = {};
for (i = 0; i < keys.length; i++) {
jsonExport.bases[extraBasePaths[keys[i]]] = keys[i];
}
//export root info
jsonExport.root = {
path: core.getPath(libraryRoot),
guid: core.getGuid(libraryRoot)
};
//export relid dictionary
jsonExport.relids = {};
for (i = 0; i < guids.length; i += 1) {
jsonExport.relids[guids[i]] = core.getRelid(nodes[guids[i]]);
}
//export containment
jsonExport.containment = {};
fillContainmentTree(libraryRoot, jsonExport.containment);
//export node data
jsonExport.nodes = {};
for (i = 0; i < guids.length; i += 1) {
jsonExport.nodes[guids[i]] = getNodeData(nodes[guids[i]]);
}
//we export MetaSheet info only if not the whole project is exported!!!
jsonExport.metaSheets = core.getParent(libraryRoot) ? getMetaSheetInfo(core.getRoot(libraryRoot)) : {};
if (options.withAssets) {
jsonExport = {projectJson: jsonExport, assets: options.cache.assets};
}
callback(null, jsonExport);
});
}
function importLibrary(core, originLibraryRoot, updatedLibraryJson, callback) {
var exportOptions = {},
jsonExport,
nodes,
logTxt = '',
guids = {};
//function log(txt) {
// logTxt += '\n' + txt;
//}
//function logId(nodes, id) {
// var txtId = id + '';
// if (nodes[id] && nodes[id].attributes && nodes[id].attributes.name) {
// txtId = nodes[id].attributes.name + '(' + id + ')';
// }
//
// return txtId;
//}
function loadImportBases(callback) {
var needed = [],
error = null,
stillToGo = 0,
i,
guids = updatedLibraryJson.bases || {},
root = core.getRoot(originLibraryRoot),
guidList = Object.keys(guids),
baseLoaded = function (err) {
error = error || err;
if (--stillToGo === 0) {
callback(error);
}
},
loadBase = function (guid, path) {
core.loadByPath(root, path, function (err, node) {
if (err) {
return baseLoaded(err);
}
if (core.getGuid(node) !== guid) {
return baseLoaded('GUID mismatch');
}
nodes[guid] = node;
baseLoaded(null);
});
};
for (i = 0; i < guidList.length; i++) {
if (nodes[guidList[i]] === undefined) {
needed.push(guidList[i]);
}
}
if (needed.length > 0) {
stillToGo = needed.length;
for (i = 0; i < needed.length; i++) {
loadBase(needed[i], guids[needed[i]]);
}
} else {
callback(null);
}
}
function updateRegistry(guid) {
var keys, i, key,
node = nodes[guid],
jsonNode = updatedLibraryJson.nodes[guid];
keys = core.getOwnRegistryNames(node);
for (i = 0; i < keys.length; i++) {
core.delRegistry(node, keys[i]);
}
//keys = Object.keys(jsonNode.registry);
//for (i = 0; i < keys.length; i++) {
for (key in jsonNode.registry) {
core.setRegistry(node, key, jsonNode.registry[key]);
}
}
function updateAttributes(guid) {
var keys, i, key,
node = nodes[guid],
jsonNode = updatedLibraryJson.nodes[guid];
keys = core.getOwnAttributeNames(node);
for (i = 0; i < keys.length; i++) {
core.delAttribute(node, keys[i]);
}
// keys = Object.keys(jsonNode.attributes);
// for (i = 0; i < keys.length; i++) {
for (key in jsonNode.attributes) {
core.setAttribute(node, key, jsonNode.attributes[key]);
}
}
function updateConstraints(guid) {
var keys, i, key,
node = nodes[guid],
jsonNode = updatedLibraryJson.nodes[guid];
keys = core.getOwnConstraintNames(node);
for (i = 0; i < keys.length; i++) {
core.delConstraint(node, keys[i]);
}
// keys = Object.keys(jsonNode.constraints || {});
// for (i = 0; i < keys.length; i++) {
for (key in jsonNode.constraints) {
core.setConstraint(node, key, jsonNode.constraints[key]);
}
}
function updateNode(guid, parent) {
//first we check if the node have to be moved
var node = nodes[guid];
if (parent && core.getParent(node) && core.getGuid(parent) !== core.getGuid(core.getParent(node))) {
//parent changed so it has to be moved...
nodes[guid] = core.moveNode(node, parent);
}
updateAttributes(guid);
updateRegistry(guid);
updateConstraints(guid);
}
function addNode(guid) {
//at this point we assume that an empty vessel has been already created and part of the _nodes
updateAttributes(guid);
updateRegistry(guid);
updateConstraints(guid);
}
function updateNodes(guid, parent, containmentTreeObject) {
if (guids[guid] === 'update') {
updateNode(guid, parent);
}
var keys = Object.keys(containmentTreeObject),
i,
node = nodes[guid],
relid;
for (i = 0; i < keys.length; i++) {
if (guids[keys[i]] !== 'update') {
relid = updatedLibraryJson.relids[keys[i]];
if (core.getChildrenRelids(node).indexOf(relid) !== -1) {
relid = undefined;
}
//this child is a new one so we should create
nodes[keys[i]] = core.createNode({parent: node, guid: keys[i], relid: relid});
addNode(keys[i]);
}
updateNodes(keys[i], node, containmentTreeObject[keys[i]]);
}
}
function updateNodeInheritance(guid) {
core.setBase(nodes[guid], nodes[updatedLibraryJson.nodes[guid].base]);
}
function updateInheritance() {
var i,
guidList = Object.keys(updatedLibraryJson.nodes),
base;
for (i = 0; i < guidList.length; i++) {
base = core.getBase(nodes[guidList[i]]);
if ((base && core.getGuid(base) !== updatedLibraryJson.nodes[guidList[i]].base) ||
(base === null && updatedLibraryJson.nodes[guidList[i]].base !== null)) {
updateNodeInheritance(guidList[i]);
}
}
}
function getInheritanceBasedGuidOrder() {
var inheritanceOrdered = Object.keys(updatedLibraryJson.nodes).sort(),
i = 0,
baseGuid,
baseIndex;
while (i < inheritanceOrdered.length) {
baseGuid = updatedLibraryJson.nodes[inheritanceOrdered[i]].base;
if (baseGuid) {
baseIndex = inheritanceOrdered.indexOf(baseGuid);
if (baseIndex > i) {
inheritanceOrdered.splice(baseIndex, 1);
inheritanceOrdered.splice(i, 0, baseGuid);
} else {
++i;
}
} else {
++i;
}
}
return inheritanceOrdered;
}
function getCombinedTarget(combinedId) {
var idArray = combinedId.split('@'),
node = nodes[idArray[0]],
pathArray = (idArray[1] || '').split('/'),
i;
pathArray.shift();
for (i = 0; i < pathArray.length; i += 1) {
node = core.createNode({parent: node, relid: pathArray[i]});
}
return node;
}
function isCombinedId(id) {
var idArray = id.split('@');
return idArray.length === 2 && nodes[idArray[0]] && idArray[1][0] === '/';
}
function updateNodeRelations(guid) {
// Although it is possible that we set the base pointer at this point
// we should go through inheritance just to be sure.
var node = nodes[guid],
jsonNode = updatedLibraryJson.nodes[guid],
keys, i, j, k, target, memberGuid, member,
baseMemberPaths, base, key, set, setj;
//pointers
//The base pointer should be always removed, as at this point it could be already set falsly.
core.deletePointer(node, 'base');
if (guids[guid] === 'update') {
keys = core.getOwnPointerNames(node);
for (i = 0; i < keys.length; i++) {
core.deletePointer(node, keys[i]);
}
}
// keys = Object.keys(jsonNode.pointers);
// for (i = 0; i < keys.length; i++) {
for (key in jsonNode.pointers) {
target = jsonNode.pointers[key];
if (target === null) {
core.setPointer(node, key, null);
} else if (nodes[target] && guids[target] !== 'remove') {
core.setPointer(node, key, nodes[target]);
} else if (isCombinedId(target)) {
core.setPointer(node, key, getCombinedTarget(target));
} else {
throw new Error('invalid pointer target found [' + target +
'] for pointer [' + key + '] of node [' + guid + ']');
}
}
//sets
if (guids[guid] === 'update') {
keys = core.getSetNames(node);
for (i = 0; i < keys.length; i += 1) {
core.deleteSet(node, keys[i]);
}
}
// keys = Object.keys(jsonNode.sets);
// for (i = 0; i < keys.length; i++) {
for (key in jsonNode.sets) {
set = jsonNode.sets[key];
//for every set we create it, go through its members...
base = core.getBase(node);
baseMemberPaths = base !== null ? core.getMemberPaths(base, key) : [];
core.createSet(node, key);
for (j = 0; j < set.length; j++) {
setj = set[j];
memberGuid = setj.guid;
if (nodes[memberGuid] || isCombinedId(memberGuid)) {
member = getCombinedTarget(memberGuid);
var memberPath = core.getPath(member);
if (baseMemberPaths.indexOf(memberPath) === -1 ||
setj.overridden === true) {
core.addMember(node, key, member);
}
for (k in setj.attributes) {
core.setMemberAttribute(node, key, memberPath, k,
setj.attributes[k]);
}
for (k in setj.registry) {
core.setMemberRegistry(node, key, memberPath, k,
setj.registry[k]);
}
}
}
}
}
function updateRelations() {
var guids = getInheritanceBasedGuidOrder(),
i;
for (i = 0; i < guids.length; i++) {
updateNodeRelations(guids[i]);
}
}
function updateAttributeMeta(guid, meta) {
var jsonMeta = meta.attributes || {},
node = nodes[guid],
keys, i;
keys = Object.keys(jsonMeta);
for (i = 0; i < keys.length; i++) {
core.setAttributeMeta(node, keys[i], jsonMeta[keys[i]]);
}
}
function updateChildrenMeta(guid, meta) {
var jsonMeta = meta.children || {items: [], minItems: [], maxItems: []},
i;
ASSERT(jsonMeta.items.length === jsonMeta.minItems.length &&
jsonMeta.minItems.length === jsonMeta.maxItems.length);
core.setChildrenMetaLimits(nodes[guid], jsonMeta.min, jsonMeta.max);
for (i = 0; i < jsonMeta.items.length; i++) {
core.setChildMeta(nodes[guid], nodes[jsonMeta.items[i]], jsonMeta.minItems[i], jsonMeta.maxItems[i]);
}
}
function updatePointerMeta(guid, meta) {
var jsonMeta = meta.pointers || {},
keys = Object.keys(jsonMeta),
i, j;
for (i = 0; i < keys.length; i++) {
ASSERT(jsonMeta[keys[i]].items.length === jsonMeta[keys[i]].minItems.length &&
jsonMeta[keys[i]].maxItems.length === jsonMeta[keys[i]].minItems.length);
for (j = 0; j < jsonMeta[keys[i]].items.length; j++) {
core.setPointerMetaTarget(nodes[guid], keys[i], nodes[jsonMeta[keys[i]].items[j]],
jsonMeta[keys[i]].minItems[j], jsonMeta[keys[i]].maxItems[j]);
}
core.setPointerMetaLimits(nodes[guid], keys[i], jsonMeta[keys[i]].min, jsonMeta[keys[i]].max);
}
}
function updateAspectMeta(guid, meta) {
var jsonMeta = meta.aspects || {},
keys = Object.keys(jsonMeta),
i, j;
for (i = 0; i < keys.length; i++) {
for (j = 0; j < jsonMeta[keys[i]].length; j++) {
core.setAspectMetaTarget(nodes[guid], keys[i], nodes[jsonMeta[keys[i]][j]]);
}
}
}
function updateConstraintMeta(guid, meta) {
var jsonMeta = meta.constraints || {},
keys = Object.keys(jsonMeta),
i;
for (i = 0; i < keys.length; i++) {
core.setConstraint(nodes[guid], keys[i], jsonMeta[keys[i]]);
}
}
function updateMixinMeta(guid) {
var mixinGuids = updatedLibraryJson.nodes[guid].meta.mixins || [],
i;
for (i = 0; i < mixinGuids.length; i += 1) {
if (nodes[mixinGuids[i]]) {
core.addMixin(nodes[guid], core.getPath(nodes[mixinGuids[i]]));
}
}
}
function updateMeta(guid) {
if (guids[guid] === 'update') {
core.clearMetaRules(nodes[guid]);
}
var meta = updatedLibraryJson.nodes[guid].meta;
updateAttributeMeta(guid, meta);
updateChildrenMeta(guid, meta);
updatePointerMeta(guid, meta);
updateAspectMeta(guid, meta);
updateConstraintMeta(guid, meta);
updateMixinMeta(guid);
}
function updateMetaRules(guid, containmentTreeObject) {
var keys, i;
updateMeta(guid);
keys = Object.keys(containmentTreeObject);
for (i = 0; i < keys.length; i++) {
updateMetaRules(keys[i], containmentTreeObject[keys[i]]);
}
}
function importMetaSheetInfo(root) {
var oldSheets = updatedLibraryJson.metaSheets || {},
setMemberAttributesAndRegistry = function (setname, memberguid) {
var attributes = oldSheets[setname][memberguid].attributes || {},
registry = oldSheets[setname][memberguid].registry || {},
keys = Object.keys(attributes),
i;
for (i = 0; i < keys.length; i++) {
core.setMemberAttribute(root, setname, core.getPath(nodes[memberguid]), keys[i],
attributes[keys[i]]);
}
keys = Object.keys(registry);
for (i = 0; i < keys.length; i++) {
core.setMemberRegistry(root, setname, core.getPath(nodes[memberguid]), keys[i],
registry[keys[i]]);
}
},
getCurrentShortSheetInfo = function () {
//we collect all the current sheet info, as they are more appropriate to being updated
var metaNodes = core.getAllMetaNodes(root),
sheets = {},
i,
keys = core.getSetNames(root),
j,
members;
for (i = 0; i < ke