123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- const {
- __errorContinuable,
- __httpReq,
- __wHttpReq,
- __postInternalErrorMsg, __postMessage,
- __sequenceNumber,
- __successContinuable,
- __uri_to_id
- } = require("../__worker");
- const _do = require("../___do");
- const _utils = require('../utils');
- const _mmmk = require("../mmmk");
- const _fs = _do.convert(require('fs'), ['readFile', 'writeFile', 'readdir']);
- const _fspp = _do.convert(require('../___fs++'), ['mkdirs']);
- module.exports = {
- 'interfaces'
- :
- [{'method': 'POST', 'url=': '/exportMM2Ecore'}],
- 'csworker'
- :
- function (resp, method, uri, reqData, wcontext) {
- var actions = [__wHttpReq('GET', '/current.model?wid=' + wcontext.__aswid)];
- _do.chain(actions)(
- function (asdata) {
- //The generated file will be in the "exported_to_ecore" folder
- var writeActions = [_fspp.mkdirs('./exported_to_ecore/')];
- _do.chain(writeActions)(
- function () {
- //This variable will contain the lines of the generated files, i.e. the xml code.
- var file_contents = '';
- //This variable will contain the identifier of the root.
- var rootNode = null;
- //This variable represent the abstract syntax. It contains all the information.
- var as = _utils.jsonp(asdata['data']);
- //This variable will contain a list of possible roots
- var listRoots = [];
- //This variable will contain all the classes to be created
- var listClasses = [];
- //This variable will contain all the enumerations to be created
- var listEnums = [];
- /**
- We create the package that will contain all the classes. The name of the package
- is the name of the metamodel, aside of the MM, if apply.
- The URI of the metamodel is asked to the user.
- **/
- var packageName = reqData['name'];
- var nsURI = reqData['uri'];
- if (reqData['name'].endsWith("MM"))
- packageName = packageName.substr(0, packageName.length - 2);
- /**
- This object maps how the type is written. For example, in AToMPM it's "string",
- while in Ecore it's "EString"
- **/
- var dataType = {
- 'string': 'EString',
- 'int': 'EInt',
- 'float': 'EFloat',
- 'boolean': 'EBoolean',
- 'code': 'EString'
- };
- /**
- This function removes an element of an array.
- **/
- function remove(array, elem) {
- var index = array.indexOf(elem);
- if (index !== -1) {
- array.splice(index, 1);
- }
- }
- /**
- This function returns true if the element is in the array.
- **/
- function inside(array, elem) {
- for (var i = 0; i < array.length; i++) {
- if (array[i] == elem)
- return true;
- }
- return false;
- }
- /**
- This function will search if the class, specified by its identifier (nodeIdentifier), is a
- subclass of another one, i.e. if it inherits from another class.
- It will return the name of the superclass, if it exists.
- If the class has multiple inheritance, it will return the last one that was found since ecore
- doesn't allow multiple inheritance.
- **/
- function findSuperClass(nodeIdentifier) {
- var superclassName = null;
- var superclassKey = null;
- for (var edge in as.edges) {
- if (as.edges[edge].src == nodeIdentifier) {
- var linkKey = as.edges[edge].dest;
- if (isInheritanceLink(linkKey))
- superclassKey = linkDestination(linkKey);
- }
- }
- if (superclassKey != null)
- superclassName = as.nodes[superclassKey].name.value;
- return superclassName;
- }
- /**
- This function will return the identifier of the destination of a link,
- specified by its identifier.
- **/
- function linkDestination(linkIdentifier) {
- var cleDest = null;
- for (var dest in as.edges) {
- if (as.edges[dest].src == linkIdentifier)
- cleDest = as.edges[dest].dest;
- }
- return cleDest;
- }
- /**
- This function will return true if the class is an inheritance link.
- It will return false otherwise.
- **/
- function isInheritanceLink(nodeIdentifier) {
- return as.nodes[nodeIdentifier]["$type"].endsWith("Inheritance");
- }
- /**
- This function will return true if the class is an association link.
- It will return false otherwise.
- **/
- function isAssociationLink(nodeIdentifier) {
- return as.nodes[nodeIdentifier]["$type"].endsWith("Association");
- }
- /**
- This function will return true if the class is an actual class.
- It will return false otherwise.
- **/
- function isClass(nodeIdentifier) {
- return as.nodes[nodeIdentifier]["$type"].endsWith("Class");
- }
- /**
- This function searches if a root exists. A root is a class from which all the other
- classes are accessible.
- The process :
- - find all the classes;
- - eliminate all the classes that are the end of a link, that means they are accessible;
- - if listNodes contains only an element, that is the root. Else, there is no root.
- **/
- function hasRoot() {
- var listNodes = [];
- for (var key in as.nodes) {
- if (isClass(key))
- listNodes.push(key);
- }
- for (var i = 0; i < as.edges.length; i += 2) {
- if (as.edges[i].src != as.edges[i + 1].dest)
- remove(listNodes, as.edges[i + 1].dest);
- }
- return listNodes;
- }
- /**
- This function returns the root. If none was found, it will create one.
- **/
- function setRoot() {
- listRoots = hasRoot();
- if (listRoots.length == 1) {
- rootNode = listRoots[0];
- reformRootClass(rootNode);
- }
- else
- createRootClass();
- }
- /**
- This function creates a root and assigns all the possible roots as references.
- **/
- function createRootClass() {
- var root = {};
- root.name = packageName + 'Root';
- var refers = [];
- for (var i = 0; i < listRoots.length; i++) {
- reformRootClass(listRoots[i]);
- var refType = as.nodes[listRoots[i]].name.value;
- var refName = refType + 'Link';
- var ref = createEReference(refName, "containment", refType, [1, -1]);
- refers.push(ref);
- }
- root.references = refers;
- root.attributes = [];
- listClasses.push(root);
- }
- /**
- This function change all the linkType property of each reference of
- the node to containment. The node is the root or a possible one.
- **/
- function reformRootClass(rootNode) {
- var rootClass = createEClass(rootNode);
- for (var i = 0; i < rootClass.references.length; i++)
- rootClass.references[i].linkType = "containment";
- listClasses.push(rootClass);
- }
- /**
- This function creates an EClassifier.
- Input: the identifier of the node
- Output: an object with all the needed properties
- **/
- function createEClass(nodeIdentifier) {
- var eClass = {};
- var node = as.nodes[nodeIdentifier];
- eClass.name = node.name.value;
- eClass.abstract = node.abstract.value;
- eClass.superclass = findSuperClass(nodeIdentifier);
- eClass.references = findReferences(nodeIdentifier);
- eClass.attributes = findAttributes(eClass, nodeIdentifier);
- return eClass;
- }
- /**
- This function creates an EReference.
- Input: the name, linktype, type and bounds of the reference
- Output: an object with all the needed properties
- **/
- function createEReference(name, linktype, type, bounds) {
- var eReference = {};
- eReference.name = name;
- eReference.linkType = linktype; //containment or visual
- if (bounds != null) {
- if (bounds[0] != 0)
- eReference.lowerBound = bounds[0];
- if (bounds[1] == 'inf')
- eReference.upperBound = -1;
- else if (bounds[1] != 1) //else we have the value 1 by default in ecore
- eReference.upperBound = bounds[1];
- }
- eReference.type = type; //eType, type of the destination class
- return eReference;
- }
- /**
- This function create an EAttribute.
- Input: the name, datatype, default value of the attribute and the booleans eenum, map and list
- Output: an object with all the needed properties
- **/
- function createEAttribute(name, datatype, defaultValue, eenum, list, map) {
- var eAttribute = {};
- eAttribute.name = name;
- eAttribute.complexType = (eenum || list || map);
- eAttribute.list = list;
- eAttribute.eenum = eenum;
- eAttribute.map = map;
- if (!eAttribute.complexType) {
- eAttribute.dataType = dataType[datatype];
- if (defaultValue != '')
- eAttribute.defaultValue = defaultValue;
- }
- else
- setDataType(eAttribute, datatype, eenum, list, map, defaultValue);
- return eAttribute;
- }
- /**
- This function is to assign the datatype and related variables to an attribute, but
- just for complex types, i.e. list or ENUM.
- **/
- function setDataType(eAttribute, datatype, eenum, list, map, defaultValue) {
- if (list)
- setListType(eAttribute, datatype);
- else if (eenum)
- setEnumType(eAttribute, datatype, defaultValue);
- else if (map)
- setMapType(eAttribute, datatype, defaultValue);
- }
- /**
- This function sets the default value and matches the datatype of a list attribute.
- The matching types are contained in the object dataType.
- **/
- function setListType(eAttribute, datatype) {
- var listType = datatype.split('<')[1].split('>')[0];
- listType = dataType[listType];
- eAttribute.dataType = listType;
- }
- /**
- This function sets the datatype (and the default value?) of an enumeration attribute.
- **/
- function setEnumType(eAttribute, datatype, defaultValue) {
- var name = eAttribute.name;
- var nameEnum = name.charAt(0).toUpperCase() + name.slice(1);
- eAttribute.dataType = nameEnum;
- eAttribute.defaultValue = defaultValue;
- createEnumClass(nameEnum, datatype);
- }
- /**
- This function creates an Enum class related to an enumeration attribute.
- **/
- function createEnumClass(nameEnum, datatype) {
- var eEnum = {};
- eEnum.name = nameEnum;
- var options = datatype.split('(')[1].split(')')[0];
- options = options.split(', ');
- eEnum.options = options;
- listEnums.push(eEnum);
- }
- /**
- This function sets the datatype (and the default value?) of a map attribute.
- **/
- function setMapType(eAttribute, datatype, defaultValue) {
- //var keys = datatype.split('[')[1].split(']')[0];
- //keys = keys.split(',');
- var values = datatype.split(']')[1].split('[')[1].split(']')[0];
- values = values.split(',');
- //for(var i = 0; i < keys.length; i++)
- // createMapClass(keys[i], values[i]);
- }
- /**
- This function finds all the attributes of a class.
- Input: the class identifier
- Output: a list of all the attributes of the class
- **/
- function findAttributes(classe, nodeIdentifier) {
- var attributesList = [];
- var node = as.nodes[nodeIdentifier].attributes['value'];
- for (var attr in node) {
- var name = node[attr].name;
- var defaultValue = node[attr]['default'];
- var datatype = node[attr].type;
- var eenum = false;
- var list = false;
- var map = false;
- if (datatype.startsWith("list"))
- list = true;
- else if (datatype.startsWith("ENUM"))
- eenum = true;
- else if (datatype.startsWith("map"))
- map = true;
- var attribute = createEAttribute(name, datatype, defaultValue, eenum, list, map);
- attributesList.push(attribute);
- }
- return attributesList;
- }
- /**
- This function finds the cardinalities of a reference.
- Input: the class identifier (nodeIdentifier) and the reference name
- Output: a list of bounds (min and max)
- **/
- function findCardinalities(nodeIdentifier, referenceName) {
- var bounds = [];
- var cardinal = as.nodes[nodeIdentifier].cardinalities['value'];
- for (var car in cardinal) {
- if (cardinal[car].type == referenceName) {
- bounds.push(cardinal[car].min);
- bounds.push(cardinal[car].max);
- }
- }
- return bounds;
- }
- /**
- This function finds all the references of a class.
- Input: the class identifier
- Output: a list of all the references of the class
- **/
- function findReferences(nodeIdentifier) {
- var referencesList = [];
- for (var edge in as.edges) {
- if (as.edges[edge].src == nodeIdentifier) {
- var linkKey = as.edges[edge].dest;
- if (isAssociationLink(linkKey)) {
- var name = as.nodes[linkKey].name.value;
- var linktype = as.nodes[linkKey].linktype['value'];
- if (nodeIdentifier == rootNode || inside(listRoots, nodeIdentifier))
- linktype = "containment";
- var bounds = findCardinalities(nodeIdentifier, name);
- var dest = linkDestination(linkKey);
- var type = as.nodes[dest].name.value;
- var reference = createEReference(name, linktype, type, bounds);
- referencesList.push(reference);
- }
- }
- }
- return referencesList;
- }
- /**
- This function returns what's to be written in the file in the beginning, including the
- package's name and the URI.
- **/
- function writeHeader() {
- var header = '<?xml version="1.0" encoding="UTF-8"?> \n';
- header += '<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \n';
- header += ' xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="' + packageName;
- header += '" nsURI="' + nsURI + '" nsPrefix="' + packageName + '"> \n';
- return header;
- }
- /**
- This function returns what's to be written in the file in the case of an EClass.
- **/
- function writeEClassifier(classe) {
- var classLine = ' <eClassifiers xsi:type="ecore:EClass" name="' + classe.name;
- if (classe.abstract)
- classLine += '" abstract="true';
- if (classe.superclass != null)
- classLine += '" eSuperTypes="#//' + classe.superclass;
- if (classe.attributes.length > 0 || classe.references.length > 0) {
- classLine += '"> \n';
- for (var i = 0; i < classe.attributes.length; i++)
- classLine += writeEAttribute(classe.attributes[i]);
- for (var i = 0; i < classe.references.length; i++)
- classLine += writeEReference(classe.references[i]);
- classLine += ' </eClassifiers> \n';
- }
- else
- classLine += '"/> \n';
- return classLine;
- }
- /**
- This function returns what's to be written in the file in the case of an EAttribute.
- **/
- function writeEAttribute(attr) {
- var attrLine = ' <eStructuralFeatures xsi:type="ecore:EAttribute" name="' + attr.name;
- if (attr.map)
- attrLine += writeMapAttribute(attr);
- else {
- if (attr.list)
- attrLine += '" upperBound="-1';
- if (attr.list || !(attr.complexType))
- attrLine += '" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//' + attr.dataType;
- if (attr.eenum)
- attrLine += '" eType="#//' + attr.dataType;
- if (attr.defaultValue != null)
- attrLine += '" defaultValueLiteral="' + attr.defaultValue;
- attrLine += '"/> \n';
- }
- return attrLine;
- }
- /**
- This function returns what's to be written in a Map attribute.
- **/
- function writeMapAttribute(attr) {
- var mapAttr = '';
- mapAttr += '" upperBound="-1" transient="true"> \n';
- mapAttr += writeEGenericType("EMap");
- mapAttr += ' </eStructuralFeatures> \n';
- return mapAttr;
- }
- /**
- This function returns what's to be written in a GenericType.
- **/
- function writeEGenericType(datatype) {
- var gentype = ' <eGenericType eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//';
- gentype += datatype + '"> \n';
- gentype += writeETypeArguments(["EEList", "EString"]);
- gentype += writeETypeArguments(["EEList", "EDataType"]);
- gentype += ' </eGenericType> \n';
- return gentype;
- }
- /**
- This function returns what's to be written in a ETypeArguments.
- **/
- function writeETypeArguments(types) {
- var typeArg = ' <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//';
- typeArg += types[0] + '"> \n';
- typeArg += writeSingleETypeArguments(types[1]);
- typeArg += ' </eTypeArguments> \n';
- return typeArg;
- }
- /**
- This function returns what's to be written in a ETypeArguments.
- **/
- function writeSingleETypeArguments(type) {
- var singleTypeArg = ' <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//';
- singleTypeArg += type + '"/> \n';
- return singleTypeArg;
- }
- /**
- This function returns what's to be written in the file in the case of an EReference.
- **/
- function writeEReference(ref) {
- var refLine = ' <eStructuralFeatures xsi:type="ecore:EReference" name="' + ref.name;
- if (ref.lowerBound != null)
- refLine += '" lowerBound="' + ref.lowerBound;
- if (ref.upperBound != null)
- refLine += '" upperBound="' + ref.upperBound;
- refLine += '" eType="#//' + ref.type;
- if (ref.linkType == "containment")
- refLine += '" containment="true';
- refLine += '"/> \n';
- return refLine;
- }
- /**
- This function returns what's to be written in the file in the case of an EEnum.
- **/
- function writeEEnum(enume) {
- var enumLine = ' <eClassifiers xsi:type="ecore:EEnum" name="' + enume.name + '"> \n';
- for (var i = 0; i < enume.options.length; i++)
- enumLine += ' <eLiterals name="' + enume.options[i] + '"/> \n';
- enumLine += ' </eClassifiers> \n';
- return enumLine;
- }
- /**
- This function creates the classes then put them in the variable listClasses.
- **/
- function populateListClasses() {
- for (var node in as.nodes) {
- if (!(inside(listRoots, node)) && isClass(node)) {
- var eclass = createEClass(node);
- listClasses.push(eclass);
- }
- }
- }
- /**
- This function will write everything (classes, attributes, references, enumerations)
- in a string.
- **/
- function writeFile() {
- file_contents += writeHeader();
- for (var i = 0; i < listClasses.length; i++)
- file_contents += writeEClassifier(listClasses[i]);
- for (var i = 0; i < listEnums.length; i++)
- file_contents += writeEEnum(listEnums[i]);
- file_contents += '</ecore:EPackage>';
- }
- /****************************************************************************************************************************************************************
- The following is like the main class of this file. It will call the appropriate functions
- in order to create the export file.
- ****************************************************************************************************************************************************************/
- //creation of root and, if apply, possible roots
- setRoot();
- //creation of classes, including their attributes and references
- populateListClasses();
- //write the file
- writeFile();
- _fs.writeFileSync('./exported_to_ecore/' + packageName + 'Metamodel.ecore', file_contents);
- __postMessage({
- 'statusCode': 200,
- 'respIndex': resp
- });
- },
- function (writeErr) {
- __postInternalErrorMsg(resp, writeErr);
- }
- );
- },
- function (err) {
- __postInternalErrorMsg(resp, err);
- }
- );
- }
- ,
- 'asworker'
- :
- function (resp, method, uri, reqData, wcontext) {
- __postMessage(
- {
- 'statusCode': 200,
- 'respIndex': resp
- });
- }
- };
|