diff --git a/categories.json b/categories.json new file mode 100644 index 0000000..b6b666d --- /dev/null +++ b/categories.json @@ -0,0 +1,24 @@ +{ + "$class": "concerto.metamodel@1.0.0.Model", + "decorators": [], + "namespace": "org.categories@1.0.0", + "imports": [], + "declarations": [ + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Category", + "isAbstract": true, + "properties": [] + }, + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Engineering", + "isAbstract": false, + "properties": [], + "superType": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Category" + } + } + ] +} \ No newline at end of file diff --git a/decorators.json b/decorators.json new file mode 100644 index 0000000..b6b0631 --- /dev/null +++ b/decorators.json @@ -0,0 +1,79 @@ +{ + "$class": "concerto.metamodel@1.0.0.Model", + "decorators": [], + "namespace": "org.car@1.0.0", + "imports": [ + { + "$class": "concerto.metamodel@1.0.0.ImportTypes", + "namespace": "org.categories@1.0.0", + "types": [ + "Engineering" + ] + } + ], + "declarations": [ + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Car", + "isAbstract": false, + "properties": [ + { + "$class": "concerto.metamodel@1.0.0.ObjectProperty", + "name": "part", + "type": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Part" + }, + "isArray": false, + "isOptional": false + } + ] + }, + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Part", + "isAbstract": true, + "properties": [ + { + "$class": "concerto.metamodel@1.0.0.StringProperty", + "name": "name", + "isArray": false, + "isOptional": false, + "decorators": [ + { + "$class": "concerto.metamodel@1.0.0.Decorator", + "name": "category", + "arguments": [ + { + "$class": "concerto.metamodel@1.0.0.DecoratorTypeReference", + "type": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Engineering" + }, + "isArray": false + } + ] + } + ] + } + ] + }, + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Wheel", + "isAbstract": false, + "properties": [ + { + "$class": "concerto.metamodel@1.0.0.DoubleProperty", + "name": "diameter", + "isArray": false, + "isOptional": false + } + ], + "superType": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Part" + } + } + ] +} \ No newline at end of file diff --git a/lib/metamodelutil.js b/lib/metamodelutil.js index c39e724..e735146 100644 --- a/lib/metamodelutil.js +++ b/lib/metamodelutil.js @@ -126,6 +126,11 @@ function resolveName(name, table) { * @return {object} the metamodel with fully qualified names */ function resolveTypeNames(metaModel, table) { + // any element can have a decorator (including primitive fields) , so lets resolve those first + (metaModel.decorators || []).forEach((decorator) => { + resolveTypeNames(decorator, table); + }); + switch (metaModel.$class) { case `${MetaModelNamespace}.Model`: { (metaModel.declarations || []).forEach((decl) => { @@ -145,15 +150,6 @@ function resolveTypeNames(metaModel, table) { (metaModel.properties || []).forEach((property) => { resolveTypeNames(property, table); }); - (metaModel.decorators || []).forEach((decorator) => { - resolveTypeNames(decorator, table); - }); - } - break; - case `${MetaModelNamespace}.EnumDeclaration`: { - (metaModel.decorators || []).forEach((decorator) => { - resolveTypeNames(decorator, table); - }); } break; case `${MetaModelNamespace}.MapDeclaration`: { @@ -161,33 +157,19 @@ function resolveTypeNames(metaModel, table) { resolveTypeNames(metaModel.value, table); } break; - case `${MetaModelNamespace}.EnumProperty`: - case `${MetaModelNamespace}.ObjectProperty`: - case `${MetaModelNamespace}.RelationshipProperty`: { - const name = metaModel.type.name; - metaModel.type.namespace = resolveName(name, table); - (metaModel.decorators || []).forEach((decorator) => { - resolveTypeNames(decorator, table); - }); - } - break; case `${MetaModelNamespace}.Decorator`: { (metaModel.arguments || []).forEach((argument) => { resolveTypeNames(argument, table); }); } break; - case `${MetaModelNamespace}.DecoratorTypeReference`: { - const name = metaModel.type.name; - metaModel.type.namespace = resolveName(name, table); - } - break; + case `${MetaModelNamespace}.EnumProperty`: + case `${MetaModelNamespace}.ObjectProperty`: + case `${MetaModelNamespace}.RelationshipProperty`: + case `${MetaModelNamespace}.DecoratorTypeReference`: case `${MetaModelNamespace}.ObjectMapKeyType`: case `${MetaModelNamespace}.ObjectMapValueType`: { metaModel.type.namespace = resolveName(metaModel.type.name, table); - (metaModel.decorators || []).forEach((decorator) => { - resolveTypeNames(decorator, table); - }); } break; case `${MetaModelNamespace}.StringScalar`: @@ -197,9 +179,6 @@ function resolveTypeNames(metaModel, table) { case `${MetaModelNamespace}.LongScalar`: case `${MetaModelNamespace}.IntegerScalar`: { metaModel.namespace = resolveName(metaModel.name, table); - (metaModel.decorators || []).forEach((decorator) => { - resolveTypeNames(decorator, table); - }); } break; } diff --git a/test/cto/categories.cto b/test/cto/categories.cto new file mode 100644 index 0000000..5c9ef3d --- /dev/null +++ b/test/cto/categories.cto @@ -0,0 +1,4 @@ +namespace org.categories@1.0.0 + +abstract concept Category {} +concept Engineering extends Category {} diff --git a/test/cto/decorators.cto b/test/cto/decorators.cto new file mode 100644 index 0000000..2192e0e --- /dev/null +++ b/test/cto/decorators.cto @@ -0,0 +1,16 @@ +namespace org.car@1.0.0 + +import org.categories@1.0.0.{Engineering} + +concept Car { + o Part part +} + +abstract concept Part { + @category(Engineering) + o String name +} + +concept Wheel extends Part { + o Double diameter +} \ No newline at end of file diff --git a/test/cto/decorators.json b/test/cto/decorators.json new file mode 100644 index 0000000..5fdbe7b --- /dev/null +++ b/test/cto/decorators.json @@ -0,0 +1,108 @@ +{ + "$class": "concerto.metamodel@1.0.0.Models", + "models": [ + { + "$class": "concerto.metamodel@1.0.0.Model", + "decorators": [], + "namespace": "org.categories@1.0.0", + "imports": [], + "declarations": [ + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Category", + "isAbstract": true, + "properties": [] + }, + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Engineering", + "isAbstract": false, + "properties": [], + "superType": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Category" + } + } + ] + }, + { + "$class": "concerto.metamodel@1.0.0.Model", + "decorators": [], + "namespace": "org.car@1.0.0", + "imports": [ + { + "$class": "concerto.metamodel@1.0.0.ImportTypes", + "namespace": "org.categories@1.0.0", + "types": [ + "Engineering" + ] + } + ], + "declarations": [ + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Car", + "isAbstract": false, + "properties": [ + { + "$class": "concerto.metamodel@1.0.0.ObjectProperty", + "name": "part", + "type": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Part" + }, + "isArray": false, + "isOptional": false + } + ] + }, + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Part", + "isAbstract": true, + "properties": [ + { + "$class": "concerto.metamodel@1.0.0.StringProperty", + "name": "name", + "isArray": false, + "isOptional": false, + "decorators": [ + { + "$class": "concerto.metamodel@1.0.0.Decorator", + "name": "category", + "arguments": [ + { + "$class": "concerto.metamodel@1.0.0.DecoratorTypeReference", + "type": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Engineering" + }, + "isArray": false + } + ] + } + ] + } + ] + }, + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Wheel", + "isAbstract": false, + "properties": [ + { + "$class": "concerto.metamodel@1.0.0.DoubleProperty", + "name": "diameter", + "isArray": false, + "isOptional": false + } + ], + "superType": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Part" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/cto/decoratorsResolved.json b/test/cto/decoratorsResolved.json new file mode 100644 index 0000000..e028a65 --- /dev/null +++ b/test/cto/decoratorsResolved.json @@ -0,0 +1,112 @@ +{ + "$class": "concerto.metamodel@1.0.0.Models", + "models": [ + { + "$class": "concerto.metamodel@1.0.0.Model", + "decorators": [], + "namespace": "org.categories@1.0.0", + "imports": [], + "declarations": [ + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Category", + "isAbstract": true, + "properties": [] + }, + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Engineering", + "isAbstract": false, + "properties": [], + "superType": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Category", + "namespace": "org.categories@1.0.0" + } + } + ] + }, + { + "$class": "concerto.metamodel@1.0.0.Model", + "decorators": [], + "namespace": "org.car@1.0.0", + "imports": [ + { + "$class": "concerto.metamodel@1.0.0.ImportTypes", + "namespace": "org.categories@1.0.0", + "types": [ + "Engineering" + ] + } + ], + "declarations": [ + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Car", + "isAbstract": false, + "properties": [ + { + "$class": "concerto.metamodel@1.0.0.ObjectProperty", + "name": "part", + "type": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Part", + "namespace": "org.car@1.0.0" + }, + "isArray": false, + "isOptional": false + } + ] + }, + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Part", + "isAbstract": true, + "properties": [ + { + "$class": "concerto.metamodel@1.0.0.StringProperty", + "name": "name", + "isArray": false, + "isOptional": false, + "decorators": [ + { + "$class": "concerto.metamodel@1.0.0.Decorator", + "name": "category", + "arguments": [ + { + "$class": "concerto.metamodel@1.0.0.DecoratorTypeReference", + "type": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Engineering", + "namespace": "org.categories@1.0.0" + }, + "isArray": false + } + ] + } + ] + } + ] + }, + { + "$class": "concerto.metamodel@1.0.0.ConceptDeclaration", + "name": "Wheel", + "isAbstract": false, + "properties": [ + { + "$class": "concerto.metamodel@1.0.0.DoubleProperty", + "name": "diameter", + "isArray": false, + "isOptional": false + } + ], + "superType": { + "$class": "concerto.metamodel@1.0.0.TypeIdentifier", + "name": "Part", + "namespace": "org.car@1.0.0" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/metamodelutil.js b/test/metamodelutil.js index 10e4ebb..3ae55ac 100644 --- a/test/metamodelutil.js +++ b/test/metamodelutil.js @@ -100,6 +100,20 @@ describe('MetaModel (Car)', () => { }); }); +describe('MetaModel (Decorators)', () => { + const decoratorsPath = path.resolve(__dirname, './cto/decorators.json'); + const decoratorsModel = JSON.parse(fs.readFileSync(decoratorsPath, 'utf8')); + const decoratorsModelResolved = JSON.parse(fs.readFileSync(path.resolve(__dirname, './cto/decoratorsResolved.json'), 'utf8')); + + describe('#toMetaModel', () => { + it('should convert a CTO model to its metamodel with name resolution', async () => { + const resolved = MetaModelUtil.resolveLocalNamesForAll(decoratorsModel); + resolved.should.deep.equal(decoratorsModelResolved); + }); + }); +}); + + describe('MetaModel aliasing', () => { it('should convert a CTO model to its metamodel with name resolution', async () => {