UNPKG

webgme-engine

Version:

WebGME server and Client API without a GUI

1,219 lines (997 loc) 53.5 kB
/*globals define*/ /*eslint-env node, browser*/ /** * @author kecso / https://github.com/kecso */ define([ 'common/core/CoreAssert', 'common/core/tasync', 'common/core/constants', 'common/util/random', 'common/core/CoreIllegalOperationError' ], function (ASSERT, TASYNC, CONSTANTS, RANDOM, CoreIllegalOperationError) { 'use strict'; var LibraryCore = function (innerCore, options) { ASSERT(typeof options === 'object'); ASSERT(typeof options.globConf === 'object'); ASSERT(typeof options.logger !== 'undefined'); var logger = options.logger, self = this, key; for (key in innerCore) { this[key] = innerCore[key]; } logger.debug('initialized LibraryCore'); //<editor-fold=Helper Functions> function getAllLibraryRoots(node) { var roots = []; while (node) { if (self.isLibraryRoot(node)) { roots.push(node); } node = self.getParent(node); } return roots; } function getLibraryName(node) { ASSERT(self.isValidNode(node)); var name = ''; node = self.getParent(node); while (node) { if (self.isLibraryRoot(node) && self.getParent(node) !== null) { name = self.getAttribute(node, 'name') + CONSTANTS.NAMESPACE_SEPARATOR + name; } node = self.getParent(node); } return name; } function getLibraryRootsInfo(node) { var allMetaNodes = self.getAllMetaNodes(node), libraryRoots = {}, path, roots, i, name; for (path in allMetaNodes) { roots = getAllLibraryRoots(allMetaNodes[path]); for (i = 0; i < roots.length; i += 1) { name = getLibraryName(roots[i]) + self.getAttribute(roots[i], 'name'); if (!libraryRoots[name]) { libraryRoots[name] = roots[i]; } } } return libraryRoots; } function getRootOfLibrary(node, name) { return self.getRoot(node).libraryRoots[name]; } function getLibraryRoot(node) { while (node) { if (self.isLibraryRoot(node)) { return node; } node = self.getParent(node); } return null; } function getLibraryInfo(libraryRootHashOrNode) { var isNode = typeof libraryRootHashOrNode === 'object', libraryName = '', libraryNamePrefix = '', getPath = function (node) { if (isNode) { return self.getPath(node, libraryRootHashOrNode); } else { return self.getPath(node); } }, getName = function (node) { return self.getFullyQualifiedName(node).substr(libraryNamePrefix.length); }, getGuid = function (node) { if (isNode) { return self.getLibraryGuid(node, libraryName); } else { return self.getGuid(node); } }, load = function () { if (isNode) { return self.loadSubTree(libraryRootHashOrNode); } else { return self.loadTree(libraryRootHashOrNode); } }; if (isNode) { libraryName = self.getAttribute(libraryRootHashOrNode, 'name'); libraryNamePrefix = libraryName + '.'; } return TASYNC.call(function (libraryNodes) { var info = {}, infoItem, i, inMeta = self.getMemberPaths(libraryNodes[0], CONSTANTS.META_SET_NAME); for (i = 1; i < libraryNodes.length; i += 1) { infoItem = { path: getPath(libraryNodes[i]), hash: self.getHash(libraryNodes[i]), fcn: getName(libraryNodes[i]), isMeta: inMeta.indexOf(self.getPath(libraryNodes[i])) !== -1, }; info[getGuid(libraryNodes[i])] = infoItem; } return info; }, load()); //we use that the root of the library is always the first element } function removeLibraryRelations(root, path) { var overlayItems = self.overlayQuery(root, path), i; for (i = 0; i < overlayItems.length; i += 1) { if (overlayItems[i].s === path || overlayItems.t === path) { self.overlayRemove(root, overlayItems[i].s, overlayItems[i].n, overlayItems[i].t); } } } function moveLibraryRelations(root, from, to) { var overlayItems = self.overlayQuery(root, from), i; for (i = 0; i < overlayItems.length; i += 1) { if (overlayItems[i].s === from) { self.overlayRemove(root, overlayItems[i].s, overlayItems[i].n, overlayItems[i].t); self.overlayInsert(root, to, overlayItems[i].n, overlayItems[i].t); } else if (overlayItems[i].t === from) { self.overlayRemove(root, overlayItems[i].s, overlayItems[i].n, overlayItems[i].t); self.overlayInsert(root, overlayItems[i].s, overlayItems[i].n, to); } } } function isPathInSubTree(fullPath, subTreePath) { if (fullPath === subTreePath) { return true; } if (fullPath.indexOf(subTreePath + CONSTANTS.PATH_SEP) === 0) { return true; } return false; } function isClosureInternalTarget(targetPath, closureInfo) { var selectionPath; for (selectionPath in closureInfo.selection) { if (isPathInSubTree(targetPath, selectionPath)) { return true; } } return false; } function collectBaseInformation(baseNode, closureInfo) { var libraryRoots = getAllLibraryRoots(baseNode), namespaceInfo = {}, i, namespace; for (i = 0; i < libraryRoots.length; i += 1) { namespace = self.getFullyQualifiedName(libraryRoots[i]); namespaceInfo[namespace] = { info: self.getLibraryInfo(libraryRoots[i], namespace), guid: self.getLibraryGuid(baseNode, namespace) }; if (namespaceInfo[namespace].info && namespaceInfo[namespace].info.hash) { namespaceInfo[namespace].hash = namespaceInfo[namespace].info.hash; } } closureInfo.bases[self.getGuid(baseNode)] = { originGuid: libraryRoots.length > 0 ? self.getLibraryGuid(baseNode) : self.getGuid(baseNode), name: self.getAttribute(baseNode, 'name'), fullName: self.getFullyQualifiedName(baseNode), namsespaces: namespaceInfo }; } function addRelationsFromNodeToClosureInfo(node, allMetaNodes, closureInfo) { var basePath = self.getPath(node), overlayInfo = self.getRawOverlayInformation(node), overlayKey, pointerName, path, targetPath; for (overlayKey in overlayInfo) { path = basePath + overlayKey; if (isClosureInternalTarget(path, closureInfo)) { for (pointerName in overlayInfo[overlayKey]) { if (self.isPointerName(pointerName)) { targetPath = basePath + overlayInfo[overlayKey][pointerName]; if (pointerName === CONSTANTS.BASE_POINTER) { if (allMetaNodes[targetPath]) { collectBaseInformation(allMetaNodes[targetPath], closureInfo); closureInfo.relations.preserved[path] = closureInfo.relations.preserved[path] || {}; closureInfo.relations.preserved[path][CONSTANTS.BASE_POINTER] = self.getGuid(allMetaNodes[targetPath]); } else if (isClosureInternalTarget(targetPath, closureInfo)) { closureInfo.relations.preserved[path] = closureInfo.relations.preserved[path] || {}; closureInfo.relations.preserved[path][CONSTANTS.BASE_POINTER] = targetPath; } else { closureInfo.relations.lost[path] = closureInfo.relations.lost[path] || {}; closureInfo.relations.lost[path][CONSTANTS.BASE_POINTER] = targetPath; } } else { if (isClosureInternalTarget(targetPath, closureInfo)) { closureInfo.relations.preserved[path] = closureInfo.relations.preserved[path] || {}; closureInfo.relations.preserved[path][pointerName] = targetPath; } else { closureInfo.relations.lost[path] = closureInfo.relations.lost[path] || {}; closureInfo.relations.lost[path][pointerName] = targetPath; } } } } } } } function normalizeSelectionForClosure(nodes) { var paths = [], i, j, nodesToKeep = [], nodesToCut = {}; for (i = 0; i < nodes.length; i += 1) { paths.push(self.getPath(nodes[i])); } for (i = 0; i < paths.length; i += 1) { for (j = 0; j < paths.length; j += 1) { if (i !== j && isPathInSubTree(paths[j], paths[i])) { nodesToCut[paths[j]] = true; } } } for (i = 0; i < paths.length; i += 1) { if (nodesToCut[paths[i]] !== true) { nodesToKeep.push(nodes[i]); } } return nodesToKeep; } function getBasePathOfPath(path, closureInfo) { var basePath; for (basePath in closureInfo.hashes) { if (isPathInSubTree(path, basePath)) { return basePath; } } return ''; } function mapRelationEndings(closureInfo) { var source, sourceInfo, name, basePath; for (source in closureInfo.relations.preserved) { sourceInfo = closureInfo.relations.preserved[source]; for (name in sourceInfo) { if (!closureInfo.bases[sourceInfo[name]]) { basePath = getBasePathOfPath(sourceInfo[name], closureInfo); if (basePath) { sourceInfo[name] = sourceInfo[name].replace(basePath, closureInfo.hashes[basePath]); } else { logger.error('during closure generation unknown based target [' + sourceInfo[name] + '] remained.'); delete sourceInfo[name]; } } } basePath = getBasePathOfPath(source, closureInfo); if (basePath) { closureInfo.relations.preserved[source.replace(basePath, closureInfo.hashes[basePath])] = closureInfo.relations.preserved[source]; delete closureInfo.relations.preserved[source]; } else { logger.error('during closure generation unknown based source [' + source + '] remained.'); delete closureInfo.relations.preserved[source]; } } } function gatherOccurancesOfType(baseGuid, closureInformation, allMetaNodes) { var keys = Object.keys(allMetaNodes), occurrences = [], i; for (i = 0; i < keys.length; i += 1) { if ((self.isLibraryElement(allMetaNodes[keys[i]]) && closureInformation.bases[baseGuid].originGuid === self.getLibraryGuid(allMetaNodes[keys[i]])) || closureInformation.bases[baseGuid].originGuid === self.getGuid(allMetaNodes[keys[i]])) { occurrences.push(allMetaNodes[keys[i]]); } } return occurrences; } function checkClosure(allMetaNodes, closureInformation) { var keys = Object.keys(allMetaNodes), occurrences, i, j, errorTxt; // First check against direct GUID matches.. closureInformation.destinationBases = {}; for (i = 0; i < keys.length; i += 1) { closureInformation.destinationBases[self.getGuid(allMetaNodes[keys[i]])] = keys[i]; } keys = Object.keys(closureInformation.bases || {}); for (i = 0; i < keys.length; i += 1) { if (!closureInformation.destinationBases[keys[i]]) { // ... if no match try to find a unique match based on library GUIDs. occurrences = gatherOccurancesOfType(keys[i], closureInformation, allMetaNodes); if (occurrences.length === 0) { throw new CoreIllegalOperationError('Cannot find necessary base [' + closureInformation.bases[keys[i]].fullName + ' : ' + keys[i] + ']'); } else if (occurrences.length === 1) { closureInformation.destinationBases[keys[i]] = self.getPath(occurrences[0]); } else { errorTxt = 'Ambiguous occurrences of base [' + closureInformation.bases[keys[i]].fullName + ' : ' + keys[i] + '] ( '; for (j = 0; j < occurrences.length; j += 1) { errorTxt += '[' + self.getFullyQualifiedName(occurrences[j]) + ' : ' + self.getPath(occurrences[j]) + '] '; } errorTxt += ')'; throw new CoreIllegalOperationError(errorTxt); } } } return null; } function getAncestor(node, from, to) { var fromArray = from.split(CONSTANTS.PATH_SEP), toArray = to.split(CONSTANTS.PATH_SEP), commonAncestorPath = '', i; fromArray.shift(); toArray.shift(); for (i = 0; i < fromArray.length && i < toArray.length; i += 1) { if (fromArray[i] === toArray[i]) { commonAncestorPath += CONSTANTS.PATH_SEP + fromArray[i]; } else { break; } } while (self.getPath(node) !== commonAncestorPath && node !== null) { node = self.getParent(node); } return node; } function addRelation(parent, from, to, name) { var commonAncestor = getAncestor(parent, from, to), relFrom, relTo; if (commonAncestor) { relFrom = from.substr(self.getPath(commonAncestor).length); relTo = to.substr(self.getPath(commonAncestor).length); self.overlayInsert(commonAncestor, relFrom, name, relTo); } else { logger.error('unable to add relation: ' + name + '(' + from + '->' + to + ')'); } } function getFinalPath(path, closureInformation) { // #9ab4 1eaad 98572 de827 49f0d 54520 3ad99 6b564 7 => 41 char is the hash length var hash = path.substr(0, 41), resultPath = ''; if (closureInformation.relids[hash]) { resultPath = closureInformation.parent + CONSTANTS.PATH_SEP + closureInformation.relids[hash]; resultPath += path.substr(41); } return resultPath; } function computePaths(closureInformation) { var source, name, sourceInfo; for (source in closureInformation.relations.preserved) { sourceInfo = closureInformation.relations.preserved[source]; for (name in sourceInfo) { if (closureInformation.destinationBases[sourceInfo[name]]) { sourceInfo[name] = closureInformation.destinationBases[sourceInfo[name]]; } else { sourceInfo[name] = getFinalPath(sourceInfo[name], closureInformation); } } } for (source in closureInformation.relations.preserved) { closureInformation.relations.preserved[getFinalPath(source, closureInformation)] = closureInformation.relations.preserved[source]; delete closureInformation.relations.preserved[source]; } } //</editor-fold> //<editor-fold=Modified Methods> this.loadRoot = function (hash) { return TASYNC.call(function (root) { root.libraryRoots = getLibraryRootsInfo(root); return root; }, innerCore.loadRoot(hash)); }; this.createNode = function (parameters) { var node; if (parameters && parameters.parent && (self.isLibraryRoot(parameters.parent) || self.isLibraryElement(parameters.parent))) { throw new CoreIllegalOperationError('Not allowed to create new node inside library.'); } if (parameters && parameters.base && self.isLibraryRoot(parameters.base)) { throw new CoreIllegalOperationError('Not allowed to instantiate library root.'); } node = innerCore.createNode(parameters); if (node.parent === null) { node.libraryRoots = {}; } return node; }; this.deleteNode = function (node, technical) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to remove library node by simply deleting them.'); } return innerCore.deleteNode(node, technical); }; this.copyNode = function (node, parent) { if (self.isLibraryRoot(parent) || self.isLibraryElement(parent)) { throw new CoreIllegalOperationError('Not allowed to add nodes inside a library.'); } if (self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to copy library root.'); } return innerCore.copyNode(node, parent); }; this.copyNodes = function (nodes, parent) { var i; if (self.isLibraryRoot(parent) || self.isLibraryElement(parent)) { throw new CoreIllegalOperationError('Not allowed to add nodes inside a library.'); } for (i = 0; i < nodes.length; i += 1) { if (self.isLibraryRoot(nodes[i])) { throw new CoreIllegalOperationError('Not allowed to copy library root.'); } } return innerCore.copyNodes(nodes, parent); }; this.moveNode = function (node, parent) { if (self.isLibraryRoot(parent) || self.isLibraryElement(parent)) { throw new CoreIllegalOperationError('Not allowed to add nodes inside a library.'); } if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to move library elements.'); } return innerCore.moveNode(node, parent); }; this.setAttribute = function (node, name, value) { if (self.isLibraryElement(node) || self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setAttribute(node, name, value); }; this.delAttribute = function (node, name) { if (self.isLibraryElement(node) || self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delAttribute(node, name); }; this.setRegistry = function (node, name, value) { if (self.isLibraryElement(node) || self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setRegistry(node, name, value); }; this.delRegistry = function (node, name) { if (self.isLibraryElement(node) || self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delRegistry(node, name); }; this.setPointer = function (node, name, target) { if (self.isLibraryElement(node) || self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setPointer(node, name, target); }; this.deletePointer = function (node, name) { if (self.isLibraryElement(node) || self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.deletePointer(node, name); }; this.setBase = function (node, base) { if (self.isLibraryElement(node) || self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } if (base && self.isLibraryRoot(base)) { throw new CoreIllegalOperationError('Not allowed to instantiate library root.'); } return innerCore.setBase(node, base); }; this.addMember = function (node, name, member) { if (self.isLibraryElement(node) || self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.addMember(node, name, member); }; this.delMember = function (node, name, path) { if (self.isLibraryElement(node) || self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delMember(node, name, path); }; this.setMemberAttribute = function (node, setName, memberPath, attrName, value) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setMemberAttribute(node, setName, memberPath, attrName, value); }; this.delMemberAttribute = function (node, setName, memberPath, attrName) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delMemberAttribute(node, setName, memberPath, attrName); }; this.setMemberRegistry = function (node, setName, memberPath, regName, value) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setMemberRegistry(node, setName, memberPath, regName, value); }; this.delMemberRegistry = function (node, setName, memberPath, regName) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delMemberRegistry(node, setName, memberPath, regName); }; this.createSet = function (node, name) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.createSet(node, name); }; this.deleteSet = function (node, name) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.deleteSet(node, name); }; this.setSetAttribute = function (node, setName, regName, regValue) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setSetAttribute(node, setName, regName, regValue); }; this.delSetAttribute = function (node, setName, regName) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delSetAttribute(node, setName, regName); }; this.setSetRegistry = function (node, setName, regName, regValue) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setSetRegistry(node, setName, regName, regValue); }; this.delSetRegistry = function (node, setName, regName) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delSetRegistry(node, setName, regName); }; this.setGuid = function (node, guid) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { //FIXME cannot return any error in async functions :/ // /throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } else { return innerCore.setGuid(node, guid); } }; this.setConstraint = function (node, name, constraint) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setConstraint(node, name, constraint); }; this.delConstraint = function (node, name) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delConstraint(node, name); }; this.clearMetaRules = function (node) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.clearMetaRules(node); }; this.setAttributeMeta = function (node, name, rule) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setAttributeMeta(node, name, rule); }; this.delAttributeMeta = function (node, name) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delAttributeMeta(node, name); }; this.setChildMeta = function (node, child, min, max) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } if (self.isLibraryRoot(child)) { throw new CoreIllegalOperationError('Not allowed to use library root as valid child.'); } return innerCore.setChildMeta(node, child, min, max); }; this.delChildMeta = function (node, childPath) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delChildMeta(node, childPath); }; this.setChildrenMetaLimits = function (node, min, max) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setChildrenMetaLimits(node, min, max); }; this.setPointerMetaTarget = function (node, name, target, min, max) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setPointerMetaTarget(node, name, target, min, max); }; this.delPointerMetaTarget = function (node, name, targetPath) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delPointerMetaTarget(node, name, targetPath); }; this.setPointerMetaLimits = function (node, name, min, max) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setPointerMetaLimits(node, name, min, max); }; this.delPointerMeta = function (node, name) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delPointerMeta(node, name); }; this.setAspectMetaTarget = function (node, name, target) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.setAspectMetaTarget(node, name, target); }; this.delAspectMetaTarget = function (node, name, targetPath) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delAspectMetaTarget(node, name, targetPath); }; this.delAspectMeta = function (node, name) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delAspectMeta(node, name); }; this.delMixin = function (node, mixinPath) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.delMixin(node, mixinPath); }; this.addMixin = function (node, mixinPath) { var libraryName, root = self.getRoot(node); if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } for (libraryName in root.libraryRoots) { if (self.getPath(root.libraryRoots[libraryName]) === mixinPath) { throw new CoreIllegalOperationError('Not allowed to use library root as mixin.'); } } return innerCore.addMixin(node, mixinPath); }; this.clearMixins = function (node) { if (self.isLibraryRoot(node) || self.isLibraryElement(node)) { throw new CoreIllegalOperationError('Not allowed to modify library elements.'); } return innerCore.clearMixins(node); }; //</editor-fold> //<editor-fold=Added Methods> this.getLibraryRoot = function (node, name) { var root = self.getRoot(node); return root.libraryRoots[name] || null; }; this.isLibraryElement = function (node) { var parent = self.getParent(node); while (parent) { if (self.isLibraryRoot(parent)) { return true; } parent = self.getParent(parent); } return false; }; this.isLibraryRoot = function (node) { if (innerCore.getAttribute(node, '_libraryInfo')) { return true; } return false; }; this.getNamespace = function (node) { var libPrefix = getLibraryName(node); if (libPrefix) { // Trim the trailing dot.. libPrefix = libPrefix.substring(0, libPrefix.length - 1); } return libPrefix; }; this.getFullyQualifiedName = function (node) { ASSERT(self.isValidNode(node)); return getLibraryName(node) + self.getAttribute(node, 'name'); }; this.getLibraryGuid = function (node, name) { ASSERT(self.isValidNode(node)); var libraryRoot; if (!self.isLibraryElement(node) && !self.isLibraryRoot(node)) { throw new CoreIllegalOperationError('Node is not a library member'); } if (!name) { libraryRoot = getLibraryRoot(node); } else { libraryRoot = getRootOfLibrary(node, name); } if (!libraryRoot) { throw new CoreIllegalOperationError('Unknown library was given'); } if (self.getFullyQualifiedName(node).indexOf(self.getFullyQualifiedName(libraryRoot)) !== 0) { throw new CoreIllegalOperationError('Node is not a member of the library'); } if (self.isLibraryRoot(node) && self.getPath(node) === self.getPath(libraryRoot)) { return innerCore.getDataGuid(node); } return innerCore.getDeducedGuid(node, self.getLibraryGuid(self.getParent(node), name)); }; this.addLibrary = function (node, name, libraryRootHash, libraryInfo) { var root = self.getRoot(node), libraryRelid = RANDOM.generateRelid(root.data); innerCore.setProperty(root, libraryRelid, libraryRootHash); root.childrenRelids = null; return TASYNC.call(function (newLibraryRoot) { return TASYNC.call(function (libraryNodes) { var inMeta = self.getMemberPaths(newLibraryRoot, CONSTANTS.META_SET_NAME), libraryInfoAttribute = libraryInfo; //remove the libraryRoot from the libraryNodes libraryNodes.shift(); //set the name of the library root innerCore.setAttribute(newLibraryRoot, 'name', name); //add library_info libraryInfoAttribute.hash = libraryRootHash; innerCore.setAttribute(newLibraryRoot, '_libraryInfo', libraryInfoAttribute); if (libraryNodes.length > 0) { //connect the FCO as base of libraryFCO innerCore.setBase(self.getBaseRoot(libraryNodes[0]), self.getFCO(root)); //adding nodes to the global META var i; for (i = 0; i < libraryNodes.length; i += 1) { if (inMeta.indexOf(self.getPath(libraryNodes[i])) !== -1) { innerCore.addMember(root, CONSTANTS.META_SET_NAME, libraryNodes[i]); } } } //refreshing libraryInfo root.libraryRoots[name] = newLibraryRoot; }, self.loadSubTree(newLibraryRoot)); }, self.loadChild(root, libraryRelid)); }; this.updateLibrary = function (node, name, updatedLibraryRootHash, libraryInfo/*, updateInstructions*/) { var logs = {added: {}, updated: {}, moved: {}, removed: {}}, root = self.getRoot(node), libraryRoot = getRootOfLibrary(root, name), relid, FCO = self.getFCO(root); if (!libraryRoot) { //do nothing if not valid library return logs; } relid = self.getRelid(libraryRoot); return TASYNC.call(function (oldInfo, newInfo) { var newNodePaths = [], removedNodePaths = [], removedNodeInMeta = [], addedToMetaPaths = [], removedFromMetaPaths = [], i, moves = [], guid; for (guid in newInfo) { if (!oldInfo[guid]) { newNodePaths.push('/' + relid + newInfo[guid].path); } else if (oldInfo[guid].path !== newInfo[guid].path) { moves.push({from: '/' + relid + oldInfo[guid].path, to: '/' + relid + newInfo[guid].path}); } } for (guid in oldInfo) { if (!newInfo[guid]) { removedNodePaths.push('/' + relid + oldInfo[guid].path); removedNodeInMeta.push(oldInfo[guid].isMeta); } } for (i = 0; i < removedNodePaths.length; i += 1) { removeLibraryRelations(root, removedNodePaths[i]); } for (i = 0; i < moves.length; i += 1) { moveLibraryRelations(root, moves[i].from, moves[i].to); } //finally address the changes in the meta element of the library for (guid in newInfo) { if (oldInfo[guid]) { if (newInfo[guid].isMeta && !oldInfo[guid].isMeta) { addedToMetaPaths.push('/' + relid + newInfo[guid].path); } if (!newInfo[guid].isMeta && oldInfo[guid].isMeta) { removedFromMetaPaths.push('/' + relid + newInfo[guid].path); } } } innerCore.setProperty(root, relid, updatedLibraryRootHash); root = self.removeChildFromCache(root, relid); return TASYNC.call(function (newLibraryRoot) { return TASYNC.call(function (newLibraryNodes) { var i, inMeta = self.getMemberPaths(newLibraryRoot, CONSTANTS.META_SET_NAME), libraryInfoAttribute = libraryInfo, libraryFCO; newLibraryNodes.shift(); //set the name of the library root innerCore.setAttribute(newLibraryRoot, 'name', name); //add library_info libraryInfoAttribute.hash = updatedLibraryRootHash; innerCore.setAttribute(newLibraryRoot, '_libraryInfo', libraryInfoAttribute); if (newLibraryNodes.length > 0) { //connect the FCO as base of libraryFCO, but be sure to remove the nullPtr libraryFCO = self.getBaseRoot(newLibraryNodes[0]); innerCore.deletePointer(libraryFCO, 'base'); innerCore.setBase(libraryFCO, FCO); for (i = 0; i < newLibraryNodes.length; i += 1) { //adding new nodes to the global META if (newNodePaths.indexOf(self.getPath(newLibraryNodes[i])) !== -1 && inMeta.indexOf(self.getPath(newLibraryNodes[i])) !== -1) { innerCore.addMember(root, CONSTANTS.META_SET_NAME, newLibraryNodes[i]); } //adding existing nodes to the global META if (addedToMetaPaths.indexOf(self.getPath(newLibraryNodes[i])) !== -1) { innerCore.addMember(root, CONSTANTS.META_SET_NAME, newLibraryNodes[i]); } //removing existing nodes from the global META if (removedFromMetaPaths.indexOf(self.getPath(newLibraryNodes[i])) !== -1) { innerCore.delMember( root, CONSTANTS.META_SET_NAME, self.getPath(newLibraryNodes[i]) ); let sets = self.isMemberOf(newLibraryNodes[i]); sets = sets[''] || []; sets.forEach((set) => { if (set.indexOf(CONSTANTS.META_SET_NAME) === 0) { innerCore.addMember( root, CONSTANTS.META_SET_NAME, newLibraryNodes[i] ); } }); } } } //for bookkeping purposes we need to remove the to-delete nodes from the all-META set if (removedNodePaths.length > 0) { for (i = 0; i < removedNodePaths.length; i += 1) { if (removedNodeInMeta[i]) { innerCore.delMember( root, CONSTANTS.META_SET_NAME, removedNodePaths[i] ); } } } root.libraryRoots[name] = newLibraryRoot; return logs; }, self.loadSubTree(newLibraryRoot)); }, self.loadChild(root, relid)); }, getLibraryInfo(libraryRoot), getLibraryInfo(updatedLibraryRootHash)); }; this.removeLibrary = function (node, name) { ASSERT(self.isValidNode(node)); var root = self.getRoot(node), libraryRoot = root.libraryRoots[name]; if (libraryRoot && !self.isLibraryElement(libraryRoot)) { innerCore.deleteNode(root.libraryRoots[name], true); delete root.libraryRoots[name]; } }; this.renameLibrary = function (node, oldName, newName) { ASSERT(self.isValidNode(node)); var root = self.getRoot(node); ASSERT(typeof oldName === 'string' && typeof newName === 'string' && oldName.indexOf(CONSTANTS.NAMESPACE_SEPARATOR) === -1 && newName.indexOf(CONSTANTS.NAMESPACE_SEPARATOR) === -1 && root.libraryRoots[oldName]); if (oldName !== newName) { ASSERT(!root.libraryRoots[newName], 'Library already exists [' + newName + ']'); innerCore.setAttribute(root.libraryRoots[oldName], 'name', newName); root.libraryRoots[newName] = root.libraryRoots[oldName]; delete root.libraryRoots[oldName]; } }; this.getLibraryNames = function (node) { ASSERT(self.isValidNode(node)); return Object.keys(self.getRoot(node).libraryRoots); }; this.getLibraryMetaNodes = function (node, name, onlyOwn) { var allNodes = self.getAllMetaNodes(node), libraryNodes = {}, path; for (path in allNodes) { try { if (onlyOwn) { if (self.getNamespace(allNodes[path]) === name) { libraryNodes[path] = allNodes[path]; } } else { if (self.getNamespace(allNodes[path]).indexOf(name) === 0) { libraryNodes[path] = allNodes[path]; } } } catch (e) { // There is the occasional occurance that the list contains // already removed nodes, so they will throw exceptions... } } return libraryNodes; }; this.getLibraryInfo = function (node, name) { var libroot = getRootOfLibrary(node, name); return self.getAttribute(libroot, '_libraryInfo'); }; this.getClosureInformation = function (nodes) { ASSERT(nodes.length > 0); var closureInfo = { hashes: {}, selection: {}, bases: {}, relations: {preserved: {}, lost: {}} }, // infoLosses = {}, allMetaNodes, path, node, keys, i; nodes = normalizeSelectionForClosure(nodes); allMetaNodes = this.getAllMetaNodes(nodes[0]); // We first collect the absolute paths of the selected nodes for (i = 0; i < nodes.length; i += 1) { // The selection cannot contain library elements as that would violate read-only if (this.isLibraryElement(nodes[i]) || this.isLibraryRoot(nodes[i])) { throw new CoreIllegalOperationError('Cannot select node[' + this.getPath(nodes[i]) + '] because it is library content!' ); } if (this.getParent(nodes[i]) === null) { throw new CoreIllegalOperationError('Cannot select the project root!'); } closureInfo.selection[this.getPath(nodes[i])] = this.getGuid(nodes[i]); closureInfo.hashes[this.getPath(nodes[i])] = this.getHash(nodes[i]); } // Secondly, we collect relation information (the first order ones). // We leave the handling of the root node's overlay info for a separate step for (i = 0; i < nodes.length; i += 1) { node = this.getParent(nodes[i]); while (this.getPath(node)) { // until it is not the root addRelationsFromNodeToClosureInfo(node, allMetaNodes, closureInfo); node = this.getParent(node); } } // Finally we process the