exportMM2Ecore.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  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=': '/exportMM2Ecore'}],
  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 xml code.
  30. var file_contents = '';
  31. //This variable will contain the identifier of the root.
  32. var rootNode = null;
  33. //This variable represent the abstract syntax. It contains all the information.
  34. var as = _utils.jsonp(asdata['data']);
  35. //This variable will contain a list of possible roots
  36. var listRoots = [];
  37. //This variable will contain all the classes to be created
  38. var listClasses = [];
  39. //This variable will contain all the enumerations to be created
  40. var listEnums = [];
  41. /**
  42. We create the package that will contain all the classes. The name of the package
  43. is the name of the metamodel, aside of the MM, if apply.
  44. The URI of the metamodel is asked to the user.
  45. **/
  46. var packageName = reqData['name'];
  47. var nsURI = reqData['uri'];
  48. if (reqData['name'].endsWith("MM"))
  49. packageName = packageName.substr(0, packageName.length - 2);
  50. /**
  51. This object maps how the type is written. For example, in AToMPM it's "string",
  52. while in Ecore it's "EString"
  53. **/
  54. var dataType = {
  55. 'string': 'EString',
  56. 'int': 'EInt',
  57. 'float': 'EFloat',
  58. 'boolean': 'EBoolean',
  59. 'code': 'EString'
  60. };
  61. /**
  62. This function removes an element of an array.
  63. **/
  64. function remove(array, elem) {
  65. var index = array.indexOf(elem);
  66. if (index !== -1) {
  67. array.splice(index, 1);
  68. }
  69. }
  70. /**
  71. This function returns true if the element is in the array.
  72. **/
  73. function inside(array, elem) {
  74. for (var i = 0; i < array.length; i++) {
  75. if (array[i] == elem)
  76. return true;
  77. }
  78. return false;
  79. }
  80. /**
  81. This function will search if the class, specified by its identifier (nodeIdentifier), is a
  82. subclass of another one, i.e. if it inherits from another class.
  83. It will return the name of the superclass, if it exists.
  84. If the class has multiple inheritance, it will return the last one that was found since ecore
  85. doesn't allow multiple inheritance.
  86. **/
  87. function findSuperClass(nodeIdentifier) {
  88. var superclassName = null;
  89. var superclassKey = null;
  90. for (var edge in as.edges) {
  91. if (as.edges[edge].src == nodeIdentifier) {
  92. var linkKey = as.edges[edge].dest;
  93. if (isInheritanceLink(linkKey))
  94. superclassKey = linkDestination(linkKey);
  95. }
  96. }
  97. if (superclassKey != null)
  98. superclassName = as.nodes[superclassKey].name.value;
  99. return superclassName;
  100. }
  101. /**
  102. This function will return the identifier of the destination of a link,
  103. specified by its identifier.
  104. **/
  105. function linkDestination(linkIdentifier) {
  106. var cleDest = null;
  107. for (var dest in as.edges) {
  108. if (as.edges[dest].src == linkIdentifier)
  109. cleDest = as.edges[dest].dest;
  110. }
  111. return cleDest;
  112. }
  113. /**
  114. This function will return true if the class is an inheritance link.
  115. It will return false otherwise.
  116. **/
  117. function isInheritanceLink(nodeIdentifier) {
  118. return as.nodes[nodeIdentifier]["$type"].endsWith("Inheritance");
  119. }
  120. /**
  121. This function will return true if the class is an association link.
  122. It will return false otherwise.
  123. **/
  124. function isAssociationLink(nodeIdentifier) {
  125. return as.nodes[nodeIdentifier]["$type"].endsWith("Association");
  126. }
  127. /**
  128. This function will return true if the class is an actual class.
  129. It will return false otherwise.
  130. **/
  131. function isClass(nodeIdentifier) {
  132. return as.nodes[nodeIdentifier]["$type"].endsWith("Class");
  133. }
  134. /**
  135. This function searches if a root exists. A root is a class from which all the other
  136. classes are accessible.
  137. The process :
  138. - find all the classes;
  139. - eliminate all the classes that are the end of a link, that means they are accessible;
  140. - if listNodes contains only an element, that is the root. Else, there is no root.
  141. **/
  142. function hasRoot() {
  143. var listNodes = [];
  144. for (var key in as.nodes) {
  145. if (isClass(key))
  146. listNodes.push(key);
  147. }
  148. for (var i = 0; i < as.edges.length; i += 2) {
  149. if (as.edges[i].src != as.edges[i + 1].dest)
  150. remove(listNodes, as.edges[i + 1].dest);
  151. }
  152. return listNodes;
  153. }
  154. /**
  155. This function returns the root. If none was found, it will create one.
  156. **/
  157. function setRoot() {
  158. listRoots = hasRoot();
  159. if (listRoots.length == 1) {
  160. rootNode = listRoots[0];
  161. reformRootClass(rootNode);
  162. }
  163. else
  164. createRootClass();
  165. }
  166. /**
  167. This function creates a root and assigns all the possible roots as references.
  168. **/
  169. function createRootClass() {
  170. var root = {};
  171. root.name = packageName + 'Root';
  172. var refers = [];
  173. for (var i = 0; i < listRoots.length; i++) {
  174. reformRootClass(listRoots[i]);
  175. var refType = as.nodes[listRoots[i]].name.value;
  176. var refName = refType + 'Link';
  177. var ref = createEReference(refName, "containment", refType, [1, -1]);
  178. refers.push(ref);
  179. }
  180. root.references = refers;
  181. root.attributes = [];
  182. listClasses.push(root);
  183. }
  184. /**
  185. This function change all the linkType property of each reference of
  186. the node to containment. The node is the root or a possible one.
  187. **/
  188. function reformRootClass(rootNode) {
  189. var rootClass = createEClass(rootNode);
  190. for (var i = 0; i < rootClass.references.length; i++)
  191. rootClass.references[i].linkType = "containment";
  192. listClasses.push(rootClass);
  193. }
  194. /**
  195. This function creates an EClassifier.
  196. Input: the identifier of the node
  197. Output: an object with all the needed properties
  198. **/
  199. function createEClass(nodeIdentifier) {
  200. var eClass = {};
  201. var node = as.nodes[nodeIdentifier];
  202. eClass.name = node.name.value;
  203. eClass.abstract = node.abstract.value;
  204. eClass.superclass = findSuperClass(nodeIdentifier);
  205. eClass.references = findReferences(nodeIdentifier);
  206. eClass.attributes = findAttributes(eClass, nodeIdentifier);
  207. return eClass;
  208. }
  209. /**
  210. This function creates an EReference.
  211. Input: the name, linktype, type and bounds of the reference
  212. Output: an object with all the needed properties
  213. **/
  214. function createEReference(name, linktype, type, bounds) {
  215. var eReference = {};
  216. eReference.name = name;
  217. eReference.linkType = linktype; //containment or visual
  218. if (bounds != null) {
  219. if (bounds[0] != 0)
  220. eReference.lowerBound = bounds[0];
  221. if (bounds[1] == 'inf')
  222. eReference.upperBound = -1;
  223. else if (bounds[1] != 1) //else we have the value 1 by default in ecore
  224. eReference.upperBound = bounds[1];
  225. }
  226. eReference.type = type; //eType, type of the destination class
  227. return eReference;
  228. }
  229. /**
  230. This function create an EAttribute.
  231. Input: the name, datatype, default value of the attribute and the booleans eenum, map and list
  232. Output: an object with all the needed properties
  233. **/
  234. function createEAttribute(name, datatype, defaultValue, eenum, list, map) {
  235. var eAttribute = {};
  236. eAttribute.name = name;
  237. eAttribute.complexType = (eenum || list || map);
  238. eAttribute.list = list;
  239. eAttribute.eenum = eenum;
  240. eAttribute.map = map;
  241. if (!eAttribute.complexType) {
  242. eAttribute.dataType = dataType[datatype];
  243. if (defaultValue != '')
  244. eAttribute.defaultValue = defaultValue;
  245. }
  246. else
  247. setDataType(eAttribute, datatype, eenum, list, map, defaultValue);
  248. return eAttribute;
  249. }
  250. /**
  251. This function is to assign the datatype and related variables to an attribute, but
  252. just for complex types, i.e. list or ENUM.
  253. **/
  254. function setDataType(eAttribute, datatype, eenum, list, map, defaultValue) {
  255. if (list)
  256. setListType(eAttribute, datatype);
  257. else if (eenum)
  258. setEnumType(eAttribute, datatype, defaultValue);
  259. else if (map)
  260. setMapType(eAttribute, datatype, defaultValue);
  261. }
  262. /**
  263. This function sets the default value and matches the datatype of a list attribute.
  264. The matching types are contained in the object dataType.
  265. **/
  266. function setListType(eAttribute, datatype) {
  267. var listType = datatype.split('<')[1].split('>')[0];
  268. listType = dataType[listType];
  269. eAttribute.dataType = listType;
  270. }
  271. /**
  272. This function sets the datatype (and the default value?) of an enumeration attribute.
  273. **/
  274. function setEnumType(eAttribute, datatype, defaultValue) {
  275. var name = eAttribute.name;
  276. var nameEnum = name.charAt(0).toUpperCase() + name.slice(1);
  277. eAttribute.dataType = nameEnum;
  278. eAttribute.defaultValue = defaultValue;
  279. createEnumClass(nameEnum, datatype);
  280. }
  281. /**
  282. This function creates an Enum class related to an enumeration attribute.
  283. **/
  284. function createEnumClass(nameEnum, datatype) {
  285. var eEnum = {};
  286. eEnum.name = nameEnum;
  287. var options = datatype.split('(')[1].split(')')[0];
  288. options = options.split(', ');
  289. eEnum.options = options;
  290. listEnums.push(eEnum);
  291. }
  292. /**
  293. This function sets the datatype (and the default value?) of a map attribute.
  294. **/
  295. function setMapType(eAttribute, datatype, defaultValue) {
  296. //var keys = datatype.split('[')[1].split(']')[0];
  297. //keys = keys.split(',');
  298. var values = datatype.split(']')[1].split('[')[1].split(']')[0];
  299. values = values.split(',');
  300. //for(var i = 0; i < keys.length; i++)
  301. // createMapClass(keys[i], values[i]);
  302. }
  303. /**
  304. This function finds all the attributes of a class.
  305. Input: the class identifier
  306. Output: a list of all the attributes of the class
  307. **/
  308. function findAttributes(classe, nodeIdentifier) {
  309. var attributesList = [];
  310. var node = as.nodes[nodeIdentifier].attributes['value'];
  311. for (var attr in node) {
  312. var name = node[attr].name;
  313. var defaultValue = node[attr]['default'];
  314. var datatype = node[attr].type;
  315. var eenum = false;
  316. var list = false;
  317. var map = false;
  318. if (datatype.startsWith("list"))
  319. list = true;
  320. else if (datatype.startsWith("ENUM"))
  321. eenum = true;
  322. else if (datatype.startsWith("map"))
  323. map = true;
  324. var attribute = createEAttribute(name, datatype, defaultValue, eenum, list, map);
  325. attributesList.push(attribute);
  326. }
  327. return attributesList;
  328. }
  329. /**
  330. This function finds the cardinalities of a reference.
  331. Input: the class identifier (nodeIdentifier) and the reference name
  332. Output: a list of bounds (min and max)
  333. **/
  334. function findCardinalities(nodeIdentifier, referenceName) {
  335. var bounds = [];
  336. var cardinal = as.nodes[nodeIdentifier].cardinalities['value'];
  337. for (var car in cardinal) {
  338. if (cardinal[car].type == referenceName) {
  339. bounds.push(cardinal[car].min);
  340. bounds.push(cardinal[car].max);
  341. }
  342. }
  343. return bounds;
  344. }
  345. /**
  346. This function finds all the references of a class.
  347. Input: the class identifier
  348. Output: a list of all the references of the class
  349. **/
  350. function findReferences(nodeIdentifier) {
  351. var referencesList = [];
  352. for (var edge in as.edges) {
  353. if (as.edges[edge].src == nodeIdentifier) {
  354. var linkKey = as.edges[edge].dest;
  355. if (isAssociationLink(linkKey)) {
  356. var name = as.nodes[linkKey].name.value;
  357. var linktype = as.nodes[linkKey].linktype['value'];
  358. if (nodeIdentifier == rootNode || inside(listRoots, nodeIdentifier))
  359. linktype = "containment";
  360. var bounds = findCardinalities(nodeIdentifier, name);
  361. var dest = linkDestination(linkKey);
  362. var type = as.nodes[dest].name.value;
  363. var reference = createEReference(name, linktype, type, bounds);
  364. referencesList.push(reference);
  365. }
  366. }
  367. }
  368. return referencesList;
  369. }
  370. /**
  371. This function returns what's to be written in the file in the beginning, including the
  372. package's name and the URI.
  373. **/
  374. function writeHeader() {
  375. var header = '<?xml version="1.0" encoding="UTF-8"?> \n';
  376. header += '<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \n';
  377. header += ' xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="' + packageName;
  378. header += '" nsURI="' + nsURI + '" nsPrefix="' + packageName + '"> \n';
  379. return header;
  380. }
  381. /**
  382. This function returns what's to be written in the file in the case of an EClass.
  383. **/
  384. function writeEClassifier(classe) {
  385. var classLine = ' <eClassifiers xsi:type="ecore:EClass" name="' + classe.name;
  386. if (classe.abstract)
  387. classLine += '" abstract="true';
  388. if (classe.superclass != null)
  389. classLine += '" eSuperTypes="#//' + classe.superclass;
  390. if (classe.attributes.length > 0 || classe.references.length > 0) {
  391. classLine += '"> \n';
  392. for (var i = 0; i < classe.attributes.length; i++)
  393. classLine += writeEAttribute(classe.attributes[i]);
  394. for (var i = 0; i < classe.references.length; i++)
  395. classLine += writeEReference(classe.references[i]);
  396. classLine += ' </eClassifiers> \n';
  397. }
  398. else
  399. classLine += '"/> \n';
  400. return classLine;
  401. }
  402. /**
  403. This function returns what's to be written in the file in the case of an EAttribute.
  404. **/
  405. function writeEAttribute(attr) {
  406. var attrLine = ' <eStructuralFeatures xsi:type="ecore:EAttribute" name="' + attr.name;
  407. if (attr.map)
  408. attrLine += writeMapAttribute(attr);
  409. else {
  410. if (attr.list)
  411. attrLine += '" upperBound="-1';
  412. if (attr.list || !(attr.complexType))
  413. attrLine += '" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//' + attr.dataType;
  414. if (attr.eenum)
  415. attrLine += '" eType="#//' + attr.dataType;
  416. if (attr.defaultValue != null)
  417. attrLine += '" defaultValueLiteral="' + attr.defaultValue;
  418. attrLine += '"/> \n';
  419. }
  420. return attrLine;
  421. }
  422. /**
  423. This function returns what's to be written in a Map attribute.
  424. **/
  425. function writeMapAttribute(attr) {
  426. var mapAttr = '';
  427. mapAttr += '" upperBound="-1" transient="true"> \n';
  428. mapAttr += writeEGenericType("EMap");
  429. mapAttr += ' </eStructuralFeatures> \n';
  430. return mapAttr;
  431. }
  432. /**
  433. This function returns what's to be written in a GenericType.
  434. **/
  435. function writeEGenericType(datatype) {
  436. var gentype = ' <eGenericType eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//';
  437. gentype += datatype + '"> \n';
  438. gentype += writeETypeArguments(["EEList", "EString"]);
  439. gentype += writeETypeArguments(["EEList", "EDataType"]);
  440. gentype += ' </eGenericType> \n';
  441. return gentype;
  442. }
  443. /**
  444. This function returns what's to be written in a ETypeArguments.
  445. **/
  446. function writeETypeArguments(types) {
  447. var typeArg = ' <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//';
  448. typeArg += types[0] + '"> \n';
  449. typeArg += writeSingleETypeArguments(types[1]);
  450. typeArg += ' </eTypeArguments> \n';
  451. return typeArg;
  452. }
  453. /**
  454. This function returns what's to be written in a ETypeArguments.
  455. **/
  456. function writeSingleETypeArguments(type) {
  457. var singleTypeArg = ' <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//';
  458. singleTypeArg += type + '"/> \n';
  459. return singleTypeArg;
  460. }
  461. /**
  462. This function returns what's to be written in the file in the case of an EReference.
  463. **/
  464. function writeEReference(ref) {
  465. var refLine = ' <eStructuralFeatures xsi:type="ecore:EReference" name="' + ref.name;
  466. if (ref.lowerBound != null)
  467. refLine += '" lowerBound="' + ref.lowerBound;
  468. if (ref.upperBound != null)
  469. refLine += '" upperBound="' + ref.upperBound;
  470. refLine += '" eType="#//' + ref.type;
  471. if (ref.linkType == "containment")
  472. refLine += '" containment="true';
  473. refLine += '"/> \n';
  474. return refLine;
  475. }
  476. /**
  477. This function returns what's to be written in the file in the case of an EEnum.
  478. **/
  479. function writeEEnum(enume) {
  480. var enumLine = ' <eClassifiers xsi:type="ecore:EEnum" name="' + enume.name + '"> \n';
  481. for (var i = 0; i < enume.options.length; i++)
  482. enumLine += ' <eLiterals name="' + enume.options[i] + '"/> \n';
  483. enumLine += ' </eClassifiers> \n';
  484. return enumLine;
  485. }
  486. /**
  487. This function creates the classes then put them in the variable listClasses.
  488. **/
  489. function populateListClasses() {
  490. for (var node in as.nodes) {
  491. if (!(inside(listRoots, node)) && isClass(node)) {
  492. var eclass = createEClass(node);
  493. listClasses.push(eclass);
  494. }
  495. }
  496. }
  497. /**
  498. This function will write everything (classes, attributes, references, enumerations)
  499. in a string.
  500. **/
  501. function writeFile() {
  502. file_contents += writeHeader();
  503. for (var i = 0; i < listClasses.length; i++)
  504. file_contents += writeEClassifier(listClasses[i]);
  505. for (var i = 0; i < listEnums.length; i++)
  506. file_contents += writeEEnum(listEnums[i]);
  507. file_contents += '</ecore:EPackage>';
  508. }
  509. /****************************************************************************************************************************************************************
  510. The following is like the main class of this file. It will call the appropriate functions
  511. in order to create the export file.
  512. ****************************************************************************************************************************************************************/
  513. //creation of root and, if apply, possible roots
  514. setRoot();
  515. //creation of classes, including their attributes and references
  516. populateListClasses();
  517. //write the file
  518. writeFile();
  519. _fs.writeFileSync('./exported_to_ecore/' + packageName + 'Metamodel.ecore', file_contents);
  520. __postMessage({
  521. 'statusCode': 200,
  522. 'respIndex': resp
  523. });
  524. },
  525. function (writeErr) {
  526. __postInternalErrorMsg(resp, writeErr);
  527. }
  528. );
  529. },
  530. function (err) {
  531. __postInternalErrorMsg(resp, err);
  532. }
  533. );
  534. }
  535. ,
  536. 'asworker'
  537. :
  538. function (resp, method, uri, reqData, wcontext) {
  539. __postMessage(
  540. {
  541. 'statusCode': 200,
  542. 'respIndex': resp
  543. });
  544. }
  545. };