modelverse_connector.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. class ModelVerseConnector {
  2. constructor(address) {
  3. ModelVerseConnector.taskname = "task_manager";
  4. ModelVerseConnector.address = (address == undefined) ? "http://127.0.0.1:8001" : address;
  5. ModelVerseConnector.ERROR = 0;
  6. ModelVerseConnector.WORKING = 1;
  7. ModelVerseConnector.OKAY = 2;
  8. ModelVerseConnector.connected = true;
  9. ModelVerseConnector.curr_model = null;
  10. ModelVerseConnector.element_map = {};
  11. ModelVerseConnector.PM2MV_metamodel_map = {
  12. "/Formalisms/__LanguageSyntax__/SimpleClassDiagram/SimpleClassDiagram" : "formalisms/SimpleClassDiagrams"
  13. };
  14. }
  15. static set_status(status) {
  16. let red = "rgb(220,20,60)";
  17. let yellow = "rgb(218,165,32)";
  18. let green = "rgb(50,205,50)";
  19. let colours = [red, yellow, green];
  20. let set_colour = function (colour) {
  21. let mv_toolbar = $('#div_toolbar_\\2f Toolbars\\2f ModelVerse\\2f ModelVerse\\2e buttons\\2e model');
  22. mv_toolbar.css("background-color", colour)
  23. };
  24. set_colour(colours[status]);
  25. }
  26. static save_model(model_name, data){
  27. ModelVerseConnector.set_status(ModelVerseConnector.WORKING);
  28. console.log("Save model: " + model_name);
  29. let parsed_data = JSON.parse(data);
  30. // console.log(parsed_data);
  31. let m = JSON.parse(parsed_data['data']['m']);
  32. let mms = JSON.parse(parsed_data['data']['mms']);
  33. //detect the metamodels
  34. if (Object.keys(mms).length > 1){
  35. console.log("Warning: More than one meta-model detected!")
  36. }
  37. let primary_mm_PM = Object.keys(mms)[0];
  38. let primary_mm_MV = ModelVerseConnector.PM2MV_metamodel_map[primary_mm_PM];
  39. //TODO: Allow user to select meta-model when it is not found
  40. // console.log("MV PM: " + primary_mm_PM);
  41. // console.log("MV MM: " + primary_mm_MV);
  42. if (primary_mm_MV == undefined){
  43. WindowManagement.openDialog(_ERROR,'Cannot find meta-model in ModelVerse: "' + primary_mm_PM + '"');
  44. return;
  45. }
  46. let model_delete = {
  47. "data": utils.jsons(["model_delete", model_name])
  48. };
  49. let model_create = {
  50. "data": utils.jsons(["model_add", primary_mm_MV, model_name, ""])
  51. };
  52. let model_edit = {
  53. "data": utils.jsons(["model_modify", model_name, primary_mm_MV])
  54. };
  55. ModelVerseConnector.curr_model = model_name;
  56. ModelVerseConnector.send_command(model_create)
  57. .then(ModelVerseConnector.get_output)
  58. .then(ModelVerseConnector.send_command(model_edit))
  59. .then(ModelVerseConnector.get_output)
  60. .then(function(data){
  61. let node_creation_promises = [];
  62. for (const [key, node] of Object.entries(m.nodes)) {
  63. if (node.name == undefined){
  64. continue;
  65. }
  66. if (!(node.linktype == undefined)){
  67. continue;
  68. }
  69. let node_name = node.name.value;
  70. let node_type = node.$type.split("/").slice(-1)[0];
  71. node_creation_promises.push(ModelVerseConnector.send_command(
  72. {"data": utils.jsons(["instantiate_node", node_type, node_name])}
  73. ));
  74. let set_id = function (id){
  75. ModelVerseConnector.element_map[key] = id.split(" ")[1].replace("\"", "");
  76. };
  77. node_creation_promises.push(ModelVerseConnector.get_output(set_id));
  78. }
  79. let simple_type = ["String", "Int", "Float", "Boolean", "Code", "File", "Map", "List", "ENUM"];
  80. for (let st of simple_type){
  81. node_creation_promises.push(ModelVerseConnector.send_command(
  82. {"data": utils.jsons(["instantiate_node", "SimpleAttribute", st])}
  83. ));
  84. node_creation_promises.push(ModelVerseConnector.get_output());
  85. }
  86. Promise.all(node_creation_promises).then( function(){
  87. let edge_creation_promises = [];
  88. for (const [key, node] of Object.entries(m.nodes)) {
  89. if (node.name == undefined || node.linktype != undefined) {
  90. let node_type = node.$type.split("/").slice(-1)[0];
  91. let node_name = null;
  92. if (node.name != undefined) {
  93. node_name = node.name.value;
  94. }else{
  95. node_name = node_type + key;
  96. }
  97. let src = null;
  98. let dest = null;
  99. for (const [edge_key, edge] of Object.entries(m.edges)) {
  100. if (edge['src'] == key) {
  101. let ed = edge['dest'];
  102. dest = ModelVerseConnector.element_map[ed];
  103. } else if (edge['dest'] == key) {
  104. let es = edge['src'];
  105. src = ModelVerseConnector.element_map[es];
  106. }
  107. }
  108. let set_id = function (id){
  109. ModelVerseConnector.element_map[key] = id.split(" ")[1].replace("\"", "");
  110. };
  111. edge_creation_promises.push(ModelVerseConnector.send_command(
  112. {"data": utils.jsons(["instantiate_edge", node_type, node_name, src, dest])}
  113. ));
  114. edge_creation_promises.push(ModelVerseConnector.get_output(set_id));
  115. }
  116. }
  117. Promise.all(edge_creation_promises).then(function() {
  118. let attrib_creation_promises = [];
  119. for (const [key, node] of Object.entries(m.nodes)) {
  120. let ele = ModelVerseConnector.element_map[key];
  121. let node_type = node.$type.split("/").slice(-1)[0];
  122. if (node.abstract != undefined && node.abstract.value){
  123. attrib_creation_promises.push(ModelVerseConnector.send_command(
  124. {"data": utils.jsons(["attr_add", ele, "abstract", true])}
  125. ));
  126. attrib_creation_promises.push(ModelVerseConnector.get_output());
  127. }
  128. if (node.name != undefined && node_type != "GlobalConstraint"){
  129. attrib_creation_promises.push(ModelVerseConnector.send_command(
  130. {"data": utils.jsons(["attr_add", ele, "name", node.name.value])}
  131. ));
  132. attrib_creation_promises.push(ModelVerseConnector.get_output());
  133. }
  134. //TODO: Cardinalities
  135. if (node.attributes != undefined) {
  136. console.log(node);
  137. for (const [key, attrib] of Object.entries(node.attributes.value)) {
  138. //console.log(attrib);
  139. let type = attrib.type[0].toUpperCase() + attrib.type.substring(1);
  140. if (type.startsWith("ENUM")){
  141. type = "ENUM";
  142. }
  143. type = type.split("<")[0];
  144. attrib_creation_promises.push(ModelVerseConnector.send_command(
  145. {"data": utils.jsons(["define_attribute", ele, attrib.name, type])}
  146. ));
  147. attrib_creation_promises.push(ModelVerseConnector.get_output());
  148. }
  149. }
  150. }
  151. ModelVerseConnector.set_status(ModelVerseConnector.OKAY);
  152. });
  153. });
  154. });
  155. }
  156. static load_MV_model(AS, CS) {
  157. return new Promise(
  158. function (resolve, reject) {
  159. console.log("Load MV Model");
  160. console.log(AS);
  161. console.log(CS);
  162. let metamodel = "Formalisms/__LanguageSyntax__/SimpleClassDiagram/SimpleClassDiagram.defaultIcons.metamodel";
  163. let class_type = "/Formalisms/__LanguageSyntax__/SimpleClassDiagram/SimpleClassDiagram.defaultIcons/ClassIcon";
  164. DataUtils.loadmm(metamodel,
  165. function(){
  166. console.log("Metamodel loaded: " + metamodel);
  167. });
  168. let model_classes = [];
  169. let model_associations = [];
  170. let model_inheris = [];
  171. let model_attribs = {};
  172. for (let i in AS) {
  173. let obj = AS[i];
  174. let obj_type = obj["__type"];
  175. if (obj_type == "Class") {
  176. model_classes.push(obj["__id"]);
  177. }else if (obj_type == "Association"){
  178. model_associations.push([obj["__source"], obj["__target"], obj["__id"]]);
  179. }else if (obj_type == "Inheritance"){
  180. model_inheris.push([obj["__source"], obj["__target"]]);
  181. }else if (obj_type == "AttributeLink") {
  182. if (model_attribs[obj["__source"]] == undefined){
  183. model_attribs[obj["__source"]] = [];
  184. }
  185. model_attribs[obj["__source"]].push([obj["__target"], obj["__id"]]);
  186. }
  187. }
  188. let class_locs = {};
  189. for (const cs_ele of CS){
  190. if (!(cs_ele["__type"] == "Group")){
  191. continue;
  192. }
  193. let asid = cs_ele["__asid"];
  194. let pos = [cs_ele["x"], cs_ele["y"]];
  195. class_locs[asid] = pos;
  196. }
  197. let ele_ids = {};
  198. __typeToCreate = class_type;
  199. let map_promises = [];
  200. for (const id of model_classes){
  201. map_promises.push(new Promise(function(resolve, reject){
  202. let updateClass =
  203. function(status, resp){
  204. let data = JSON.parse(resp);
  205. let uri = class_type + "/" + data["data"] + ".instance";
  206. ele_ids[id] = uri;
  207. let changes = {"name": id};
  208. if (model_attribs[id] != undefined){
  209. let attrib_changes = [];
  210. for (let attrib of model_attribs[id]){
  211. //console.log(attrib);
  212. let attrib_change = {
  213. "name": attrib[1],
  214. "type" : attrib[0]
  215. };
  216. attrib_changes.push(attrib_change);
  217. }
  218. changes["attributes"] = attrib_changes;
  219. }
  220. DataUtils.update(uri, changes);
  221. resolve();
  222. };
  223. let pos = class_locs[id];
  224. if (pos == undefined || pos == null){
  225. pos = [100, 100];
  226. }
  227. let vert_offset = 200;
  228. DataUtils.create(pos[0], pos[1] + vert_offset, updateClass);
  229. }));
  230. }
  231. Promise.all(map_promises).then(function(){
  232. for (const inheri of model_inheris){
  233. let connectionType = "/Formalisms/__LanguageSyntax__/SimpleClassDiagram/SimpleClassDiagram.defaultIcons/InheritanceLink.type";
  234. let source = ele_ids[inheri[0]];
  235. let target = ele_ids[inheri[1]];
  236. if (source == undefined || target == undefined){
  237. console.log("ERROR: Can't create inheritance between " + inheri[0] + " and " + inheri[1]);
  238. continue;
  239. }
  240. HttpUtils.httpReq(
  241. 'POST',
  242. HttpUtils.url(connectionType,__NO_USERNAME),
  243. {'src':source,
  244. 'dest':target,
  245. 'pos':undefined,
  246. 'segments':undefined});
  247. }
  248. })
  249. .then(function(){
  250. let assoc_create_promises = [];
  251. for (const assoc of model_associations){
  252. assoc_create_promises.push(new Promise(function(resolve, reject){
  253. let connectionType = "/Formalisms/__LanguageSyntax__/SimpleClassDiagram/SimpleClassDiagram.defaultIcons/AssociationLink.type";
  254. let source = ele_ids[assoc[0]];
  255. let target = ele_ids[assoc[1]];
  256. if (source == undefined || target == undefined){
  257. console.log("ERROR: Can't create association between " + assoc[0] + " and " + assoc[1]);
  258. resolve();
  259. }
  260. let assoc_create_callback = function(status, resp){
  261. let id = JSON.parse(resp)["data"];
  262. let assoc_id = connectionType.replace(".type", "/") + id + ".instance";
  263. ele_ids[assoc[2]] = assoc_id;
  264. resolve();
  265. };
  266. HttpUtils.httpReq(
  267. 'POST',
  268. HttpUtils.url(connectionType,__NO_USERNAME),
  269. {'src':source,
  270. 'dest':target,
  271. 'pos':undefined,
  272. 'segments':undefined},
  273. assoc_create_callback);
  274. }));
  275. }
  276. Promise.all(assoc_create_promises).then(function(){
  277. for (const assoc of model_associations){
  278. let uri = ele_ids[assoc[2]];
  279. let changes = {"name" : assoc[2]};
  280. console.log("Updating " + uri);
  281. DataUtils.update(uri, changes);
  282. }
  283. });
  284. });
  285. resolve();
  286. });
  287. }
  288. /*********COMMUNICATION FUNCTIONS**********/
  289. static send_command(param_dict) {
  290. return new Promise(
  291. function (resolve, reject) {
  292. let callback = function (status, resp) {
  293. if (utils.isHttpSuccessCode(status)) {
  294. //console.log("send_command Resolve: " + resp);
  295. resolve(resp);
  296. } else {
  297. console.log("send_command Reject: " + resp);
  298. reject(resp);
  299. }
  300. };
  301. if (!("op" in param_dict)) {
  302. param_dict["op"] = "set_input";
  303. }
  304. if (!("taskname" in param_dict)) {
  305. param_dict["taskname"] = ModelVerseConnector.taskname;
  306. }
  307. let params = "";
  308. for (const [key, value] of Object.entries(param_dict)) {
  309. params += key + "=" + value + "&";
  310. }
  311. //take off last &
  312. params = params.slice(0, -1);
  313. console.log("Sending: " + params);
  314. HttpUtils.httpReq("POST", ModelVerseConnector.address,
  315. params,
  316. callback
  317. );
  318. });
  319. }
  320. static get_output(clbk) {
  321. return new Promise(
  322. function (resolve, reject) {
  323. let callback = function (status, resp) {
  324. if (utils.isHttpSuccessCode(status)) {
  325. console.log("get_output Resolve: " + resp);
  326. if (clbk != undefined && typeof clbk == "function"){
  327. clbk(resp);
  328. }
  329. resolve(resp);
  330. } else {
  331. console.log("get_output reject: " + resp);
  332. reject(resp);
  333. }
  334. };
  335. let params = "op=get_output&taskname=" + ModelVerseConnector.taskname;
  336. HttpUtils.httpReq("POST", ModelVerseConnector.address,
  337. params,
  338. callback
  339. );
  340. }
  341. );
  342. }
  343. /*********END COMMUNICATION FUNCTIONS**********/
  344. /*********WRAPPER FUNCTIONS**********/
  345. static connect(username_param, password_param) {
  346. console.log("Connecting to: " + ModelVerseConnector.address);
  347. ModelVerseConnector.set_status(ModelVerseConnector.WORKING);
  348. let username = username_param || "admin";
  349. let password = password_param || "admin";
  350. let username_params = {
  351. "value": "\"" + username + "\""
  352. };
  353. let password_params = {
  354. "value": "\"" + password + "\""
  355. };
  356. let quiet_mode_params = {
  357. "value": "\"quiet\""
  358. };
  359. this.get_output().then(
  360. function(data){
  361. data = data.replace(/"/g, "");
  362. ModelVerseConnector.taskname = data;
  363. }
  364. )
  365. .then(() => this.send_command(username_params)).then(this.get_output)
  366. .then(() => this.send_command(password_params)).then(this.get_output)
  367. .then(() => this.send_command(quiet_mode_params)).then(this.get_output)
  368. .then(this.get_output)
  369. .then(function () {
  370. ModelVerseConnector.set_status(ModelVerseConnector.OKAY);
  371. })
  372. .catch(
  373. function () {
  374. WindowManagement.openDialog(_ERROR, 'failed to login to the ModelVerse!');
  375. ModelVerseConnector.set_status(ModelVerseConnector.ERROR);
  376. }
  377. );
  378. };
  379. //TODO: Cache this data if too slow
  380. static async get_files_in_folder(folder_name){
  381. return await ModelVerseConnector.model_list(folder_name);
  382. }
  383. static model_list(folder_name){
  384. return new Promise(function(resolve, reject) {
  385. console.log("Listing models in: '" + folder_name + "'");
  386. let folder_param = folder_name;
  387. //fix slashes on filename
  388. if (folder_param.endsWith("/")){
  389. folder_param = folder_param.slice(0, -1);
  390. }
  391. if (folder_param.startsWith("/")){
  392. folder_param = folder_param.slice(1);
  393. }
  394. let model_types = {
  395. "data": utils.jsons(["model_list", folder_param])
  396. };
  397. ModelVerseConnector.send_command(model_types).then(ModelVerseConnector.get_output)
  398. .then(function (data) {
  399. let files = [];
  400. data = data.replace("Success: ", "");
  401. let new_files = JSON.parse(data).split("\n");
  402. for (let i in new_files) {
  403. let file = new_files[i];
  404. files.push(folder_name + file);
  405. }
  406. files.sort();
  407. resolve(files);
  408. });
  409. });
  410. }
  411. static choose_model(status, model_to_save){
  412. console.log("Choosing model: ");
  413. if (status != undefined && status != 200){
  414. ModelVerseConnector.set_status(ModelVerseConnector.ERROR);
  415. return;
  416. };
  417. let loading_mode = (model_to_save == undefined);
  418. let folders = [""];
  419. let files = [];
  420. ModelVerseConnector.set_status(ModelVerseConnector.WORKING);
  421. // only exit on load
  422. if (ModelVerseConnector.curr_model && loading_mode){
  423. let command = {"data": utils.jsons(["exit"])};
  424. this.send_command(command).then(this.get_output)
  425. .then(function(data){
  426. ModelVerseConnector.curr_model = null;
  427. });
  428. }
  429. let startDir = "/";
  430. let fileb = FileBrowser.getFileBrowser(ModelVerseConnector.get_files_in_folder, false, !loading_mode, __getRecentDir(startDir));
  431. let feedback = GUIUtils.getTextSpan('', "feedback");
  432. let title = "ModelVerse Explorer";
  433. let callback = function (filenames) {
  434. if (loading_mode) {
  435. ModelVerseConnector.load_model(filenames[0]);
  436. }else{
  437. ModelVerseConnector.save_model(filenames[0], model_to_save);
  438. }
  439. };
  440. GUIUtils.setupAndShowDialog(
  441. [fileb['filebrowser'], null, null, feedback],
  442. function () {
  443. let value = [fileb['getselection']()];
  444. if (value.length > 0 && value[0] != "" && startDir) {
  445. __setRecentDir(startDir, value[0].substring(0, value[0].lastIndexOf('/') + 1));
  446. }
  447. return value;
  448. },
  449. __TWO_BUTTONS,
  450. title,
  451. callback);
  452. ModelVerseConnector.set_status(ModelVerseConnector.OKAY);
  453. }
  454. static load_model(model_name) {
  455. let metamodel = "formalisms/SimpleClassDiagrams";
  456. //fix slashes on filename
  457. // if (model_name.endsWith("/")){
  458. // model_name = model_name.slice(0, -1);
  459. // }
  460. if (model_name.startsWith("/")){
  461. model_name = model_name.slice(1);
  462. }
  463. console.log("Loading model: " + model_name);
  464. ModelVerseConnector.set_status(ModelVerseConnector.WORKING);
  465. ModelVerseConnector.curr_model = model_name;
  466. //get CS for model
  467. let SCD = "formalisms/SimpleClassDiagrams";
  468. let MM_render = "formalisms/SCD_graphical";
  469. let render_trans_model = "models/render_SCD";
  470. let render_MM = this.get_render_mm();
  471. let render_trans_code = this.get_render_trans();
  472. let render_model_add = {
  473. 'data': encodeURIComponent(utils.jsons(["model_add", SCD, MM_render, render_MM]))};
  474. let transformation_between = {
  475. 'data' : encodeURIComponent(utils.jsons(["transformation_add_AL", "rendered", MM_render, "abstract", SCD, "", "rendered", MM_render, "", render_trans_model]))
  476. };
  477. let transformation_data = {
  478. 'data' : encodeURIComponent(utils.jsons(["transformation_add_AL", render_trans_code]))
  479. };
  480. let model_rendered = {
  481. 'data' : encodeURIComponent(utils.jsons(["model_render", model_name, render_trans_model, "models/rendered_model"]))
  482. };
  483. //get AS for model
  484. let model_types = {
  485. "data": utils.jsons(["model_types", model_name])
  486. };
  487. let model_modify = {
  488. "data": utils.jsons(["model_modify", model_name, metamodel])
  489. };
  490. let model_dump = {
  491. "data": utils.jsons(["JSON"])
  492. };
  493. let model_CS = null;
  494. //CS COMMANDS
  495. //TODO: only need to add models if not present
  496. this.send_command(render_model_add).then(this.get_output)
  497. .then(this.send_command(transformation_between)).then(this.get_output)
  498. .then(this.send_command({})).then(this.get_output)
  499. .then(this.send_command(transformation_data)).then(this.get_output)
  500. .then(this.send_command(model_rendered)).then(this.get_output)
  501. .then(function(data){
  502. // console.log("Data before: ");
  503. // console.log(data);
  504. data = data.replace("Success: ", "");
  505. // console.log("Data after:");
  506. // console.log(data);
  507. model_CS = eval(JSON.parse(data));
  508. })
  509. //AS COMMANDS
  510. .then(this.send_command(model_types)).then(this.get_output)
  511. .then(function(data){
  512. console.log("model_types");
  513. console.log(data);
  514. })
  515. .then(this.send_command(model_modify)).then(this.get_output)
  516. .then(function(data){
  517. console.log("model_modify");
  518. console.log(data);
  519. })
  520. .then(this.send_command(model_dump)).then(this.get_output)
  521. .then(function(data){
  522. data = data.replace("Success: ", "");
  523. let AS = eval(JSON.parse(data));
  524. ModelVerseConnector.load_MV_model(AS, model_CS)
  525. })
  526. .then(function () {
  527. ModelVerseConnector.set_status(ModelVerseConnector.OKAY);
  528. })
  529. .catch(
  530. function (err) {
  531. console.log("Error with model loading!");
  532. console.log(err);
  533. ModelVerseConnector.set_status(ModelVerseConnector.ERROR);
  534. }
  535. );
  536. }
  537. /*********END WRAPPER FUNCTIONS**********/
  538. static get_render_mm(){
  539. let mm = "include \"primitives.alh\"\n" +
  540. "\n" +
  541. "SimpleAttribute Natural {}\n" +
  542. "SimpleAttribute String {}\n" +
  543. "SimpleAttribute Boolean {}\n" +
  544. "\n" +
  545. "Class GraphicalElement {\n" +
  546. " x : Natural\n" +
  547. " y : Natural\n" +
  548. " layer : Natural\n" +
  549. "}\n" +
  550. "\n" +
  551. "Class Group : GraphicalElement {\n" +
  552. " __asid : String\n" +
  553. " dirty : Boolean\n" +
  554. "}\n" +
  555. "\n" +
  556. "Association ConnectingLine (Group, Group) {\n" +
  557. " offsetSourceX : Natural\n" +
  558. " offsetSourceY : Natural\n" +
  559. " offsetTargetX : Natural\n" +
  560. " offsetTargetY : Natural\n" +
  561. " lineWidth : Natural\n" +
  562. " lineColour : String\n" +
  563. " arrow : Boolean\n" +
  564. " __asid : String\n" +
  565. " dirty : Boolean\n" +
  566. " layer : Natural\n" +
  567. "}\n" +
  568. "\n" +
  569. "Class LineElement : GraphicalElement {\n" +
  570. " lineWidth : Natural\n" +
  571. " lineColour : String\n" +
  572. "}\n" +
  573. "\n" +
  574. "Class Text : LineElement {\n" +
  575. " text : String\n" +
  576. "}\n" +
  577. "\n" +
  578. "Class Line : LineElement {\n" +
  579. " targetX : Natural\n" +
  580. " targetY : Natural\n" +
  581. " arrow : Boolean\n" +
  582. "}\n" +
  583. "\n" +
  584. "Class Shape : LineElement {\n" +
  585. " fillColour : String\n" +
  586. " width : Natural\n" +
  587. " height : Natural\n" +
  588. "}\n" +
  589. "\n" +
  590. "Class Figure : GraphicalElement {\n" +
  591. " width : Natural\n" +
  592. " height : Natural\n" +
  593. "}\n" +
  594. "\n" +
  595. "Class SVG {\n" +
  596. " data : String\n" +
  597. "}\n" +
  598. "\n" +
  599. "Class Rectangle : Shape {\n" +
  600. "}\n" +
  601. "\n" +
  602. "Class Ellipse : Shape {\n" +
  603. "}\n" +
  604. "\n" +
  605. "Association contains (Group, GraphicalElement) {}\n" +
  606. "Association renders (Figure, SVG) {\n" +
  607. " source_lower_cardinality = 1\n" +
  608. " target_lower_cardinality = 1\n" +
  609. " target_upper_cardinality = 1\n" +
  610. "}"
  611. ;
  612. return mm;
  613. }
  614. static get_render_trans(){
  615. let trans = "include \"primitives.alh\"\n" +
  616. "include \"modelling.alh\"\n" +
  617. "include \"object_operations.alh\"\n" +
  618. "include \"utils.alh\"\n" +
  619. "\n" +
  620. "Boolean function main(model : Element):\n" +
  621. "\tElement elements\n" +
  622. "\tString class\n" +
  623. "\tElement attrs\n" +
  624. "\tElement attr_keys\n" +
  625. "\tString attr_key\n" +
  626. "\tString group\n" +
  627. "\tString elem\n" +
  628. "\tInteger loc\n" +
  629. "\tInteger text_loc\n" +
  630. "\tElement related_groups\n" +
  631. "\tloc = 10\n" +
  632. "\n" +
  633. "\tElement groups\n" +
  634. "\tgroups = dict_create()\n" +
  635. "\n" +
  636. "\telements = allInstances(model, \"rendered/Group\")\n" +
  637. "\twhile (set_len(elements) > 0):\n" +
  638. "\t\tgroup = set_pop(elements)\n" +
  639. "\t\tif (set_len(allIncomingAssociationInstances(model, group, \"TracabilityClass\")) == 0):\n" +
  640. "\t\t\tElement to_remove\n" +
  641. "\t\t\tString elem_to_remove\n" +
  642. "\t\t\tto_remove = allAssociationDestinations(model, group, \"rendered/contains\")\n" +
  643. "\t\t\twhile (set_len(to_remove) > 0):\n" +
  644. "\t\t\t\telem_to_remove = set_pop(to_remove)\n" +
  645. "\t\t\t\tif (read_type(model, elem_to_remove) == \"rendered/Group\"):\n" +
  646. "\t\t\t\t\tset_add(to_remove, elem_to_remove)\n" +
  647. "\t\t\t\telse:\n" +
  648. "\t\t\t\t\tmodel_delete_element(model, elem_to_remove)\n" +
  649. "\t\t\tmodel_delete_element(model, group)\n" +
  650. "\n" +
  651. "\telements = allInstances(model, \"abstract/Class\")\n" +
  652. "\twhile (set_len(elements) > 0):\n" +
  653. "\t\tclass = set_pop(elements)\n" +
  654. "\t\t\n" +
  655. "\t\tInteger x\n" +
  656. "\t\tInteger y\n" +
  657. "\t\tx = loc\n" +
  658. "\t\ty = 10\n" +
  659. "\n" +
  660. "\t\t// Check if there is already an associated element\n" +
  661. "\t\tif (set_len(allOutgoingAssociationInstances(model, class, \"TracabilityClass\")) > 0):\n" +
  662. "\t\t\t// Yes, but is it still clean?\n" +
  663. "\t\t\tBoolean dirty\n" +
  664. "\t\t\tdirty = False\n" +
  665. "\n" +
  666. "\t\t\trelated_groups = allAssociationDestinations(model, class, \"TracabilityClass\")\n" +
  667. "\t\t\twhile (set_len(related_groups) > 0):\n" +
  668. "\t\t\t\tgroup = set_pop(related_groups)\n" +
  669. "\t\t\t\tif (value_eq(read_attribute(model, group, \"dirty\"), True)):\n" +
  670. "\t\t\t\t\t// No, so mark all as dirty\n" +
  671. "\t\t\t\t\tdirty = True\n" +
  672. "\t\t\t\t\tbreak!\n" +
  673. "\t\t\t\telse:\n" +
  674. "\t\t\t\t\t// Yes, so just ignore this!\n" +
  675. "\t\t\t\t\tcontinue!\n" +
  676. "\n" +
  677. "\t\t\tif (bool_not(dirty)):\n" +
  678. "\t\t\t\tdict_add(groups, class, group)\n" +
  679. "\t\t\t\tcontinue!\n" +
  680. "\t\t\telse:\n" +
  681. "\t\t\t\trelated_groups = allAssociationDestinations(model, class, \"TracabilityClass\")\n" +
  682. "\t\t\t\tElement to_remove\n" +
  683. "\t\t\t\tString elem_to_remove\n" +
  684. "\t\t\t\twhile (set_len(related_groups) > 0):\n" +
  685. "\t\t\t\t\tgroup = set_pop(related_groups)\n" +
  686. "\t\t\t\t\tto_remove = allAssociationDestinations(model, group, \"rendered/contains\")\n" +
  687. "\t\t\t\t\tx = create_value(read_attribute(model, group, \"x\"))\n" +
  688. "\t\t\t\t\ty = create_value(read_attribute(model, group, \"y\"))\n" +
  689. "\t\t\t\t\twhile (set_len(to_remove) > 0):\n" +
  690. "\t\t\t\t\t\telem_to_remove = set_pop(to_remove)\n" +
  691. "\t\t\t\t\t\tif (read_type(model, elem_to_remove) == \"rendered/Group\"):\n" +
  692. "\t\t\t\t\t\t\tset_add(to_remove, elem_to_remove)\n" +
  693. "\t\t\t\t\t\telse:\n" +
  694. "\t\t\t\t\t\t\tmodel_delete_element(model, elem_to_remove)\n" +
  695. "\t\t\t\t\tmodel_delete_element(model, group)\n" +
  696. "\n" +
  697. "\t\tattr_keys = dict_keys(getAttributeList(model, class))\n" +
  698. "\t\ttext_loc = 5\n" +
  699. "\n" +
  700. "\t\tgroup = instantiate_node(model, \"rendered/Group\", \"\")\n" +
  701. "\t\tinstantiate_attribute(model, group, \"x\", x)\n" +
  702. "\t\tinstantiate_attribute(model, group, \"y\", y)\n" +
  703. "\t\tinstantiate_attribute(model, group, \"__asid\", list_read(string_split_nr(class, \"/\", 1), 1))\n" +
  704. "\t\tinstantiate_attribute(model, group, \"layer\", 0)\n" +
  705. "\t\tdict_add(groups, class, group)\n" +
  706. "\t\tloc = loc + 200\n" +
  707. "\n" +
  708. "\t\telem = instantiate_node(model, \"rendered/Rectangle\", \"\")\n" +
  709. "\t\tinstantiate_attribute(model, elem, \"x\", 0)\n" +
  710. "\t\tinstantiate_attribute(model, elem, \"y\", 0)\n" +
  711. "\t\tinstantiate_attribute(model, elem, \"height\", 40 + set_len(getInstantiatableAttributes(model, class, \"abstract/AttributeLink\")) * 20)\n" +
  712. "\t\tinstantiate_attribute(model, elem, \"width\", 150)\n" +
  713. "\t\tinstantiate_attribute(model, elem, \"lineWidth\", 2) \n" +
  714. "\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
  715. "\t\tinstantiate_attribute(model, elem, \"fillColour\", \"white\")\n" +
  716. "\t\tinstantiate_attribute(model, elem, \"layer\", 1)\n" +
  717. "\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
  718. "\n" +
  719. "\t\tString multiplicities\n" +
  720. "\t\tString lower_card\n" +
  721. "\t\tString upper_card\n" +
  722. "\t\tif (element_eq(read_attribute(model, class, \"lower_cardinality\"), read_root())):\n" +
  723. "\t\t\tlower_card = \"*\"\n" +
  724. "\t\telse:\n" +
  725. "\t\t\tlower_card = cast_value(read_attribute(model, class, \"lower_cardinality\"))\n" +
  726. "\t\tif (element_eq(read_attribute(model, class, \"upper_cardinality\"), read_root())):\n" +
  727. "\t\t\tupper_card = \"*\"\n" +
  728. "\t\telse:\n" +
  729. "\t\t\tupper_card = cast_value(read_attribute(model, class, \"upper_cardinality\"))\n" +
  730. "\t\tmultiplicities = (((\"[\" + lower_card) + \"..\") + upper_card) + \"]\"\n" +
  731. "\n" +
  732. "\t\telem = instantiate_node(model, \"rendered/Text\", \"\")\n" +
  733. "\t\tinstantiate_attribute(model, elem, \"x\", 5)\n" +
  734. "\t\tinstantiate_attribute(model, elem, \"y\", 3)\n" +
  735. "\t\tinstantiate_attribute(model, elem, \"lineWidth\", 1)\n" +
  736. "\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
  737. "\t\tif (element_neq(read_attribute(model, class, \"name\"), read_root())):\n" +
  738. "\t\t\tinstantiate_attribute(model, elem, \"text\", string_join(read_attribute(model, class, \"name\"), \" \" + multiplicities))\n" +
  739. "\t\telse:\n" +
  740. "\t\t\tinstantiate_attribute(model, elem, \"text\", \"(unnamed) \" + multiplicities)\n" +
  741. "\t\tinstantiate_attribute(model, elem, \"layer\", 2)\n" +
  742. "\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
  743. "\n" +
  744. "\t\telem = instantiate_node(model, \"rendered/Line\", \"\")\n" +
  745. "\t\tinstantiate_attribute(model, elem, \"x\", 0)\n" +
  746. "\t\tinstantiate_attribute(model, elem, \"y\", 20)\n" +
  747. "\t\tinstantiate_attribute(model, elem, \"targetX\", 150)\n" +
  748. "\t\tinstantiate_attribute(model, elem, \"targetY\", 20)\n" +
  749. "\t\tinstantiate_attribute(model, elem, \"lineWidth\", 1)\n" +
  750. "\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
  751. "\t\tinstantiate_attribute(model, elem, \"arrow\", False)\n" +
  752. "\t\tinstantiate_attribute(model, elem, \"layer\", 2)\n" +
  753. "\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
  754. "\n" +
  755. "\t\tattrs = getInstantiatableAttributes(model, class, \"abstract/AttributeLink\")\n" +
  756. "\t\tattr_keys = dict_keys(attrs)\n" +
  757. "\t\twhile (dict_len(attr_keys) > 0):\n" +
  758. "\t\t\tattr_key = set_pop(attr_keys)\n" +
  759. "\t\t\telem = instantiate_node(model, \"rendered/Text\", \"\")\n" +
  760. "\t\t\tinstantiate_attribute(model, elem, \"x\", 5)\n" +
  761. "\t\t\tinstantiate_attribute(model, elem, \"y\", text_loc + 20)\n" +
  762. "\t\t\tinstantiate_attribute(model, elem, \"lineWidth\", 1)\n" +
  763. "\t\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
  764. "\t\t\tinstantiate_attribute(model, elem, \"text\", (attr_key + \" : \") + cast_string(list_read(string_split_nr(attrs[attr_key], \"/\", 1), 1)))\n" +
  765. "\t\t\tinstantiate_attribute(model, elem, \"layer\", 2)\n" +
  766. "\t\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
  767. "\t\t\ttext_loc = text_loc + 15\n" +
  768. "\n" +
  769. "\t\tinstantiate_link(model, \"TracabilityClass\", \"\", class, group)\n" +
  770. "\n" +
  771. "\t// Flush all associations\n" +
  772. "\telements = allInstances(model, \"rendered/ConnectingLine\")\n" +
  773. "\twhile (set_len(elements) > 0):\n" +
  774. "\t\tclass = set_pop(elements)\n" +
  775. "\t\tmodel_delete_element(model, class)\n" +
  776. "\n" +
  777. "\t// Rerender associations\n" +
  778. "\telements = allInstances(model, \"abstract/Association\")\n" +
  779. "\twhile (set_len(elements) > 0):\n" +
  780. "\t\tclass = set_pop(elements)\n" +
  781. "\n" +
  782. "\t\tattr_keys = dict_keys(getAttributeList(model, class))\n" +
  783. "\n" +
  784. "\t\telem = instantiate_link(model, \"rendered/ConnectingLine\", \"\", groups[readAssociationSource(model, class)], groups[readAssociationDestination(model, class)])\n" +
  785. "\t\tinstantiate_attribute(model, elem, \"offsetSourceX\", 75)\n" +
  786. "\t\tinstantiate_attribute(model, elem, \"offsetSourceY\", 30)\n" +
  787. "\t\tinstantiate_attribute(model, elem, \"offsetTargetX\", 75)\n" +
  788. "\t\tinstantiate_attribute(model, elem, \"offsetTargetY\", 30)\n" +
  789. "\t\tinstantiate_attribute(model, elem, \"lineWidth\", 1)\n" +
  790. "\t\tinstantiate_attribute(model, elem, \"lineColour\", \"black\")\n" +
  791. "\t\tinstantiate_attribute(model, elem, \"arrow\", True)\n" +
  792. "\t\tinstantiate_attribute(model, elem, \"__asid\", list_read(string_split_nr(class, \"/\", 1), 1))\n" +
  793. "\t\tinstantiate_attribute(model, elem, \"layer\", 0)\n" +
  794. "\t\tlog(\"Real ASID: \" + cast_value(class))\n" +
  795. "\t\tlog(\"Found ASID \" + cast_value(list_read(string_split_nr(class, \"/\", 1), 1)))\n" +
  796. "\t\tinstantiate_link(model, \"rendered/contains\", \"\", group, elem)\n" +
  797. "\n" +
  798. "\treturn True!";
  799. return trans;
  800. }
  801. }