Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Alias Import #20

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)) {
salujajaskeerat marked this conversation as resolved.
Show resolved Hide resolved
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()=>{
salujajaskeerat marked this conversation as resolved.
Show resolved Hide resolved
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']);
ekarademir marked this conversation as resolved.
Show resolved Hide resolved
});
});

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