exportM2Ecore.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. const {
  2. __errorContinuable,
  3. __httpReq,
  4. __wHttpReq,
  5. __postInternalErrorMsg, __postMessage,
  6. __sequenceNumber,
  7. __successContinuable,
  8. __uri_to_id
  9. } = require("../__worker");
  10. const _do = require("../___do");
  11. const _utils = require('../utils');
  12. const _mmmk = require("../mmmk");
  13. const _fs = _do.convert(require('fs'), ['readFile', 'writeFile', 'readdir']);
  14. const _fspp = _do.convert(require('../___fs++'), ['mkdirs']);
  15. module.exports = {
  16. 'interfaces'
  17. :
  18. [{'method': 'POST', 'url=': '/exportM2Ecore'}],
  19. 'csworker'
  20. :
  21. function (resp, method, uri, reqData, wcontext) {
  22. var actions = [__wHttpReq('GET', '/current.model?wid=' + wcontext.__aswid)];
  23. _do.chain(actions)(
  24. function (asdata) {
  25. //The generated file will be in the "exported_to_ecore" folder
  26. var writeActions = [_fspp.mkdirs('./exported_to_ecore/')];
  27. _do.chain(writeActions)(
  28. function () {
  29. //This variable will contain the lines of the generated files, i.e. the xmi code.
  30. var file_contents = '';
  31. //This variable represent the abstract syntax. It contains all the information.
  32. var as = _utils.jsonp(asdata['data']);
  33. //This variable will contain the root and all the other elements contained by the root.
  34. var root = {};
  35. var listRoots = [];
  36. var graph = [];
  37. var taken = [];
  38. var listCycle = [];
  39. /**
  40. This function removes an element of an array.
  41. **/
  42. function remove(array, elem) {
  43. var index = array.indexOf(elem);
  44. if (index !== -1) {
  45. array.splice(index, 1);
  46. }
  47. }
  48. /**
  49. This function searches if a root exists. A root is a class from which all the other
  50. classes are accessible.
  51. The process :
  52. - find all the classes;
  53. - eliminate all the classes that are the end of a link, that means they are accessible;
  54. - if there are classes that form a cycle, add them all to listNodes;
  55. - if listNodes contains only an element, that is the root. Else, there is no root.
  56. **/
  57. function hasRoot() {
  58. var listNodes = [];
  59. for (var key in as.nodes)
  60. listNodes.push(key);
  61. //removing links node
  62. for (var edge = 0; edge < as.edges.length; edge = edge + 2)
  63. remove(listNodes, as.edges[edge].dest);
  64. //removing accessible classes
  65. for (var edge = 0; edge < as.edges.length; edge = edge + 2) {
  66. if (as.edges[edge].src != as.edges[edge + 1].dest)
  67. remove(listNodes, as.edges[edge + 1].dest);
  68. }
  69. //if there are graph cycles, add the nodes in listNodes
  70. constructGraph();
  71. for (var edge = 0; edge < as.edges.length; edge = edge + 2) {
  72. var id = as.edges[edge].src;
  73. var cycle = detectCycle(id, id);
  74. if (cycle && !inside(listNodes, id)) {
  75. listNodes.push(id);
  76. listCycle.push(id);
  77. }
  78. }
  79. return listNodes;
  80. }
  81. /**
  82. This function returns the root. If none was found, it will create one.
  83. **/
  84. function setRoot() {
  85. listRoots = hasRoot();
  86. if (listRoots.length == 1)
  87. root = createNode(listRoots[0]);
  88. else
  89. root = createRootClass(listRoots);
  90. }
  91. /**
  92. This function creates a root and assigns all the possible roots as references.
  93. **/
  94. function createRootClass(list) {
  95. var node = {};
  96. node.name = reqData['name'] + 'Root';
  97. var contain = [];
  98. for (var i = 0; i < list.length; i++)
  99. contain.push(createNode(list[i]));
  100. node.contain = contain;
  101. return node;
  102. }
  103. /**
  104. This function change all the linkType property of each reference of
  105. the node to containment. The node is the root or a possible one.
  106. **/
  107. function createNode(identifier) {
  108. var node = {};
  109. var elem = as.nodes[identifier];
  110. var linkType = findType(identifier);
  111. if (inside(listRoots, identifier))
  112. node.linkType = linkType + 'Link';
  113. else
  114. node.linkType = linkType;
  115. node.id = identifier;
  116. var keys = Object.keys(elem);
  117. remove(keys, "$type");
  118. var attr = [];
  119. for (var i = 0; i < keys.length; i++)
  120. attr.push(findAttribute(keys[i], identifier));
  121. node.attributes = attr;
  122. node.contain = findContained(identifier);
  123. return node;
  124. }
  125. /**
  126. This function creates the appropriate indentation.
  127. **/
  128. function space(deep) {
  129. var space = '';
  130. for (var i = 0; i < deep; i++)
  131. space += ' ';
  132. return space;
  133. }
  134. /**
  135. This function finds the link's type of the attribute given the link's identifier.
  136. Input : identifier
  137. Output : type
  138. **/
  139. function findType(linkIdentifier) {
  140. var midType = as.nodes[linkIdentifier]["$type"].split("/");
  141. var attrType = midType[midType.length - 1];
  142. return attrType;
  143. }
  144. /**
  145. This function finds all the attributes of an element.
  146. **/
  147. function findAttribute(key, keyDest) {
  148. var attr = {};
  149. attr.name = key;
  150. attr.value = as.nodes[keyDest][key].value;
  151. var attrType = as.nodes[keyDest][key].type;
  152. if (attr.value != undefined && attr.value.length > 0) {
  153. if (attrType.startsWith("list"))
  154. attr.list = true;
  155. else
  156. attr.list = false;
  157. }
  158. return attr;
  159. }
  160. /**
  161. This function finds all the elements contained in another one, in a recursive way.
  162. **/
  163. function findContained(nodeKey) {
  164. var listContained = [];
  165. if (inside(listCycle, nodeKey) && inside(taken, nodeKey))
  166. return [];
  167. for (var k = 0; k < as.edges.length; k++) {
  168. if (nodeKey == as.edges[k].src) {
  169. if (inside(listCycle, nodeKey))
  170. taken.push(nodeKey);
  171. var lien = as.edges[k].dest;
  172. var keyDest = as.edges[k + 1].dest;
  173. var linkType = findType(lien);
  174. var elem = {};
  175. elem.linkType = linkType;
  176. elem.attributes = [];
  177. var keys = Object.keys(as.nodes[keyDest]);
  178. remove(keys, "$type");
  179. for (var i = 0; i < keys.length; i++) {
  180. var attr = findAttribute(keys[i], keyDest);
  181. if (attr.value != undefined && attr.value.length > 0)
  182. elem.attributes.push(attr);
  183. }
  184. var contain = [];
  185. if (!isInRoot(keyDest) && nodeKey != keyDest)
  186. contain = findContained(keyDest);
  187. elem.contain = contain;
  188. listContained.push(elem);
  189. }
  190. }
  191. return listContained;
  192. }
  193. /**
  194. This function returns true if the node, specified by its identifier,
  195. is directly in root.contain, not in one of the subnode.
  196. **/
  197. function isInRoot(key) {
  198. for (var i = 0; i < listCycle.length; i++) {
  199. if (listCycle[i] == key)
  200. return true;
  201. }
  202. return false;
  203. }
  204. /**
  205. This function will write the header of the file including the
  206. name of the root and the URI of the metamodel.
  207. This header is generated when the user doesn't want a dynamic instance.
  208. **/
  209. function writeHeaderStatic() {
  210. var head = '<?xml version="1.0" encoding="UTF-8"?> \n';
  211. head += '<' + root.name;
  212. head += ' xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns="' + reqData['uri'] + '"';
  213. if (root.attributes != null)
  214. head += writeAttributes(root, 0);
  215. else
  216. head += '> \n';
  217. return head;
  218. }
  219. /**
  220. This function will write the header of the file including the
  221. name of the root and the URI of the metamodel.
  222. This header is generated when the user want a dynamic instance.
  223. **/
  224. function writeHeaderDynamic(){
  225. var head = '<?xml version="1.0" encoding="UTF-8"?> \n';
  226. head += '<' + reqData['name'] + ':' + root.name;
  227. head += ' xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n';
  228. head += 'xmlns:' + reqData['name'] + '="' + reqData['uri'] + '" xsi:schemaLocation="' + reqData['uri'] + ' ' + reqData['nameMM'] + '.ecore"';
  229. if(root.attributes != null)
  230. head += writeAttributes(root, 0);
  231. else
  232. head += '> \n';
  233. return head;
  234. }
  235. /**
  236. This function writes the attributes of an element.
  237. **/
  238. function writeAttributes(node, deep) {
  239. var attribut = '';
  240. var listAttr = '';
  241. for (var i = 0; i < node.attributes.length; i++) {
  242. var attr = node.attributes[i];
  243. if (attr.list)
  244. listAttr += writeListAttribute(attr.value, attr.name, deep + 2);
  245. else
  246. attribut += ' ' + attr.name + '="' + attr.value + '"';
  247. }
  248. attribut += '> \n' + listAttr;
  249. return attribut;
  250. }
  251. /**
  252. This function writes a list attribute.
  253. **/
  254. function writeListAttribute(list, name, deep) {
  255. var liste = '';
  256. for (var i = 0; i < list.length; i++)
  257. liste += space(deep) + '<' + name + '>' + list[i] + '</' + name + '> \n';
  258. return liste;
  259. }
  260. /**
  261. This function writes all the contained nodes of the specified node
  262. **/
  263. function writeContained(listContained, deep) {
  264. var contained = '';
  265. for (var i = 0; i < listContained.length; i++) {
  266. contained += space(deep + 2) + '<' + listContained[i].linkType;
  267. var attributes = '';
  268. // if (listContained[i].attributes != null) {
  269. // }
  270. contained += writeAttributes(listContained[i], deep + 2);
  271. if (listContained[i].contain.length > 0)
  272. contained += writeContained(listContained[i].contain, deep + 2);
  273. contained += space(deep + 2) + '</' + listContained[i].linkType + '> \n';
  274. }
  275. return contained;
  276. }
  277. /**
  278. This function will write everything (classes, attributes, references, enumerations)
  279. in a string.
  280. **/
  281. function writeFile() {
  282. if(reqData['type'] == 'Dynamic instance')
  283. file_contents += writeHeaderDynamic();
  284. else
  285. file_contents += writeHeaderStatic();
  286. file_contents += writeContained(root.contain, 0);
  287. file_contents += '</' + root.name + '>';
  288. }
  289. /**
  290. This function returns true if the element is in the array.
  291. **/
  292. function inside(array, elem) {
  293. for (var i = 0; i < array.length; i++) {
  294. if (array[i] == elem)
  295. return true;
  296. }
  297. return false;
  298. }
  299. /**
  300. This function populates graph. It takes all the classes
  301. and register them in graph. graph is a list.
  302. **/
  303. function constructGraph() {
  304. var list = [];
  305. for (var i = 0; i < as.edges.length; i += 2) {
  306. var src = as.edges[i].src;
  307. if (!inside(list, src)) {
  308. list.push(src);
  309. var srcNode = {};
  310. srcNode.id = src;
  311. var linked = findLinked(src);
  312. srcNode.linked = linked;
  313. graph.push(srcNode);
  314. }
  315. }
  316. }
  317. /**
  318. This function detects graph cycle.
  319. Input :
  320. - id : the identifier of the actual node;
  321. - initial : the identifier of the first node of the cycle.
  322. Output :
  323. - true : there is a graph cycle;
  324. - false : no graph cycle.
  325. **/
  326. function detectCycle(id, initial) {
  327. for (var i = 0; i < graph.length; i++) {
  328. if (graph[i].id == id) {
  329. if (inside(graph[i].linked, initial))
  330. return true;
  331. else {
  332. for (var j = 0; j < graph[i].linked.length; j++)
  333. return detectCycle(graph[i].linked[j], initial);
  334. }
  335. return false;
  336. }
  337. }
  338. }
  339. /**
  340. This function finds all the related class of a class.
  341. **/
  342. function findLinked(src) {
  343. var listLinked = [];
  344. for (var i = 0; i < as.edges.length; i += 2) {
  345. if (as.edges[i].src == src && as.edges[i + 1].dest != src) {
  346. listLinked.push(as.edges[i + 1].dest);
  347. }
  348. }
  349. return listLinked;
  350. }
  351. /***************************************************************************************************************************************************************
  352. The following is like the main class of this file. It will call the appropriate functions
  353. in order to create the export file.
  354. ****************************************************************************************************************************************************************/
  355. setRoot();
  356. writeFile();
  357. _fs.writeFileSync('./exported_to_ecore/' + reqData['name'] + 'Model.xmi', file_contents);
  358. __postMessage({
  359. 'statusCode': 200,
  360. 'respIndex': resp
  361. });
  362. },
  363. function (writeErr) {
  364. __postInternalErrorMsg(resp, writeErr);
  365. }
  366. );
  367. },
  368. function (err) {
  369. __postInternalErrorMsg(resp, err);
  370. }
  371. );
  372. }
  373. ,
  374. 'asworker'
  375. :
  376. function (resp, method, uri, reqData, wcontext) {
  377. __postMessage(
  378. {
  379. 'statusCode': 200,
  380. 'respIndex': resp
  381. });
  382. }
  383. };