exportM2Ecore.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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['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.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.length > 0)
  182. elem.attributes.push(attr);
  183. }
  184. var contain = [];
  185. if (!isInRoot(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. **/
  208. function writeHeader() {
  209. var head = '<?xml version="1.0" encoding="ISO-8859-1"?> \n';
  210. head += '<' + root.name;
  211. head += ' xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns="' + reqData['uri'] + '"';
  212. if (root.attributes != null)
  213. head += writeAttributes(root, 0);
  214. else
  215. head += '> \n';
  216. return head;
  217. }
  218. /**
  219. This function writes the attributes of an element.
  220. **/
  221. function writeAttributes(node, deep) {
  222. var attribut = '';
  223. var listAttr = '';
  224. for (var i = 0; i < node.attributes.length; i++) {
  225. var attr = node.attributes[i];
  226. if (attr.list)
  227. listAttr += writeListAttribute(attr.value, attr.name, deep + 2);
  228. else
  229. attribut += ' ' + attr.name + '="' + attr.value + '"';
  230. }
  231. attribut += '> \n' + listAttr;
  232. return attribut;
  233. }
  234. /**
  235. This function writes a list attribute.
  236. **/
  237. function writeListAttribute(list, name, deep) {
  238. var liste = '';
  239. for (var i = 0; i < list.length; i++)
  240. liste += space(deep) + '<' + name + '>' + list[i] + '</' + name + '> \n';
  241. return liste;
  242. }
  243. /**
  244. This function writes all the contained nodes of the specified node
  245. **/
  246. function writeContained(listContained, deep) {
  247. var contained = '';
  248. for (var i = 0; i < listContained.length; i++) {
  249. contained += space(deep + 2) + '<' + listContained[i].linkType;
  250. var attributes = '';
  251. // if (listContained[i].attributes != null) {
  252. // }
  253. contained += writeAttributes(listContained[i], deep + 2);
  254. if (listContained[i].contain.length > 0)
  255. contained += writeContained(listContained[i].contain, deep + 2);
  256. contained += space(deep + 2) + '</' + listContained[i].linkType + '> \n';
  257. }
  258. return contained;
  259. }
  260. /**
  261. This function will write everything (classes, attributes, references, enumerations)
  262. in a string.
  263. **/
  264. function writeFile() {
  265. file_contents += writeHeader();
  266. file_contents += writeContained(root.contain, 0);
  267. file_contents += '</' + root.name + '>';
  268. }
  269. /**
  270. This function returns true if the element is in the array.
  271. **/
  272. function inside(array, elem) {
  273. for (var i = 0; i < array.length; i++) {
  274. if (array[i] == elem)
  275. return true;
  276. }
  277. return false;
  278. }
  279. /**
  280. This function populates graph. It takes all the classes
  281. and register them in graph. graph is a list.
  282. **/
  283. function constructGraph() {
  284. var list = [];
  285. for (var i = 0; i < as.edges.length; i += 2) {
  286. var src = as.edges[i].src;
  287. if (!inside(list, src)) {
  288. list.push(src);
  289. var srcNode = {};
  290. srcNode.id = src;
  291. var linked = findLinked(src);
  292. srcNode.linked = linked;
  293. graph.push(srcNode);
  294. }
  295. }
  296. }
  297. /**
  298. This function detects graph cycle.
  299. Input :
  300. - id : the identifier of the actual node;
  301. - initial : the identifier of the first node of the cycle.
  302. Output :
  303. - true : there is a graph cycle;
  304. - false : no graph cycle.
  305. **/
  306. function detectCycle(id, initial) {
  307. for (var i = 0; i < graph.length; i++) {
  308. if (graph[i].id == id) {
  309. if (inside(graph[i].linked, initial))
  310. return true;
  311. else {
  312. for (var j = 0; j < graph[i].linked.length; j++)
  313. return detectCycle(graph[i].linked[j], initial);
  314. }
  315. return false;
  316. }
  317. }
  318. }
  319. /**
  320. This function finds all the related class of a class.
  321. **/
  322. function findLinked(src) {
  323. var listLinked = [];
  324. for (var i = 0; i < as.edges.length; i += 2) {
  325. if (as.edges[i].src == src && as.edges[i + 1].dest != src) {
  326. listLinked.push(as.edges[i + 1].dest);
  327. }
  328. }
  329. return listLinked;
  330. }
  331. /***************************************************************************************************************************************************************
  332. The following is like the main class of this file. It will call the appropriate functions
  333. in order to create the export file.
  334. ****************************************************************************************************************************************************************/
  335. setRoot();
  336. writeFile();
  337. _fs.writeFileSync('./exported_to_ecore/' + reqData['name'] + 'Model.xmi', file_contents);
  338. __postMessage({
  339. 'statusCode': 200,
  340. 'respIndex': resp
  341. });
  342. },
  343. function (writeErr) {
  344. __postInternalErrorMsg(resp, writeErr);
  345. }
  346. );
  347. },
  348. function (err) {
  349. __postInternalErrorMsg(resp, err);
  350. }
  351. );
  352. }
  353. ,
  354. 'asworker'
  355. :
  356. function (resp, method, uri, reqData, wcontext) {
  357. __postMessage(
  358. {
  359. 'statusCode': 200,
  360. 'respIndex': resp
  361. });
  362. }
  363. };