libmt.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /*******************************************************************************
  2. AToMPM - A Tool for Multi-Paradigm Modelling
  3. Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
  4. This file is part of AToMPM.
  5. AToMPM is free software: you can redistribute it and/or modify it under the
  6. terms of the GNU Lesser General Public License as published by the Free Software
  7. Foundation, either version 3 of the License, or (at your option) any later
  8. version.
  9. AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  11. PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with AToMPM. If not, see <http://www.gnu.org/licenses/>.
  14. *******************************************************************************/
  15. {
  16. /* apply transformation T to specified model
  17. TBI:: this method only supports transforming class diagrams to ER diagrams
  18. and does so programmatically (vs. locating an appropriate [chain of]
  19. model transformations and running them) */
  20. 'transform' :
  21. function(_model,T)
  22. {
  23. var model = _utils.jsonp(_model),
  24. new_model = _utils.jsonp(_model),
  25. SCD = '/Formalisms/__LanguageSyntax__/SimpleClassDiagram/SimpleClassDiagram',
  26. ER = '/Formalisms/__LanguageSyntax__/EntityRelationship/EntityRelationship';
  27. if( T == 'SimpleClassDiagram-2-EntityRelationship' )
  28. {
  29. /* 1 map each type to all of its ancestors
  30. 2 flatten ancestor contents into their children + 'reroute' associations
  31. 3 make new_model conform to ER
  32. 4 append type-to-parentType mapping (needed for MT subtype matching) */
  33. function findAncestors(ids)
  34. {
  35. var inheritances = [];
  36. model.edges.forEach(
  37. function(edge)
  38. {
  39. if( _utils.contains(ids,edge['src']) &&
  40. model.nodes[edge['dest']]['$type'] == SCD+'/Inheritance' )
  41. inheritances.push(edge['dest']);
  42. });
  43. if( inheritances.length == 0 )
  44. return ids;
  45. var parents = [];
  46. model.edges.forEach(
  47. function(edge)
  48. {
  49. if( _utils.contains(inheritances,edge['src']) )
  50. parents.push(edge['dest']);
  51. });
  52. return ids.concat(findAncestors(parents));
  53. }
  54. var ids2ancestors = {};
  55. for( var id in model.nodes )
  56. {
  57. /* 1 */
  58. ids2ancestors[id] = findAncestors([id]).splice(1);
  59. /* 2 */
  60. /* a) apply attributes, constraints, actions and cardinalities inheritance to id
  61. b) update cardinalities of Associations who connect to parent + add edges between Associations
  62. and children... this effectively does association inheritance */
  63. ids2ancestors[id].forEach(
  64. function(a)
  65. {
  66. new_model.nodes[id]['attributes']['value'] =
  67. new_model.nodes[id]['attributes']['value'].concat( model.nodes[a]['attributes']['value'] );
  68. new_model.nodes[id]['constraints']['value'] =
  69. new_model.nodes[id]['constraints']['value'].concat( model.nodes[a]['constraints']['value'] );
  70. new_model.nodes[id]['actions']['value'] =
  71. new_model.nodes[id]['actions']['value'].concat( model.nodes[a]['actions']['value'] );
  72. new_model.nodes[id]['cardinalities']['value'] =
  73. new_model.nodes[id]['cardinalities']['value'].concat( model.nodes[a]['cardinalities']['value'] );
  74. var ancestorType = model.nodes[a]['name']['value'],
  75. idType = model.nodes[id]['name']['value'];
  76. model.edges.forEach(
  77. function(edge)
  78. {
  79. if( edge['src'] == a || edge['dest'] == a )
  80. {
  81. var ancestorIsSrc = (edge['src'] == a),
  82. assoc = (ancestorIsSrc ? edge['dest'] : edge['src']);
  83. if( model.nodes[assoc]['$type'] != SCD+'/Association' )
  84. return;
  85. for( var i in model.nodes[assoc]['cardinalities']['value'] )
  86. {
  87. var card = model.nodes[assoc]['cardinalities']['value'][i];
  88. if( card['type'] == ancestorType && card['dir'] == (ancestorIsSrc ? 'out' : 'in') )
  89. {
  90. var new_card = _utils.clone(card);
  91. new_card['type'] = idType;
  92. new_model.nodes[assoc]['cardinalities']['value'].push(new_card);
  93. }
  94. }
  95. new_model.edges.push(
  96. (ancestorIsSrc ? {'src':id,'dest':assoc} : {'src':assoc,'dest':id}) );
  97. }
  98. });
  99. });
  100. }
  101. /* 3 */
  102. /* due to extreme similarity between SCD and ER, we only need to
  103. a) update $type attributes
  104. special case: make abstract classes uninstantiable
  105. special case: remove inheritances
  106. b) update new_model.metamodels */
  107. for( var id in new_model.nodes )
  108. {
  109. if( new_model.nodes[id]['$type'] == SCD+'/Class' )
  110. {
  111. new_model.nodes[id]['$type'] = ER+'/Entity';
  112. if( new_model.nodes[id]['abstract']['value'] )
  113. {
  114. new_model.nodes[id]['constraints']['value'].push(
  115. {'name':'noAbstractInstances',
  116. 'event':'pre-create',
  117. 'code':'false'})
  118. }
  119. }
  120. else if( new_model.nodes[id]['$type'] == SCD+'/Association' )
  121. new_model.nodes[id]['$type'] = ER+'/Relationship';
  122. else if( new_model.nodes[id]['$type'] == SCD+'/GlobalConstraint' )
  123. new_model.nodes[id]['$type'] = ER+'/GlobalConstraint';
  124. else if( new_model.nodes[id]['$type'] == SCD+'/GlobalAction' )
  125. new_model.nodes[id]['$type'] = ER+'/GlobalAction';
  126. else if( new_model.nodes[id]['$type'] == SCD+'/Inheritance' )
  127. {
  128. /* special case: inheritance
  129. a) remove all edges pertaining to it
  130. b) remove it */
  131. new_model.edges = new_model.edges.filter(
  132. function(edge) {return edge['src'] != id && edge['dest'] != id});
  133. delete new_model.nodes[id];
  134. }
  135. }
  136. new_model.metamodels = [ER];
  137. /* 4 */
  138. var types2parentTypes = {};
  139. for( var id in new_model.nodes )
  140. {
  141. var type = new_model.nodes[id]['name']['value'];
  142. if( types2parentTypes[type] == undefined )
  143. {
  144. types2parentTypes[type] = []
  145. ids2ancestors[id].forEach(
  146. function(a)
  147. {
  148. types2parentTypes[type].push(new_model.nodes[a]['name']['value'])
  149. });
  150. }
  151. }
  152. new_model['types2parentTypes'] = types2parentTypes;
  153. /* return transformed model... note the little hack here... the reason for this is that pushing
  154. the 'same' attribute/constraint/action/cardiniality *objects* during the inheritance process
  155. causes problems when these are later edited (e.g., extended with targetType in compileToMM)
  156. since they all point to each other and changing one changes them all... */
  157. return _utils.clone(new_model);
  158. }
  159. else
  160. return {'$err':'unknown transformation :: '+T};
  161. },
  162. /* RAMify an AS metamodel and associated CS metamodel(s)
  163. 1. RELAX
  164. a) alter all constraints such that they are always satisfied (includes
  165. constraints that prevent instantiation of abstract types) + remember
  166. abstract types (for step 2diii)
  167. b) alter all actions such that they have no effect
  168. c) reduce all minimum multiplicities to 0
  169. 2. AUGMENT & MODIFY
  170. a) setup pattern types, constraints, actions, cardinalities, etc.
  171. b) replace types, constraints, etc. by results of steps a)
  172. c) insert post-create action that sets newly created, non-copied nodes'
  173. __pLabels to non-taken values
  174. d) alter each CS metamodel (i.e., icon definitions)
  175. i. use pattern type names
  176. ii. remove parsing and mapping functions (because pattern attribute
  177. values are code, as opposed to their 'original' types)
  178. iii. create basic icons for abstract types
  179. iv. add a Text VisualObject to show/edit the __pLabel attribute to
  180. every icon */
  181. 'ramify' :
  182. function(asmm,csmms)
  183. {
  184. var abstractTypes = [];
  185. for( var i in asmm.constraints )
  186. {
  187. if( asmm.constraints[i]['name'] == 'noAbstractInstances' )
  188. abstractTypes.push(asmm.constraints[i]['targetType']);
  189. asmm.constraints[i]['code'] =
  190. '/* comment next line to enable this constraint */\n'+
  191. 'throw "IgnoredConstraint"\n'+asmm.constraints[i]['code'];
  192. }
  193. for( var i in asmm.actions )
  194. asmm.actions[i]['code'] =
  195. '/* comment next line to enable this action */\n'+
  196. 'throw "IgnoredConstraint"\n'+asmm.actions[i]['code'];
  197. for( var t in asmm.cardinalities )
  198. for( var i in asmm.cardinalities[t] )
  199. asmm.cardinalities[t][i]['min'] = 0;
  200. var patternTypes = {},
  201. patternActions = [],
  202. patternCards = {},
  203. patternLegalConns = {},
  204. patternConnTypes = {},
  205. patternT2PT = {};
  206. for( var t in asmm.types )
  207. {
  208. patternTypes['__p'+t] =
  209. [{'name':'__pLabel', 'type':'string', 'default':''},
  210. {'name':'__pPivotIn', 'type':'string', 'default':''}, /* hergin motif-integration */
  211. {'name':'__pPivotOut', 'type':'string', 'default':''}, /* hergin motif-integration */
  212. {'name':'__pMatchSubtypes', 'type':'boolean', 'default':false}];
  213. for( var i in asmm.types[t] )
  214. patternTypes['__p'+t].push(
  215. {'name':asmm.types[t][i]['name'],
  216. 'type':'code',
  217. 'default':
  218. '"[PYTHON]"\n"Example:\t result = True"\n"Example:\t result = getAttr()"\n\n'+
  219. '"[JAVASCRIPT]"\n"Example:\t true"\n"Example:\t getAttr()"'});
  220. }
  221. for( var i in asmm.actions )
  222. {
  223. var action = asmm.actions[i];
  224. patternActions.push(_utils.clone(action));
  225. patternActions[patternActions.length-1]['targetType'] = '__p'+action['targetType'];
  226. }
  227. for( var t in asmm.cardinalities )
  228. {
  229. patternCards['__p'+t] = [];
  230. for( var i in asmm.cardinalities[t] )
  231. {
  232. var card = asmm.cardinalities[t][i];
  233. patternCards['__p'+t].push(_utils.clone(card));
  234. patternCards['__p'+t][i]['type'] = '__p'+card['type'];
  235. }
  236. }
  237. for( var t1 in asmm.legalConnections )
  238. {
  239. patternLegalConns['__p'+t1] = {};
  240. for( var t2 in asmm.legalConnections[t1] )
  241. {
  242. patternLegalConns['__p'+t1]['__p'+t2] = [];
  243. for( var i in asmm.legalConnections[t1][t2] )
  244. patternLegalConns['__p'+t1]['__p'+t2].push(
  245. '__p'+asmm.legalConnections[t1][t2][i]);
  246. }
  247. }
  248. for( var t in asmm.connectorTypes )
  249. patternConnTypes['__p'+t] = asmm.connectorTypes[t];
  250. for( var t in asmm.types2parentTypes )
  251. {
  252. patternT2PT['__p'+t] = [];
  253. for( var i in asmm.types2parentTypes[t] )
  254. patternT2PT['__p'+t].push('__p'+asmm.types2parentTypes[t][i]);
  255. }
  256. asmm.types = patternTypes;
  257. asmm.actions = patternActions;
  258. asmm.cardinalities = patternCards;
  259. asmm.legalConnections = patternLegalConns;
  260. asmm.connectorTypes = patternConnTypes;
  261. asmm.types2parentTypes = patternT2PT;
  262. patternActions.push(
  263. {'name': 'distinctPLabels',
  264. 'event': 'post-create',
  265. 'code': 'if( getAttr("__pLabel") == "" )\n'+
  266. '{\n'+
  267. ' var pLabels = getAllNodes().\n'+
  268. ' filter( function(n) {return hasAttr("__pLabel",n);} ).\n'+
  269. ' map( function(n) {return getAttr("__pLabel",n);} ),\n'+
  270. ' i = "0";\n'+
  271. '\n'+
  272. ' while( _utils.contains(pLabels,i) )\n'+
  273. ' i = String(parseInt(i)+1);\n'+
  274. ' setAttr("__pLabel",i);\n'+
  275. '}',
  276. 'targetType': '*'});
  277. function addPLabelTextVisualObject(nodes)
  278. {
  279. nodes['__pLabelText'] = {"position": {"type": "list<double>", "value": [0,0]},
  280. "orientation": {"type": "double", "value": 0},
  281. "scale": {"type": "list<double>", "value": [1,1]},
  282. "textContent": {"type": "string", "value": "#"},
  283. "style": {"type": "map<string,string>", "value": {"stroke": "#6000ff",
  284. "fill": "#6000ff",
  285. "font-size": "15px",
  286. "opacity": "1"}},
  287. "mapper": {"type": "code", "value": "({'textContent':getAttr('__pLabel')})"},
  288. "parser": {"type": "code", "value": "({'__pLabel':getAttr('textContent')})"},
  289. "$type": "/Formalisms/__LanguageSyntax__/ConcreteSyntax/ConcreteSyntax/Text"};
  290. }
  291. var NO_MAPPERS_PARSERS = '/* mapping and parsing code is disabled by default because pattern attribute values are code */';
  292. for( var csmm in csmms )
  293. {
  294. var patternTypes = {},
  295. patternCards = {},
  296. patternT2PT = {};
  297. csmms[csmm] = _utils.jsonp(csmms[csmm]);
  298. for( type in csmms[csmm].types )
  299. {
  300. var contents = undefined;
  301. patternTypes['__p'+type] = _utils.clone(csmms[csmm].types[type]);
  302. patternCards['__p'+type] = [];
  303. patternT2PT['__p'+type] = [];
  304. patternTypes['__p'+type].forEach(
  305. function(attr)
  306. {
  307. if( attr['name'] == '$contents' )
  308. contents = attr;
  309. else if( attr['name'] == 'parser' || attr['name'] == 'mapper' )
  310. attr['default'] = NO_MAPPERS_PARSERS;
  311. });
  312. for( id in contents['default']['nodes'] )
  313. for( attr in contents['default']['nodes'][id] )
  314. if( attr == 'parser' || attr == 'mapper' )
  315. contents['default']['nodes'][id][attr]['value'] = NO_MAPPERS_PARSERS;
  316. addPLabelTextVisualObject(contents['default']['nodes']);
  317. }
  318. abstractTypes.forEach(
  319. function(type)
  320. {
  321. patternCards['__p'+type+'Icon'] = [];
  322. patternT2PT['__p'+type+'Icon'] = [];
  323. patternTypes['__p'+type+'Icon'] = [{"name": "typename", "type": "string", "default": '__p'+type+'Icon'},
  324. {"name": "position", "type": "list<double>", "default": [0,0]},
  325. {"name": "orientation", "type": "double", "default": 0},
  326. {"name": "scale", "type": "list<double>", "default": [1,1]},
  327. {"name": "mapper", "type": "code", "default": ''},
  328. {"name": "parser", "type": "code", "default": ''},
  329. {"name": "$contents", "type": "map<string,*>", "default": {
  330. "nodes": {
  331. "text": {
  332. "textContent": {"type": "string", "value": '__p'+type+'Icon'},
  333. "style": {"type": "map<string,string>", "value": {"stroke": "#000000",
  334. "stroke-dasharray": "",
  335. "fill": "#000000",
  336. "fill-opacity": "1",
  337. "font-size": "13px"}},
  338. "mapper": {"type": "code", "value": ""},
  339. "parser": {"type": "code", "value": ""},
  340. "$type": "/Formalisms/__LanguageSyntax__/ConcreteSyntax/ConcreteSyntax/Text",
  341. "position": {"type": "list<double>", "value": [10,76]},
  342. "orientation": {"type": "double", "value": 0},
  343. "scale": {"type": "list<double>", "value": [1,1]}},
  344. "rect": {
  345. "width": {"type": "double", "value": 75},
  346. "height": {"type": "double", "value": 75},
  347. "cornerRadius": {"type": "double", "value": 15},
  348. "style": {"type": "map<string,string>", "value": {"stroke": "#000000",
  349. "fill": "#ffffff",
  350. "fill-opacity": 0.75}},
  351. "mapper": {"type": "code", "value": ""},
  352. "parser": {"type": "code", "value": ""},
  353. "$type": "/Formalisms/__LanguageSyntax__/ConcreteSyntax/ConcreteSyntax/Rectangle",
  354. "position": {"type": "list<double>", "value": [0,0]},
  355. "orientation": {"type": "double", "value": 0},
  356. "scale": {"type": "list<double>", "value": [1,1]}},
  357. "textBelowRect": {
  358. "distance": {"type": "double", "value": 10},
  359. "alignment": {"type": "ENUM(\"right\",\"left\",\"center\")", "value": "center"},
  360. "$type": "/Formalisms/__LanguageSyntax__/ConcreteSyntax/ConcreteSyntax/Below",
  361. "position": {"type": "list<double>", "value": [5,38]},
  362. "orientation": {"type": "double", "value": 0},
  363. "scale": {"type": "list<double>", "value": [1,1]},
  364. "link-style": {"type": "map<string,string>", "value": {"stroke": "#00ff00",
  365. "stroke-dasharray": "",
  366. "stroke-opacity": 1,
  367. "arrow-start": "none",
  368. "arrow-end": "classic-wide-long"}}}},
  369. "edges": [{"src": "text", "dest": "textBelowRect"},
  370. {"src": "textBelowRect", "dest": "rect"}]}},
  371. {"name": "$asuri", "type": "string", "default": "-1"}];
  372. addPLabelTextVisualObject(patternTypes['__p'+type+'Icon'][6]['default']['nodes']);
  373. });
  374. csmms[csmm].types = patternTypes;
  375. csmms[csmm].cardinalities = patternCards;
  376. csmms[csmm].types2parentTypes = patternT2PT;
  377. }
  378. return {'asmm':asmm,'csmms':csmms};
  379. }
  380. }