exporttosccdxml.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /* SCCDXML exporter plugin*/
  2. const {
  3. __errorContinuable,
  4. __httpReq,
  5. __wHttpReq,
  6. __postInternalErrorMsg, __postMessage,
  7. __sequenceNumber,
  8. __successContinuable,
  9. __uri_to_id
  10. } = require("../__worker");
  11. const _do = require("../___do");
  12. const _utils = require('../utils');
  13. const _fs = _do.convert(require('fs'), ['readFile', 'writeFile', 'readdir']);
  14. const _fspp = _do.convert(require('../___fs++'), ['mkdirs']);
  15. module.exports = {
  16. 'interfaces' : [{'method':'POST', 'url=':'/exporttosccdxml'}],
  17. 'csworker' :
  18. function(resp,method,uri,reqData,wcontext)
  19. {
  20. var self = this,
  21. actions = [__wHttpReq('GET','/current.model?wid='+wcontext.__aswid)];
  22. _do.chain(actions)(
  23. function(asdata){
  24. var writeActions = [_fspp.mkdirs('./exported_to_sccdxml/classes/')];
  25. _do.chain(writeActions) (
  26. function() {
  27. var file_contents = '',
  28. as = _utils.jsonp(asdata['data']),
  29. type_map = {},
  30. type_map_keys = {},
  31. incoming = {},
  32. outgoing = {};
  33. for (var key in as.nodes) {
  34. var node = as.nodes[key];
  35. if (!(node['$type'] in type_map)) {
  36. type_map[node['$type']] = [];
  37. type_map_keys[node['$type']] = [];
  38. }
  39. type_map[node['$type']].push(node);
  40. type_map_keys[node['$type']].push(key);
  41. node['$key'] = key;
  42. }
  43. function xml_safe(str) {
  44. return str.replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;");
  45. }
  46. function safe_add(l, el) {
  47. if (l.indexOf(el) == -1) {
  48. l.push(el);
  49. }
  50. }
  51. function find_initial(state){
  52. var substates = outgoing[state['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/contain' || as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/ocContain';}).map(function(tid) {return as.nodes[tid];});
  53. for (var sidx in substates){
  54. var substate = as.nodes[outgoing[substates[sidx]['$key']]];
  55. if(substate['isStart']['value']){
  56. return substate['name']['value'];
  57. }
  58. }
  59. }
  60. function get_full_path(state){
  61. var parent_link = incoming[state['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/contain' || as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/ocContain' || as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/containOC' || as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/includes';}).map(function(tid) {return as.nodes[tid];})[0];
  62. if (parent_link != undefined){
  63. var parent = as.nodes[incoming[parent_link['$key']][0]];
  64. return get_full_path(parent).concat([state['name']['value']]);
  65. }
  66. else{
  67. return [state['name']['value']];
  68. }
  69. }
  70. function find_path(source, destination){
  71. if(source['$key'] == destination['$key']){
  72. return ".";
  73. }
  74. // Resolve absolute path of source
  75. source_path = get_full_path(source);
  76. // Resolve absolute path of destination
  77. destination_path = get_full_path(destination);
  78. // Now make the destination path relative to the source path
  79. var common;
  80. for (var i in source_path){
  81. if(source_path[i] != destination_path[i]){
  82. common = i;
  83. break;
  84. }
  85. }
  86. source_path = source_path.slice(i, source_path.length);
  87. destination_path = destination_path.slice(i, destination_path.length);
  88. var result = ".";
  89. for (var i in source_path){
  90. result += "/..";
  91. }
  92. for (var i in destination_path){
  93. result += "/" + destination_path[i];
  94. }
  95. return result;
  96. }
  97. function write_raise(lst){
  98. contents = "";
  99. for(var ridx in lst){
  100. var r = lst[ridx];
  101. contents += '<raise event="' + r['event'] + '"';
  102. if(r['scope'].length == 0){
  103. contents += ' scope="broad">\n';
  104. }
  105. else if(r['scope'].lastIndexOf("output(", 0) === 0){
  106. contents += ' scope="output" port="' + r['scope'].slice(7, -1)+ '">\n';
  107. }
  108. else{
  109. contents += ' scope="' + r['scope'] + '">\n';
  110. }
  111. let args = r['arguments'];
  112. for (var aidx in args){
  113. var arg = args[aidx];
  114. if(arg != ""){
  115. contents += '<parameter expr="' + xml_safe(arg) + '"/>\n';
  116. }
  117. }
  118. contents += '</raise>\n';
  119. }
  120. return contents;
  121. }
  122. function resolve_transitions(state){
  123. contents = "";
  124. if(outgoing[state['$key']] == undefined){
  125. return "";
  126. }
  127. var transitions = outgoing[state['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/transition';}).map(function(tid) {return as.nodes[tid];});
  128. for (var tidx in transitions){
  129. var transition = transitions[tidx];
  130. contents += "<transition ";
  131. if(transition['after']['value'].length > 0){
  132. contents += 'after="' + xml_safe(transition['after']['value']) + '" ';
  133. }
  134. if(transition['event']['value'].length > 0){
  135. contents += 'event="' + transition['event']['value'] + '" ';
  136. }
  137. if(transition['guard']['value'].length > 0){
  138. contents += 'cond="' + xml_safe(transition['guard']['value']) + '" ';
  139. }
  140. if(transition['port']['value'].length > 0){
  141. contents += 'port="' + transition['port']['value'] + '" ';
  142. }
  143. var target = as.nodes[outgoing[transition['$key']][0]];
  144. contents += 'target="' + find_path(state, target) + '" ';
  145. contents += '>\n';
  146. var parameters = transition['parameters']['value'];
  147. for(var pidx in parameters){
  148. var param = parameters[pidx];
  149. contents += '<parameter name="' + param + '"/>\n';
  150. }
  151. if(transition['action']['value'].length > 0){
  152. contents += "<script><![CDATA[";
  153. contents += transition['action']['value'].split("/*newline*/").join("\n");
  154. contents += "]]></script>\n";
  155. }
  156. var raise = transition['raise']['value'];
  157. contents += write_raise(raise);
  158. contents += '</transition>\n';
  159. }
  160. return contents;
  161. }
  162. function find_priority(state){
  163. state = as.nodes[state['$key']];
  164. if(state['option']['value'] == "OTF"){
  165. return "source_parent";
  166. }
  167. else if(state['option']['value'] == "ITF"){
  168. return "source_child";
  169. }
  170. else {
  171. console.log("Unknown conflict resolution selected: " + state['option']['value']);
  172. return "undefined";
  173. }
  174. }
  175. function add_actions(state){
  176. contents = "<onentry>\n";
  177. state = as.nodes[state['$key']];
  178. if(state['entryAction'] == undefined){
  179. return "";
  180. }
  181. contents += "<script><![CDATA[";
  182. if(state['entryAction']['value'].length > 0){
  183. contents += state['entryAction']['value'].split("/*newline*/").join("\n");
  184. }
  185. contents += "]]></script>\n";
  186. var raise = state['raiseEntry']['value'];
  187. contents += write_raise(raise);
  188. contents += "</onentry>\n";
  189. contents += "<onexit>\n";
  190. contents += "<script><![CDATA[";
  191. if(state['exitAction']['value'].length > 0){
  192. contents += state['exitAction']['value'].split("/*newline*/").join("\n");
  193. }
  194. contents += "]]></script>\n";
  195. var raise = state['raiseExit']['value'];
  196. contents += write_raise(raise);
  197. contents += "</onexit>\n";
  198. return contents;
  199. }
  200. function recursive(state, is_root){
  201. var contents = "";
  202. if(outgoing[state['$key']] == undefined){
  203. var containOC = [],
  204. contain = [],
  205. ocContain = [];
  206. }
  207. else{
  208. var containOC = outgoing[state['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/containOC';}).map(function(tid) {return as.nodes[tid];});
  209. var contain = outgoing[state['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/contain';}).map(function(tid) {return as.nodes[tid];});
  210. var ocContain = outgoing[state['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/ocContain';}).map(function(tid) {return as.nodes[tid];});
  211. var history = outgoing[state['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/includes';}).map(function(tid) {return as.nodes[tid];});
  212. }
  213. var s = as.nodes[state['$key']];
  214. if (is_root){
  215. contents += '<scxml initial="' + find_initial(s) + '" priority="' + find_priority(s) + '">\n';
  216. var children = containOC.concat(contain).concat(ocContain).concat(history);
  217. console.log("Children of root: " + children);
  218. for (var cidx in children){
  219. var child = as.nodes[outgoing[children[cidx]['$key']]];
  220. contents += recursive(child, false);
  221. }
  222. contents += resolve_transitions(s);
  223. contents += '</scxml>\n';
  224. }
  225. else if (containOC.length > 0){
  226. contents += '<parallel id="' + s['name']['value'] + '">\n';
  227. var children = containOC.concat(contain).concat(ocContain).concat(history);
  228. for (var cidx in children){
  229. var child = as.nodes[outgoing[children[cidx]['$key']]];
  230. contents += recursive(child, false);
  231. }
  232. contents += resolve_transitions(s);
  233. contents += add_actions(s);
  234. contents += '</parallel>\n';
  235. }
  236. else if(contain.length + ocContain.length > 0){
  237. contents += '<state id="' + s['name']['value'] + '" initial="' + find_initial(s) + '">\n';
  238. var children = containOC.concat(contain).concat(ocContain).concat(history);
  239. for (var cidx in children){
  240. var child = as.nodes[outgoing[children[cidx]['$key']]];
  241. contents += recursive(child, false);
  242. }
  243. contents += resolve_transitions(s);
  244. contents += add_actions(s);
  245. contents += '</state>\n';
  246. }
  247. else {
  248. var history = incoming[state['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/includes';}).map(function(tid) {return as.nodes[tid];});
  249. if (history.length > 0){
  250. contents += '<history id="' + s['name']['value'] + '" type="' + s['type']['value'].trim() + '"/>\n';
  251. }
  252. else{
  253. contents += '<state id="' + s['name']['value'] + '">\n';
  254. contents += resolve_transitions(s);
  255. contents += add_actions(s);
  256. contents += '</state>\n';
  257. }
  258. }
  259. return contents;
  260. }
  261. function calc_in_out_rel(rel_type) {
  262. for (var idx in type_map_keys[rel_type]) {
  263. key = type_map_keys[rel_type][idx];
  264. incoming[key] = [];
  265. outgoing[key] = [];
  266. for (var e_key in as.edges) {
  267. var e = as.edges[e_key];
  268. if (e['dest'] == key) {
  269. safe_add(incoming[key], e['src']);
  270. if (!(e['src'] in outgoing)) {
  271. outgoing[e['src']] = [];
  272. }
  273. safe_add(outgoing[e['src']], key);
  274. }
  275. if (e['src'] == key) {
  276. safe_add(outgoing[key], e['dest']);
  277. if (!(e['dest'] in incoming)) {
  278. incoming[e['dest']] = [];
  279. }
  280. safe_add(incoming[e['dest']], key);
  281. }
  282. }
  283. }
  284. }
  285. calc_in_out_rel('/Formalisms/SCCD/SCCD/transition');
  286. calc_in_out_rel('/Formalisms/SCCD/SCCD/Inheritance');
  287. calc_in_out_rel('/Formalisms/SCCD/SCCD/Association');
  288. calc_in_out_rel('/Formalisms/SCCD/SCCD/contain');
  289. calc_in_out_rel('/Formalisms/SCCD/SCCD/containOC');
  290. calc_in_out_rel('/Formalisms/SCCD/SCCD/ocContain');
  291. calc_in_out_rel('/Formalisms/SCCD/SCCD/behaviour');
  292. calc_in_out_rel('/Formalisms/SCCD/SCCD/includes');
  293. for (var key in type_map['/Formalisms/SCCD/SCCD/Class']){
  294. var node = type_map['/Formalisms/SCCD/SCCD/Class'][key],
  295. external = node['external']['value'],
  296. type = node['name']['value'];
  297. if(!external){
  298. file_contents += '<class name="' + node['name']['value'] + '" default="true">\n';
  299. file_contents += '\t<relationships>\n';
  300. inheritances = outgoing[node['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/Inheritance';}).map(function(tid) {return as.nodes[tid];});
  301. for (var iidx in inheritances){
  302. target = as.nodes[outgoing[inheritances[iidx]['$key']][0]];
  303. file_contents += '\t\t<inheritance class="' + target['name']['value'] + '"/>\n';
  304. }
  305. associations = outgoing[node['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/Association';}).map(function(tid) {return as.nodes[tid];});
  306. for (var iidx in associations){
  307. target = as.nodes[outgoing[associations[iidx]['$key']][0]];
  308. file_contents += '\t\t<association name="' + associations[iidx]['name']['value'] + '" class="' + target['name']['value'] + '"/>\n';
  309. }
  310. file_contents += '';
  311. file_contents += '\t</relationships>\n';
  312. for (var fidx in node['methods']['value']){
  313. var func = node['methods']['value'][fidx];
  314. file_contents += '\t<method name="' + func['name'] + '">\n';
  315. for (var aidx in func['args']){
  316. var arg = func['args'][aidx];
  317. if(arg['name'] == ""){
  318. continue;
  319. }
  320. file_contents += '\t\t<parameter name="' + arg['name'] + '"/>\n';
  321. }
  322. file_contents += '\t<body><![CDATA[';
  323. file_contents += func['body'].split("/*newline*/").join("\n");
  324. file_contents += "\n";
  325. file_contents += '\t]]></body>\n';
  326. file_contents += '\t</method>\n';
  327. }
  328. var behaviour = outgoing[node['$key']].filter(function(el) {return as.nodes[el]['$type'] == '/Formalisms/SCCD/SCCD/behaviour';}).map(function(tid) {return as.nodes[tid];});
  329. var statechart = as.nodes[outgoing[behaviour[0]['$key']][0]];
  330. file_contents += recursive(statechart, true);
  331. file_contents += '</class>\n';
  332. }
  333. }
  334. _fs.writeFileSync('./exported_to_sccdxml/classes/model.xml', file_contents);
  335. __postMessage({'statusCode': 200,
  336. 'respIndex': resp});
  337. },
  338. function(writeErr) {__postInternalErrorMsg(resp, writeErr);}
  339. );
  340. },
  341. function(err) {__postInternalErrorMsg(resp, err);}
  342. );
  343. },
  344. 'asworker' :
  345. function(resp,method,uri,reqData,wcontext)
  346. {
  347. this.localcontext.counter++;
  348. __postMessage(
  349. {'statusCode':200,
  350. 'respIndex':resp});
  351. }
  352. };