webgme-bip
Version:
Design studio for component-based modeling with BIP.
512 lines (476 loc) • 21.5 kB
JavaScript
/*globals define*/
/*jshint node:true, browser:true*/
/**
* Generated by PluginGenerator 1.7.0 from webgme on Sun Feb 19 2017 20:48:34 GMT-0600 (CST).
* A plugin that inherits from the PluginBase. To see source code documentation about available
* properties and methods visit %host%/docs/source/PluginBase.html.
*/
define([
'plugin/PluginConfig',
'text!./metadata.json',
'plugin/PluginBase',
'common/util/xmljsonconverter'
], function (PluginConfig,
pluginMetadata,
PluginBase,
Converter) {
'use strict';
pluginMetadata = JSON.parse(pluginMetadata);
/**
* Initializes a new instance of ArchitectureSpecGenerator.
* @class
* @augments {PluginBase}
* @classdesc This class represents the plugin ArchitectureSpecGenerator.
* @constructor
*/
var ArchitectureSpecGenerator = function () {
// Call base class' constructor.
PluginBase.call(this);
this.pluginMetadata = pluginMetadata;
};
/**
* Metadata associated with the plugin. Contains id, name, version, description, icon, configStructue etc.
* This is also available at the instance at this.pluginMetadata.
* @type {object}
*/
ArchitectureSpecGenerator.metadata = pluginMetadata;
// Prototypical inheritance from PluginBase.
ArchitectureSpecGenerator.prototype = Object.create(PluginBase.prototype);
ArchitectureSpecGenerator.prototype.constructor = ArchitectureSpecGenerator;
/**
* Main function for the plugin to execute. This will perform the execution.
* Notes:
* - Always log with the provided logger.[error,warning,info,debug].
* - Do NOT put any user interaction logic UI, etc. inside this method.
* - callback always has to be called even if error happened.
*
* @param {function(string, plugin.PluginResult)} callback - the result callback
*/
ArchitectureSpecGenerator.prototype.main = function (callback) {
// Use self to access core, project, result, logger etc from PluginBase.
// These are all instantiated at this point.
var self = this,
artifact;
self.loadNodeMap(self.activeNode)
.then(function (nodes) {
var filesToAdd = {},
result = ArchitectureSpecGenerator.getGeneratedFile(self, nodes);
if (result.violations.length > 0) {
result.violations.forEach(function (violation) {
self.createMessage(violation.node, violation.message, 'error');
});
throw new Error('Model has ' + result.violations.length +
' violation(s), see messages for details');
}
filesToAdd['Glue.xml'] = result.fileContent;
artifact = self.blobClient.createArtifact('GlueSpecification');
return artifact.addFiles(filesToAdd);
})
.then(function (fileHash) {
self.result.addArtifact(fileHash);
return artifact.save();
})
.then(function () {
self.result.setSuccess(true);
callback(null, self.result);
})
.catch(function (err) {
self.logger.error(err.stack);
// Result success is false at invocation.
callback(err, self.result);
});
};
/**
*
* @param {PluginBase} self - An initialized and configured plugin.
* @param {Object<string, Object>} nodes - all nodes loaded from the projectNode.
* @param {object} activeNode - the projectNode.
*
* @returns {fileContent: string, violations: Objects[]}
*/
ArchitectureSpecGenerator.getGeneratedFile = function (self, nodes) {
var result = {
violations: ArchitectureSpecGenerator.prototype.getViolations.call(self, nodes),
fileContent: null
},
rawModel = ArchitectureSpecGenerator.prototype.generateArchitectureModel.call(self, nodes),
model = ArchitectureSpecGenerator.prototype.generateMacros.call(self, rawModel),
macros,
xml;
if (model.violations.length > 0) {
result.violations = result.violations.concat(model.violations);
}
if (result.violations.length > 0) {
return result;
}
macros = ArchitectureSpecGenerator.prototype.generateRequireAccept.call(self, model.architectureModel.ports);
xml = {glue: {accepts: {accept: macros.accept}, requires: {require: macros.require}}};
return {
fileContent: (new Converter.JsonToXml()).convertToString(xml),
violations: result.violations // Will be empty array..
};
};
ArchitectureSpecGenerator.prototype.generateRequireAccept = function (inputPorts) {
var acceptPorts = [],
option = [],
causes = [],
ports = [],
macros = {
accept: [],
require: []
},
effect, port, acc, requiredPort,
acceptCauses, requiredPorts, listOfPorts;
for (port of inputPorts) {
effect = {'@id': port.name, '@specType': port.componentType};
acceptPorts = [];
for (acc of port.accept) {
if (acc !== '') {
acceptPorts.push({'@id': acc[0], '@specType': acc[1]});
}
}
acceptCauses = {port: acceptPorts};
option = [];
if (port.require !== '') {
for (requiredPorts of port.require) {
causes = [];
for (listOfPorts of requiredPorts) {
ports = [];
for (requiredPort of listOfPorts) {
if (requiredPort !== '') {
ports.push({'@id': requiredPort[0], '@specType': requiredPort[1]});
}
}
causes.push({port: ports});
}
option.push({causes: causes});
}
} else {
option.push({causes: [{port: []}]});
}
macros.accept.push({effect: effect, causes: acceptCauses});
macros.require.push({effect: effect, causes: {option: option}});
}
return macros;
};
ArchitectureSpecGenerator.prototype.generateMacros = function (architectureModel) {
var self = this,
port,
macros, violation, outputViolation, aux,
output = {
architectureModel: {},
violations: []
};
for (port of architectureModel.ports) {
macros = ArchitectureSpecGenerator.prototype.generateMacrosAlgorithm.call(self, port);
if (macros.violations.length > 0) {
for (violation of macros.violations) {
aux = true;
for (outputViolation of output.violations) {
if (violation.end === outputViolation.end) {
aux = false;
}
}
if (aux) {
output.violations.push(violation);
}
}
}
//set to list
port.require = [...macros.require];
port.accept = [...macros.accept];
}
for (port of architectureModel.ports) {
port = ArchitectureSpecGenerator.prototype.getMacroLists.call(self, port);
}
output.architectureModel = architectureModel;
return output;
};
ArchitectureSpecGenerator.prototype.getMacroLists = function (port) {
var acceptedPort, requireList, option, requiredPort,
simpleAccept = [],
simpleRequire = [],
simpleRequireList = [];
for (acceptedPort of port.accept) {
if (acceptedPort === '') {
simpleAccept.push('');
} else {
simpleAccept.push([acceptedPort.name, acceptedPort.componentType]);
}
}
for (requireList of port.require) {
if (requireList === '') {
simpleRequire = '';
} else {
simpleRequireList = [];
for (option of requireList) {
var simpleList = [];
for (requiredPort of option) {
if (requiredPort === '') {
simpleList.push('');
} else {
simpleList.push([requiredPort.name, requiredPort.componentType]);
}
}
simpleRequireList.push(simpleList);
}
simpleRequire.push(simpleRequireList);
}
}
port.accept = simpleAccept;
port.require = simpleRequire;
return port;
};
/* This is the paper's algorithm */
ArchitectureSpecGenerator.prototype.generateMacrosAlgorithm = function (port) {
var i, connector, connectorEnd, end,
option = [],
macros = {
require: new Set(),
accept: new Set(),
violations: []
},
reqCause = [];
if (port.connectorEnds !== undefined) {
for (end of port.connectorEnds) {
if (!end.hasOwnProperty('connector')) {
macros.require.add('');
macros.accept.add('');
break;
}
}
}
for (connector of port.connectors) {
option = [];
for (end of connector.ends) {
if (end !== undefined && end.port === port) {
connectorEnd = end;
}
}
if (connectorEnd !== undefined && connectorEnd.multiplicity !== '1') {
for (end of connector.ends) {
macros.accept.add(end.port);
}
} else {
for (end of connector.ends) {
if (end.port.id !== port.id) {
macros.accept.add(end.port);
}
}
}
if (connectorEnd !== undefined && connectorEnd.type === 'Trigger') {
option.push('');
} else {
var triggerExists = false;
for (end of connector.ends) {
if (end.type === 'Trigger') {
triggerExists = true;
}
}
for (end of connector.ends) {
if (triggerExists === false) {
if ((end.port.id !== port.id || end.multiplicity !== '1') &&
/^[0-9]$/.test(end.multiplicity)) {
reqCause = [];
for (i = 0; i < parseInt(end.multiplicity); i++) {
reqCause.push(end.port);
}
option.push(reqCause);
} else if (!/^[0-9]$/.test(end.multiplicity)) {
macros.violations.push({
node: end,
message: 'Multiplicity [' + end.multiplicity + '] of component end [' +
this.core.getPath(end) + '] is not a integer, thus architecture cannot be generated ' +
'with this plugin. Please run the TotalSpecAndEngineExecution plugin instead.'
});
}
} else {
if (end.type === 'Trigger' &&
/^[0-9]$/.test(end.multiplicity) &&
(end.port.id !== port.id || end.multiplicity !== '1' )) {
reqCause = [];
for (i = 0; i < parseInt(end.multiplicity); i++) {
reqCause.push(end.port);
}
option.push(reqCause);
} else if (end.type === 'Trigger' && !/^[0-9]$/.test(end.multiplicity)) {
macros.violations.push({
node: end,
message: 'Multiplicity [' + end.multiplicity + '] of component end [' +
this.core.getPath(end) + '] is not a integer, thus architecture cannot be generated ' +
'with this plugin. Please run the TotalSpecAndEngineExecution plugin instead.'
});
}
}
}
}
macros.require.add(option);
}
return macros;
};
ArchitectureSpecGenerator.prototype.generateArchitectureModel = function (nodes) {
var self = this,
path, node, end, port, child, connector, parent,
srcConnectorEnd, dstConnectorEnd,
subConnectors = [],
architectureModel = {
ports: [],
connectors: [],
connectorEnds: []
};
for (path in nodes) {
node = nodes[path];
//TODO: Update for hierarchical models
parent = self.core.getParent(node);
if (!self.isMetaTypeOf(parent, self.META.ArchitectureStyle) && !self.isMetaTypeOf(parent, self.META.ComponentTypesLibrary)) {
if (self.isMetaTypeOf(node, self.META.ComponentType)) {
for (child of self.core.getChildrenPaths(node)) {
if (self.isMetaTypeOf(nodes[child], self.META.EnforceableTransition)) {
nodes[child].componentType = path;
}
}
} else if ((self.isMetaTypeOf(node, self.META.EnforceableTransition)) && (!self.isMetaTypeOf(self.core.getParent(parent), self.META.ArchitectureStyle)) && (!self.isMetaTypeOf(self.core.getParent(parent), self.META.ComponentTypesLibrary))) {
port = node;
architectureModel.ports.push(port);
port.name = self.core.getAttribute(node, 'name');
port.id = self.core.getPath(node, 'ID');
} else if (self.isMetaTypeOf(node, self.META.Connector)) {
/* If the connector is binary */
if (self.getMetaType(nodes[self.core.getPointerPath(node, 'dst')]) !== self.META.Connector) {
connector = node;
architectureModel.connectors.push(node);
srcConnectorEnd = nodes[self.core.getPointerPath(node, 'src')];
dstConnectorEnd = nodes[self.core.getPointerPath(node, 'dst')];
srcConnectorEnd.connector = node;
dstConnectorEnd.connector = node;
connector.ends = [srcConnectorEnd, dstConnectorEnd];
/* If it is part of an n-ary connector */
} else {
subConnectors.push(node);
}
} else if (self.isMetaTypeOf(node, self.META.Connection)) {
end = nodes[self.core.getPointerPath(node, 'src')];
if (end !== undefined && self.getMetaType(end) !== self.META.Connector) {
architectureModel.connectorEnds.push(end);
port = nodes[self.core.getPointerPath(node, 'dst')];
end.port = port;
if (!port.hasOwnProperty('connectorEnds')) {
port.connectorEnds = [];
}
port.connectorEnds.push(end);
end.type = self.core.getAttribute(end, 'name');
end.degree = self.core.getAttribute(end, 'degree');
end.multiplicity = self.core.getAttribute(end, 'multiplicity');
}
//TODO: add also export ports for hierarchical connector motifs
}
}
}
architectureModel = ArchitectureSpecGenerator.prototype.connectorToEnds.call(self,
subConnectors, architectureModel, nodes);
return ArchitectureSpecGenerator.prototype.portToEnds.call(self, architectureModel);
};
ArchitectureSpecGenerator.prototype.connectorToEnds = function (subConnectors, architectureModel, nodes) {
var subPart, node, auxNode, end, connector,
self = this;
for (subPart of subConnectors) {
node = nodes[self.core.getPointerPath(subPart, 'dst')];
auxNode = nodes[self.core.getPointerPath(node, 'src')];
end = nodes[self.core.getPointerPath(subPart, 'src')];
if (end !== undefined && architectureModel.connectors.includes(node)) {
node.ends.push(end);
end.connector = node;
} else if (end !== undefined && architectureModel.connectorEnds.includes(auxNode)) {
for (connector in architectureModel.connectors) {
if (connector.ends.includes(auxNode)) {
connector.ends.push(end);
end.connector = connector;
}
}
}
}
return architectureModel;
};
ArchitectureSpecGenerator.prototype.portToEnds = function (architectureModel) {
var port, end,
self = this;
for (port of architectureModel.ports) {
port.connectors = new Set();
if (port.connectorEnds !== undefined) {
for (end of port.connectorEnds) {
if (end.hasOwnProperty('connector')) {
port.connectors.add(end.connector);
}
}
} else {
self.logger.warn('Port ' + self.core.getAttribute(port, 'name') + ' is not connected to any end.');
}
}
return architectureModel;
};
ArchitectureSpecGenerator.prototype.getViolations = function (nodes) {
var violations = [],
self = this,
nodePath, path,
node, isConnected,
zeroEnforceableTransitions = true;
for (nodePath in nodes) {
node = nodes[nodePath];
if (this.isMetaTypeOf(node, this.META.EnforceableTransition)) {
zeroEnforceableTransitions = false;
} else if (this.isMetaTypeOf(node, this.META.Connector)) {
if (self.core.getPointerPath(node, 'dst') === null) {
violations.push({
node: node,
message: 'Connector [' + nodePath + '] with no destination encountered. ' +
'Please connect or remove it.'
});
}
if (self.core.getPointerPath(node, 'src') === null) {
violations.push({
node: node,
message: 'Connector [' + nodePath + '] with no source encountered. Please connect or remove it.'
});
}
} else if (this.isMetaTypeOf(node, this.META.Connection)) {
if (self.core.getPointerPath(node, 'dst') === null) {
violations.push({
node: node,
message: 'Connection [' + nodePath + '] with no destination encountered. ' +
'Please connect or remove it'
});
}
if (self.core.getPointerPath(node, 'src') === null) {
violations.push({
node: node,
message: 'Connection [' + nodePath + '] with no source encountered. ' +
'Please connect or remove it.'
});
}
} else if (this.isMetaTypeOf(node, this.META.Trigger) || this.isMetaTypeOf(node, this.META.Synchron)) {
isConnected = false;
for (path in nodes) {
if (this.isMetaTypeOf(nodes[path], this.META.Connection)) {
if (self.core.getPointerPath(nodes[path], 'src') === nodePath) {
isConnected = true;
}
}
}
if (!isConnected) {
violations.push({
node: node,
message: 'ConnectorEnd [' + nodePath + '] is not connected to any port. ' +
'Please connect or remove it.'
});
}
}
}
if (zeroEnforceableTransitions) {
violations.push({
message: 'No Enforceable Transitions in the entire project, cannot generate Architecture specification.'
});
}
return violations;
};
return ArchitectureSpecGenerator;
});