Skip to content

Commit

Permalink
feat: Alias Import (#20)
Browse files Browse the repository at this point in the history
* feat: Concept for alias import added

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

* feat: metamodel.json updated for alias imports

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

* fix: concept ImportAliasTypes  merged into ImportTypes

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

* feat(alias): variable renamed and test cases added

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

* feat(alias import): metamodelutil updated

- Test cases added
- metamodelfile.js updated for aliased imports

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

* feat(alias import): test cases updated

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

* feat(alias import): Pr changes

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

* feat(import alias): Code refactored & Test Coverage

- metamodelutil.js code refactored
- Test cases updated to increase coverage

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

* feat(alias): Test case fixed

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

* feat(alias):code refactored & comments added

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>

---------

Signed-off-by: Jaskeerat Singh Saluja <58400083+salujajaskeerat@users.noreply.github.com>
  • Loading branch information
salujajaskeerat authored Jul 4, 2024
1 parent 2adebc2 commit 9eeb019
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 3 deletions.
5 changes: 5 additions & 0 deletions lib/metamodel.cto
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ concept LongDomainValidator {
o Long upper optional
}

concept AliasedType{
o String name
o String aliasedName
}
abstract concept Import {
o String namespace
o String uri optional
Expand All @@ -222,6 +226,7 @@ concept ImportType extends Import {

concept ImportTypes extends Import {
o String[] types
o AliasedType[] aliasedTypes optional
}

concept Model {
Expand Down
30 changes: 30 additions & 0 deletions lib/metamodel.json
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,25 @@
}
]
},
{
"$class":"concerto.metamodel@1.0.0.ConceptDeclaration",
"name":"AliasedType",
"isAbstract":false,
"properties":[
{
"$class": "concerto.metamodel@1.0.0.StringProperty",
"name": "name",
"isArray": false,
"isOptional": false
},
{
"$class": "concerto.metamodel@1.0.0.StringProperty",
"name": "aliasedName",
"isArray": false,
"isOptional": false
}
]
},
{
"$class": "concerto.metamodel@1.0.0.ConceptDeclaration",
"name": "Import",
Expand Down Expand Up @@ -1053,13 +1072,24 @@
"name": "types",
"isArray": true,
"isOptional": false
},
{
"$class": "concerto.metamodel@1.0.0.ObjectProperty",
"name": "aliasedTypes",
"type": {
"$class": "concerto.metamodel@1.0.0.TypeIdentifier",
"name": "AliasedType"
},
"isArray": true,
"isOptional": true
}
],
"superType": {
"$class": "concerto.metamodel@1.0.0.TypeIdentifier",
"name": "Import"
}
},

{
"$class": "concerto.metamodel@1.0.0.ConceptDeclaration",
"name": "Model",
Expand Down
15 changes: 12 additions & 3 deletions lib/metamodelutil.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,21 @@ function createNameTable(priorModels, metaModel) {
}
table[imp.name] = namespace;
} else if (imp.$class === `${MetaModelNamespace}.ImportTypes`) {
for (const type of imp.types) {
// Create a map of aliased types if they exist, otherwise initialize an empty map.
const aliasedMap = imp.aliasedTypes
? new Map(imp.aliasedTypes.map(({ name, aliasedName }) => [name, aliasedName]))
: new Map();
imp.types.forEach((type) => {
// 'localName' is the identifier used to refer to the imported type, as it can be aliased..
const localName = aliasedMap.get(type) || type;

// Verify if the type declaration exists in the model file.
// Here, 'type' refers to the actual declaration name within the model file that is being imported.
if (!findDeclaration(modelFile, type)) {
throw new Error(`Declaration ${type} in namespace ${namespace} not found`);
}
table[type] = namespace;
}
table[localName] = namespace;
});
} else {
(modelFile.declarations || []).forEach((decl) => {
table[decl.name] = namespace;
Expand Down
104 changes: 104 additions & 0 deletions test/cto/aliasedImport.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"$class": "concerto.metamodel@1.0.0.Models",
"models": [
{
"$class": "concerto.metamodel@1.0.0.Model",
"decorators": [],
"namespace": "org.saluja",
"imports": [],
"declarations": [
{
"$class": "concerto.metamodel@1.0.0.ConceptDeclaration",
"name": "doc",
"isAbstract": false,
"properties": [
{
"$class": "concerto.metamodel@1.0.0.StringProperty",
"name": "name",
"isArray": false,
"isOptional": false,
"location": {
"$class": "concerto.metamodel@1.0.0.Range",
"start": {
"offset": 35,
"line": 4,
"column": 2,
"$class": "concerto.metamodel@1.0.0.Position"
},
"end": {
"offset": 49,
"line": 5,
"column": 1,
"$class": "concerto.metamodel@1.0.0.Position"
}
}
}
],
"location": {
"$class": "concerto.metamodel@1.0.0.Range",
"start": {
"offset": 22,
"line": 3,
"column": 1,
"$class": "concerto.metamodel@1.0.0.Position"
},
"end": {
"offset": 50,
"line": 5,
"column": 2,
"$class": "concerto.metamodel@1.0.0.Position"
}
}
}
]
},
{
"$class": "concerto.metamodel@1.0.0.Model",
"decorators": [],
"namespace": "org.test",
"imports": [
{
"$class": "concerto.metamodel@1.0.0.ImportTypes",
"namespace": "org.saluja",
"types": [
"doc"
],
"aliasedTypes": [
{
"$class": "concerto.metamodel@1.0.0.AliasedType",
"name": "doc",
"aliasedName": "d"
}
]
}
],
"declarations": [
{
"$class": "concerto.metamodel@1.0.0.ConceptDeclaration",
"name": "file",
"isAbstract": false,
"properties": [],
"location": {
"$class": "concerto.metamodel@1.0.0.Range",
"start": {
"offset": 50,
"line": 5,
"column": 1,
"$class": "concerto.metamodel@1.0.0.Position"
},
"end": {
"offset": 75,
"line": 6,
"column": 2,
"$class": "concerto.metamodel@1.0.0.Position"
}
},
"superType": {
"$class": "concerto.metamodel@1.0.0.TypeIdentifier",
"name": "d"
}
}
]
}
]
}
105 changes: 105 additions & 0 deletions test/cto/aliasedImportResolved.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"$class": "concerto.metamodel@1.0.0.Models",
"models": [
{
"$class": "concerto.metamodel@1.0.0.Model",
"decorators": [],
"namespace": "org.saluja",
"imports": [],
"declarations": [
{
"$class": "concerto.metamodel@1.0.0.ConceptDeclaration",
"name": "doc",
"isAbstract": false,
"properties": [
{
"$class": "concerto.metamodel@1.0.0.StringProperty",
"name": "name",
"isArray": false,
"isOptional": false,
"location": {
"$class": "concerto.metamodel@1.0.0.Range",
"start": {
"offset": 35,
"line": 4,
"column": 2,
"$class": "concerto.metamodel@1.0.0.Position"
},
"end": {
"offset": 49,
"line": 5,
"column": 1,
"$class": "concerto.metamodel@1.0.0.Position"
}
}
}
],
"location": {
"$class": "concerto.metamodel@1.0.0.Range",
"start": {
"offset": 22,
"line": 3,
"column": 1,
"$class": "concerto.metamodel@1.0.0.Position"
},
"end": {
"offset": 50,
"line": 5,
"column": 2,
"$class": "concerto.metamodel@1.0.0.Position"
}
}
}
]
},
{
"$class": "concerto.metamodel@1.0.0.Model",
"decorators": [],
"namespace": "org.test",
"imports": [
{
"$class": "concerto.metamodel@1.0.0.ImportTypes",
"namespace": "org.saluja",
"types": [
"doc"
],
"aliasedTypes": [
{
"$class": "concerto.metamodel@1.0.0.AliasedType",
"name": "doc",
"aliasedName": "d"
}
]
}
],
"declarations": [
{
"$class": "concerto.metamodel@1.0.0.ConceptDeclaration",
"name": "file",
"isAbstract": false,
"properties": [],
"location": {
"$class": "concerto.metamodel@1.0.0.Range",
"start": {
"offset": 50,
"line": 5,
"column": 1,
"$class": "concerto.metamodel@1.0.0.Position"
},
"end": {
"offset": 75,
"line": 6,
"column": 2,
"$class": "concerto.metamodel@1.0.0.Position"
}
},
"superType": {
"$class": "concerto.metamodel@1.0.0.TypeIdentifier",
"name": "d",
"namespace": "org.saluja"
}
}
]
}
]
}
78 changes: 78 additions & 0 deletions test/metamodelutil.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,74 @@ describe('MetaModel (Car)', () => {
});
});

describe('MetaModel aliasing', () => {

it('should convert a CTO model to its metamodel with name resolution', async () => {
const ModelPath = path.resolve(__dirname, './cto/aliasedImport.json');
const Model = JSON.parse(fs.readFileSync(ModelPath, 'utf8'));
const MetaModelResolved = JSON.parse(fs.readFileSync(path.resolve(__dirname, './cto/aliasedImportResolved.json'), 'utf8'));

const mm1r = MetaModelUtil.resolveLocalNamesForAll(Model);
mm1r.should.deep.equal(MetaModelResolved);
});

it('Should throw if name not found',async()=>{
const model = {
'$class': 'concerto.metamodel@1.0.0.Models',
'models': [
{
'$class': 'concerto.metamodel@1.0.0.Model',
decorators: [],
namespace: 'org.vehicle',
imports: [],
declarations: []
},
{
'$class': 'concerto.metamodel@1.0.0.Model',
decorators: [],
namespace: 'org.test',
imports: [
{
'$class': 'concerto.metamodel@1.0.0.ImportTypes',
namespace: 'org.vehicle',
types: [
'wheel'
],
aliasedTypes: [
{
'$class': 'concerto.metamodel@1.0.0.AliasedType',
name: 'wheel',
aliasedName: 'w'
}
]
}
],
declarations: [
{
'$class': 'concerto.metamodel@1.0.0.ConceptDeclaration',
name: 'car',
isAbstract: false,
properties: [
{
'$class': 'concerto.metamodel@1.0.0.ObjectProperty',
name: 'wheels',
type: {
'$class': 'concerto.metamodel@1.0.0.TypeIdentifier',
name: 'w'
},
isArray: true,
isOptional: false,
}
],
}
]
}
]

};
(()=>MetaModelUtil.resolveLocalNamesForAll(model)).should.throw();
});
});
describe('MetaModel (with Maps & Scalars)', () => {
process.env.ENABLE_MAP_TYPE = 'true'; // TODO Remove on release of MapType
const modelPath = path.resolve(__dirname, './cto/mapsImported.json');
Expand Down Expand Up @@ -186,6 +254,16 @@ describe('importFullyQualifiedNames', () => {
const result = MetaModelUtil.importFullyQualifiedNames(ast);
result.should.deep.equal(['test.Foo', 'test.Bar']);
});
it('should return imports when aliasing', async () => {
const ast = {
$class: 'concerto.metamodel@1.0.0.ImportTypes',
namespace: 'test',
types: ['Foo', 'Bar'],
aliasedTypes:{'f':'Foo','b':'Bar'}
};
const result = MetaModelUtil.importFullyQualifiedNames(ast);
result.should.deep.equal(['test.Foo', 'test.Bar']);
});
});

it('should throw for unrecognized import', async () => {
Expand Down

0 comments on commit 9eeb019

Please sign in to comment.