UNPKG

webgme-engine

Version:

WebGME server and Client API without a GUI

1,027 lines (849 loc) 38.6 kB
/*globals define*/ /*eslint-env node, browser*/ /** * @author kecso / https://github.com/kecso */ define([ 'common/core/CoreAssert', 'common/core/tasync', 'common/util/canon', 'common/core/constants' ], function (ASSERT, TASYNC, CANON, CONSTANTS) { 'use strict'; var MixinCore = function (innerCore, options) { ASSERT(typeof options === 'object'); ASSERT(typeof options.globConf === 'object'); ASSERT(typeof options.logger !== 'undefined'); var logger = options.logger, key, self = this; for (key in innerCore) { this[key] = innerCore[key]; } logger.debug('initialized MixinCore'); //<editor-fold=Helper Functions> function realNode(node) { //TODO we have to make some more sophisticated distinction if (self.getPath(node).indexOf('_') !== -1) { return false; } return true; } function getOrderedMixinList(node, cache) { var path = innerCore.getPath(node), paths, metaNodes, helper, orderedList, guid, i; if (cache && cache.orderedMixinList && cache.orderedMixinList[path]) { return cache.orderedMixinList[path]; } paths = innerCore.getOwnMemberPaths(node, CONSTANTS.MIXINS_SET); helper = {}; orderedList = []; if (paths.length > 0) { metaNodes = self.getAllMetaNodes(node); for (i = 0; i < paths.length; i += 1) { if (metaNodes[paths[i]]) { guid = self.getGuid(metaNodes[paths[i]]); helper[guid] = paths[i]; orderedList.push(guid); } } orderedList.sort(); for (i = 0; i < orderedList.length; i += 1) { orderedList[i] = metaNodes[helper[orderedList[i]]]; } } if (cache && cache.orderedMixinList) { cache.orderedMixinList[path] = orderedList; } return orderedList; } function getNodeDictionary(node, paths) { var allMetaNodes = self.getAllMetaNodes(node), dictionary = {}, i; for (i = 0; i < paths.length; i += 1) { if (allMetaNodes[paths[i]]) { dictionary[paths[i]] = allMetaNodes[paths[i]]; } } return dictionary; } function extendUniqueArray(base, extension) { var i; for (i = 0; i < extension.length; i += 1) { if (base.indexOf(extension[i]) === -1) { base.push(extension[i]); } } } function getExtendedUniqueKeyedObject(base, extension) { var extended = {}, key; for (key in base) { extended[key] = base[key]; } for (key in extension) { if (!base[key]) { extended[key] = extension[key]; } } return extended; } function getValidNames(node, getter, alreadyVisited) { var base = self.getBase(node), path = self.getPath(node), names, mixins = getOrderedMixinList(node), i; if (alreadyVisited[path]) { return []; } if (base) { names = getValidNames(base, getter, alreadyVisited); } else { names = []; } extendUniqueArray(names, getter(node)); alreadyVisited[path] = true; for (i = 0; i < mixins.length; i += 1) { extendUniqueArray(names, getValidNames(mixins[i], getter, alreadyVisited)); } return names; } function getFirstMatchingRuleHolder(node, name, getter, alreadyVisited) { var base = self.getBase(node), path = self.getPath(node), mixins = getOrderedMixinList(node), ruleHolder = null, i; if (alreadyVisited[path]) { return null; } //when it comes to rule holder, always the given node's own rule-set is the first alreadyVisited[path] = true; if (getter(node).indexOf(name) !== -1) { return node; } if (base) { ruleHolder = getFirstMatchingRuleHolder(base, name, getter, alreadyVisited); if (ruleHolder) { return ruleHolder; } } for (i = 0; i < mixins.length; i += 1) { ruleHolder = getFirstMatchingRuleHolder(mixins[i], name, getter, alreadyVisited); if (ruleHolder) { return ruleHolder; } } return null; } function getOwnRuleInfoTarget(target, validTargetPaths, alreadyVisited) { var orderedMixins = getOrderedMixinList(target), path = self.getPath(target), base = self.getBase(target), result, i; if (alreadyVisited[path]) { return null; } alreadyVisited[path] = true; if (validTargetPaths.indexOf(path) !== -1) { return target; } if (base) { result = getOwnRuleInfoTarget(base, validTargetPaths, alreadyVisited); if (result) { return result; } } for (i = 0; i < orderedMixins.length; i += 1) { result = getOwnRuleInfoTarget(orderedMixins[i], validTargetPaths, alreadyVisited); if (result) { return result; } } return null; } function getOwnRuleInfo(node, name, target, getter) { var definedTarget = getOwnRuleInfoTarget(target, getter(node, name), {}); if (definedTarget) { return { ownerNode: node, ownerPath: self.getPath(node), targetPath: self.getPath(definedTarget), targetNode: definedTarget }; } return null; } function getFirstMatchingRuleInfo(node, name, target, getter, alreadyVisited) { var base = self.getBase(node), path = self.getPath(node), mixins = getOrderedMixinList(node), info, i; if (alreadyVisited[path]) { return null; } //when it comes to rule holder, always the given node's own rule-set is the first alreadyVisited[path] = true; info = getOwnRuleInfo(node, name, target, getter); if (info) { return info; } if (base) { info = getFirstMatchingRuleInfo(base, name, target, getter, alreadyVisited); if (info) { return info; } } for (i = 0; i < mixins.length; i += 1) { info = getFirstMatchingRuleInfo(mixins[i], name, target, getter, alreadyVisited); if (info) { return info; } } return null; } function getAllMatchingRuleHolders(node, name, getter, alreadyVisited) { var base = self.getBase(node), path = self.getPath(node), mixins = getOrderedMixinList(node), ruleHolders = [], i; if (alreadyVisited[path]) { return []; } //when it comes to rule holder, always the given node's own rule-set is the first alreadyVisited[path] = true; if (getter(node).indexOf(name) !== -1) { ruleHolders.push(node); } if (base) { ruleHolders = ruleHolders.concat(getAllMatchingRuleHolders(base, name, getter, alreadyVisited)); } for (i = 0; i < mixins.length; i += 1) { ruleHolders = ruleHolders.concat(getAllMatchingRuleHolders(mixins[i], name, getter, alreadyVisited)); } return ruleHolders; } function getFirstMatchingMeta(node, name, matchFuction, getFunction) { var metaRuleHolder = getFirstMatchingRuleHolder(node, name, matchFuction, {}); if (metaRuleHolder) { return getFunction(metaRuleHolder, name); } return undefined; } function allValidRelationsNameGetter(node) { return innerCore.getOwnValidPointerNames(node).concat(innerCore.getOwnValidSetNames(node)); } function containmentGetter(/*node*/) { return ['containment']; } function isTypeOf(node, typePath, alreadyVisited, cache) { var base, mixins, i, path = self.getPath(node); if (alreadyVisited[path]) { return false; } alreadyVisited[path] = true; if (path === typePath) { return true; } base = self.getBase(node); if (base && isTypeOf(base, typePath, alreadyVisited, cache)) { return true; } mixins = getOrderedMixinList(node, cache); for (i = 0; i < mixins.length; i += 1) { if (isTypeOf(mixins[i], typePath, alreadyVisited, cache)) { return true; } } return false; } function convertRuleToItemizedArraySet(rule) { var items = [], minItems = [], maxItems = [], i; for (i in rule) { if (i !== 'min' && i !== 'max') { items.push(i); minItems.push(rule[i].min || -1); maxItems.push(rule[i].max || -1); delete rule[i]; } } rule.items = items; rule.minItems = minItems; rule.maxItems = maxItems; } //</editor-fold> //<editor-fold=Modified Methods> this.isTypeOf = function (node, typeNodeOrPath, cache) { if (!realNode(node)) { return false; } cache = cache || {}; cache.orderedMixinList = cache.orderedMixinList || {}; return isTypeOf(node, typeof typeNodeOrPath === 'string' ? typeNodeOrPath : innerCore.getPath(typeNodeOrPath), {}, cache); }; this.isValidChildOf = function (node, parentNode, cache) { if (!realNode(node)) { return true; } var childrenPaths, metaNodes, i; cache = cache || cache; // TODO: this is only needed when the containment definition is outside of meta.. // TODO: (That is when it's defined between non-meta nodes) if (innerCore.isValidChildOf(node, parentNode)) { return true; } // Now we have to look deeper as containment rule may come from a mixin childrenPaths = self.getValidChildrenPaths(parentNode, cache); metaNodes = self.getAllMetaNodes(node); for (i = 0; i < childrenPaths.length; i += 1) { if (metaNodes[childrenPaths[i]] && self.isTypeOf(node, childrenPaths[i], cache)) { return true; } } return false; }; this.isValidTargetOf = function (node, source, name) { if (!realNode(source) || node === null) { return true; } var targetPaths, metaNodes, i; // TODO: this is only needed when the set/pointer definition is outside of meta.. // TODO: (That is when it's defined between non-meta nodes) if (innerCore.isValidTargetOf(node, source, name)) { return true; } // Now we have to look deeper as pointer rule may come from a mixin targetPaths = self.getValidTargetPaths(source, name); metaNodes = self.getAllMetaNodes(node); for (i = 0; i < targetPaths.length; i += 1) { if (metaNodes[targetPaths[i]] && self.isTypeOf(node, metaNodes[targetPaths[i]])) { return true; } } return false; }; this.isValidAttributeValueOf = function (node, name, value) { if (!realNode(node)) { return true; } var ruleHolder = getFirstMatchingRuleHolder(node, name, innerCore.getOwnValidAttributeNames, {}), attributeDescriptor = self.getAttributeMeta(ruleHolder, name); //issue #51 - readonly should be taken into account if (attributeDescriptor.readonly && self.isMetaNode(node) === false) { return false; } if (ruleHolder) { return innerCore.isValidAttributeValueOf(ruleHolder, name, value); } return false; }; this.getValidPointerNames = function (node) { //console.count('getValidPointerNames'); return getValidNames(node, innerCore.getOwnValidPointerNames, {}); }; this.getValidSetNames = function (node) { //console.count('getValidSetNames'); return getValidNames(node, innerCore.getOwnValidSetNames, {}); }; this.getValidAttributeNames = function (node) { if (!realNode(node)) { return []; } return getValidNames(node, innerCore.getOwnValidAttributeNames, {}); }; this.getValidAspectNames = function (node) { //console.count('getValidAspectNames'); return getValidNames(node, innerCore.getOwnValidAspectNames, {}); }; this.getConstraintNames = function (node) { return getValidNames(node, innerCore.getConstraintNames, {}); }; this.getConstraint = function (node, name) { var constraintOwner = getFirstMatchingRuleHolder(node, name, innerCore.getOwnConstraintNames, {}); if (constraintOwner) { return innerCore.getConstraint(constraintOwner, name); } else { return null; } }; this.getJsonMeta = function (node) { var meta = {children: {}, attributes: {}, pointers: {}, aspects: {}, constraints: {}}, nullRule = {items: [], minItems: [], maxItems: []}, names, i; meta.children = self.getChildrenMeta(node); if (meta.children) { convertRuleToItemizedArraySet(meta.children); } else { meta.children = nullRule; } //attributes names = self.getValidAttributeNames(node); for (i = 0; i < names.length; i += 1) { meta.attributes[names[i]] = self.copyIfObject(self.getAttributeMeta(node, names[i])); } //pointers names = self.getValidPointerNames(node); for (i = 0; i < names.length; i += 1) { meta.pointers[names[i]] = self.getPointerMeta(node, names[i]); convertRuleToItemizedArraySet(meta.pointers[names[i]]); } //sets names = self.getValidSetNames(node); for (i = 0; i < names.length; i += 1) { meta.pointers[names[i]] = self.getPointerMeta(node, names[i]); convertRuleToItemizedArraySet(meta.pointers[names[i]]); } //aspects names = self.getValidAspectNames(node); for (i = 0; i < names.length; i += 1) { meta.aspects[names[i]] = self.getAspectMeta(node, names[i]); } //constraints names = self.getConstraintNames(node); for (i = 0; i < names.length; i += 1) { meta.constraints[names[i]] = self.getConstraint(node, names[i]); } return meta; }; this.getOwnJsonMeta = function (node) { // We have to extend the meta definition by the mixins, but only if we talk about the own rule-set. var jsonMeta = innerCore.getOwnJsonMeta(node), mixins = this.getMixinPaths(node); if (mixins.length > 0) { jsonMeta.mixins = mixins; } return jsonMeta; }; this.getValidChildrenPaths = function (node, cache) { cache = cache || {}; cache.validChildrenPaths = cache.validChildrenPaths || {}; function wrapper(testNode) { var path = innerCore.getPath(testNode); if (!cache.validChildrenPaths[path]) { // getOwnValidChildrenPaths is slower cache.validChildrenPaths[path] = innerCore.getValidChildrenPaths(testNode); } return cache.validChildrenPaths[path]; } return getValidNames(node, wrapper, {}); }; this.getChildrenMeta = function (node) { var ruleHolders = getAllMatchingRuleHolders(node, 'containment', containmentGetter, {}), childrenMeta = {}, i; for (i = 0; i < ruleHolders.length; i += 1) { childrenMeta = getExtendedUniqueKeyedObject(childrenMeta, innerCore.getChildrenMeta(ruleHolders[i])); } if (Object.keys(childrenMeta).length === 0) { return null; } return childrenMeta; }; this.getPointerMeta = function (node, name) { var ruleHolders = getAllMatchingRuleHolders(node, name, allValidRelationsNameGetter, {}), i, pointerMeta = {}; if (ruleHolders.length === 0) { return undefined; } for (i = 0; i < ruleHolders.length; i += 1) { pointerMeta = getExtendedUniqueKeyedObject(pointerMeta, innerCore.getPointerMeta(ruleHolders[i], name)); } return pointerMeta; }; this.getValidTargetPaths = function (node, name) { var getTargetPaths = function (getNode) { return innerCore.getValidTargetPaths(getNode, name); }; return getValidNames(node, getTargetPaths, {}); }; this.getAttributeMeta = function (node, name) { return getFirstMatchingMeta(node, name, innerCore.getOwnValidAttributeNames, innerCore.getAttributeMeta); }; this.getAspectMeta = function (node, name) { return getFirstMatchingMeta(node, name, innerCore.getOwnValidAspectNames, innerCore.getAspectMeta); }; this.isValidAspectMemberOf = function (node, parent, name) { var aspectMeta = self.getAspectMeta(parent, name) || [], i; for (i = 0; i < aspectMeta.length; i += 1) { if (self.isTypeOf(node, aspectMeta[i])) { return true; } } return false; }; this.getSetNames = function (node) { var rawNames = innerCore.getSetNames(node), index = rawNames.indexOf(CONSTANTS.MIXINS_SET); if (index !== -1) { rawNames.splice(index, 1); } return rawNames; }; this.getAttributeDefinitionOwner = function (node, name) { return getFirstMatchingRuleHolder(node, name, self.getOwnValidAttributeNames, {}); }; this.getAspectDefinitionOwner = function (node, name) { return getFirstMatchingRuleHolder(node, name, self.getOwnValidAspectNames, {}); }; this.getAspectDefinitionInfo = function (node, name, member) { return getFirstMatchingRuleInfo(node, name, member, self.getOwnValidAspectTargetPaths, {}); }; this.getPointerDefinitionInfo = function (node, name, target) { return getFirstMatchingRuleInfo(node, name, target, self.getOwnValidTargetPaths, {}); }; this.getSetDefinitionInfo = function (node, name, target) { return getFirstMatchingRuleInfo(node, name, target, self.getOwnValidTargetPaths, {}); }; this.getChildDefinitionInfo = function (node, child) { return getFirstMatchingRuleInfo(node, undefined, child, self.getOwnValidChildrenPaths, {}); }; //</editor-fold> //<editor-fold=Added Methods> this.getMixinErrors = function (node) { var errors = [], mixinPaths = self.getMixinPaths(node), allMetaNodes = self.getAllMetaNodes(node), mixinNodes = getOrderedMixinList(node), targetNode, targetInfoTxt, definitions, ownName = self.getAttribute(node, 'name'), names, base = self.getBase(node), ownKeys, keys, name, path, i, j, k; logger.debug('getMixinErrors(' + ownName + ')'); //mixin is missing from meta for (i = 0; i < mixinPaths.length; i += 1) { if (!allMetaNodes[mixinPaths[i]]) { logger.error('mixin node is missing from Meta [' + mixinPaths[i] + ']'); errors.push({ severity: 'error', nodeName: ownName, type: CONSTANTS.MIXIN_ERROR_TYPE.MISSING, targetInfo: mixinPaths[i], message: '[' + ownName + ']: mixin node \'' + mixinPaths[i] + '\' is missing from the Meta', hint: 'Remove mixin or add to the Meta' }); } } //attribute definition collisions definitions = {}; ownKeys = []; if (base) { ownKeys = self.getValidAttributeNames(base); } extendUniqueArray(ownKeys, self.getOwnValidAttributeNames(node)); for (i = 0; i < mixinNodes.length; i += 1) { name = self.getAttribute(mixinNodes[i], 'name'); path = self.getPath(mixinNodes[i]); keys = self.getValidAttributeNames(mixinNodes[i]); for (j = 0; j < keys.length; j += 1) { if (ownKeys.indexOf(keys[j]) === -1) { if (definitions[keys[j]]) { logger.warn('colliding attribute (' + keys[j] + ') definition [' + definitions[keys[j]].name + ']vs[' + name + ']'); errors.push({ severity: 'warning', type: CONSTANTS.MIXIN_ERROR_TYPE.ATTRIBUTE_COLLISION, nodeName: ownName, ruleName: keys[j], collisionPaths: [definitions[keys[j]].path, path], collisionNodes: [mixinNodes[definitions[keys[j]].index], mixinNodes[i]], message: '[' + ownName + ']: inherits attribute definition \'' + keys[j] + '\' from [' + definitions[keys[j]].name + '] and [' + name + ']', hint: 'Remove one of the mixin relations' }); } else { definitions[keys[j]] = {name: name, path: path, index: i}; } } } } //containment collisions definitions = {}; ownKeys = []; if (base) { ownKeys = self.getValidChildrenPaths(base); } extendUniqueArray(ownKeys, self.getOwnValidChildrenPaths(node)); for (i = 0; i < mixinNodes.length; i += 1) { name = self.getAttribute(mixinNodes[i], 'name'); keys = self.getValidChildrenPaths(mixinNodes[i]); path = self.getPath(mixinNodes[i]); for (j = 0; j < keys.length; j += 1) { if (ownKeys.indexOf(keys[j]) === -1) { if (definitions[keys[j]]) { targetNode = allMetaNodes[keys[j]]; if (targetNode) { targetInfoTxt = '[' + self.getAttribute(targetNode, 'name') + ']'; } else { targetInfoTxt = '\'' + keys[j] + '\''; } logger.warn('colliding child (' + keys[j] + ') definition [' + definitions[keys[j]].name + ']vs[' + name + ']'); errors.push({ severity: 'warning', type: CONSTANTS.MIXIN_ERROR_TYPE.CONTAINMENT_COLLISION, nodeName: ownName, targetInfo: keys[j], targetNode: targetNode, collisionPaths: [definitions[keys[j]].path, path], collisionNodes: [mixinNodes[definitions[keys[j]].index], mixinNodes[i]], message: '[' + ownName + ']: inherits child definition for ' + targetInfoTxt + ' from [' + definitions[keys[j]].name + '] and [' + name + ']', hint: 'Remove one of the mixin relations' }); } else { definitions[keys[j]] = {name: name, path: path, index: i}; } } } } //pointer target collisions names = this.getValidPointerNames(node); for (i = 0; i < names.length; i += 1) { definitions = {}; ownKeys = []; if (base) { ownKeys = self.getValidTargetPaths(base, names[i]); } extendUniqueArray(ownKeys, self.getOwnValidTargetPaths(node, names[i])); for (j = 0; j < mixinNodes.length; j += 1) { keys = self.getValidTargetPaths(mixinNodes[j], names[i]); name = self.getAttribute(mixinNodes[j], 'name'); path = self.getPath(mixinNodes[j]); for (k = 0; k < keys.length; k += 1) { if (ownKeys.indexOf(keys[k]) === -1 && keys[k] !== 'min' && keys[k] !== 'max') { if (definitions[keys[k]]) { targetNode = allMetaNodes[keys[k]]; if (targetNode) { targetInfoTxt = '[' + self.getAttribute(targetNode, 'name') + ']'; } else { targetInfoTxt = '\'' + keys[k] + '\''; } logger.warn('colliding pointer (' + names[i] + ') target (' + keys[k] + ') definition [' + definitions[keys[k]].name + ']vs[' + name + ']'); errors.push({ severity: 'warning', type: CONSTANTS.MIXIN_ERROR_TYPE.POINTER_COLLISION, nodeName: ownName, ruleName: names[i], targetInfo: keys[k], targetNode: targetNode, collisionPaths: [definitions[keys[k]].path, path], collisionNodes: [mixinNodes[definitions[keys[k]].index], mixinNodes[j]], message: '[' + ownName + ']: inherits pointer (' + names[i] + ') target definition of ' + targetInfoTxt + ' from [' + definitions[keys[k]].name + '] and [' + name + ']', hint: 'Remove one of the mixin relations' }); } else { definitions[keys[k]] = {name: name, path: path, index: i}; } } } } } //set member collisions names = this.getValidSetNames(node); for (i = 0; i < names.length; i += 1) { definitions = {}; ownKeys = []; if (base) { ownKeys = self.getValidTargetPaths(base, names[i]); } extendUniqueArray(ownKeys, self.getOwnValidTargetPaths(node, names[i])); for (j = 0; j < mixinNodes.length; j += 1) { keys = self.getValidTargetPaths(mixinNodes[j], names[i]); name = self.getAttribute(mixinNodes[j], 'name'); path = self.getPath(mixinNodes[j]); for (k = 0; k < keys.length; k += 1) { if (ownKeys.indexOf(keys[k]) === -1 && keys[k] !== 'min' && keys[k] !== 'max') { if (definitions[keys[k]]) { targetNode = allMetaNodes[keys[k]]; if (targetNode) { targetInfoTxt = '[' + self.getAttribute(targetNode, 'name') + ']'; } else { targetInfoTxt = '\'' + keys[k] + '\''; } logger.warn('colliding set (' + names[i] + ') member (' + keys[k] + ') definition [' + definitions[keys[k]].name + ']vs[' + name + ']'); errors.push({ severity: 'warning', type: CONSTANTS.MIXIN_ERROR_TYPE.SET_COLLISION, nodeName: ownName, ruleName: names[i], targetInfo: keys[k], targetNode: targetNode, collisionPaths: [definitions[keys[k]].path, path], collisionNodes: [mixinNodes[definitions[keys[k]].index], mixinNodes[j]], message: '[' + ownName + ']: inherits set (' + names[i] + ') member definition of ' + targetInfoTxt + ' from [' + definitions[keys[k]].name + '] and [' + name + ']', hint: 'Remove one of the mixin relations' }); } else { definitions[keys[k]] = {name: name, path: path, index: i}; } } } } } //aspect collisions definitions = {}; ownKeys = []; if (base) { ownKeys = self.getValidAspectNames(base); } extendUniqueArray(ownKeys, self.getOwnValidAspectNames(node)); for (i = 0; i < mixinNodes.length; i += 1) { keys = self.getValidAspectNames(mixinNodes[i]); name = self.getAttribute(mixinNodes[i], 'name'); path = self.getPath(mixinNodes[i]); for (j = 0; j < keys.length; j += 1) { if (ownKeys.indexOf(keys[j]) === -1) { if (definitions[keys[j]]) { logger.warn('colliding aspect (' + keys[j] + ') definition [' + definitions[keys[j]].name + ']vs[' + name + ']'); errors.push({ severity: 'warning', type: CONSTANTS.MIXIN_ERROR_TYPE.ASPECT_COLLISION, nodeName: ownName, ruleName: keys[j], collisionPaths: [definitions[keys[j]].path, path], collisionNodes: [mixinNodes[definitions[keys[j]].index], mixinNodes[i]], message: '[' + ownName + ']: inherits aspect definition \'' + keys[j] + '\' from [' + definitions[keys[j]].name + '] and [' + name + ']', hint: 'Remove one of the mixin relations' }); } else { definitions[keys[j]] = {name: name, path: path, index: i}; } } } } //constraint collision definitions = {}; ownKeys = []; if (base) { ownKeys = self.getConstraintNames(base); } extendUniqueArray(ownKeys, self.getOwnConstraintNames(node)); for (i = 0; i < mixinNodes.length; i += 1) { keys = self.getConstraintNames(mixinNodes[i]); name = self.getAttribute(mixinNodes[i], 'name'); for (j = 0; j < keys.length; j += 1) { if (ownKeys.indexOf(keys[j]) === -1) { if (definitions[keys[j]]) { logger.warn('colliding constraint (' + keys[j] + ') definition [' + definitions[keys[j]].name + ']vs[' + name + ']'); errors.push({ severity: 'warning', type: CONSTANTS.MIXIN_ERROR_TYPE.CONSTRAINT_COLLISION, nodeName: ownName, ruleName: keys[j], collisionPaths: [definitions[keys[j]].path, path], collisionNodes: [mixinNodes[definitions[keys[j]].index], mixinNodes[i]], message: '[' + ownName + ']: inherits constraint definition \'' + keys[j] + '\' from [' + definitions[keys[j]].name + '] and [' + name + ']', hint: 'Remove one of the mixin relations' }); } else { definitions[keys[j]] = {name: name, path: path, index: i}; } } } } logger.debug('getMixinErrors(' + ownName + ') finished'); return errors; }; this.getMixinPaths = function (node) { var paths = [], nodes = getOrderedMixinList(node), raw = innerCore.getOwnMemberPaths(node, CONSTANTS.MIXINS_SET), i; for (i = 0; i < nodes.length; i += 1) { paths.push(self.getPath(nodes[i])); } for (i = 0; i < raw.length; i += 1) { if (paths.indexOf(raw[i]) === -1) { paths.push(raw[i]); } } return paths; }; this.getMixinNodes = function (node) { var paths = self.getMixinPaths(node); return getNodeDictionary(node, paths); }; this.delMixin = function (node, mixinPath) { // var metaNodes = innerCore.getAllMetaNodes(node); innerCore.delMember(node, CONSTANTS.MIXINS_SET, mixinPath); }; this.addMixin = function (node, mixinPath) { var metaNodes = innerCore.getAllMetaNodes(node); if (metaNodes[mixinPath]) { innerCore.addMember(node, CONSTANTS.MIXINS_SET, metaNodes[mixinPath]); } }; this.clearMixins = function (node) { self.deleteSet(node, CONSTANTS.MIXINS_SET); }; this.getBaseTypes = function (node) { var metaType = innerCore.getBaseType(node), metaTypes = []; if (metaType) { metaTypes = getOrderedMixinList(metaType); metaTypes.unshift(metaType); } return metaTypes; }; this.canSetAsMixin = function (node, mixinPath) { var result = { isOk: true, reason: '' }, mixinNode = self.getAllMetaNodes(node)[mixinPath]; if (self.getPath(node) === mixinPath) { result.isOk = false; result.reason = 'Node cannot be mixin of itself!'; } else if (!mixinNode) { result.isOk = false; result.reason = 'Mixin must be on the Meta!'; } else if (innerCore.isTypeOf(node, mixinNode)) { result.isOk = false; result.reason = 'Base of node cannot be its mixin as well!'; } return result; }; //</editor-fold> }; return MixinCore; });