From 043279474a7a5bb56407e6f300eea3c38a0e70bb Mon Sep 17 00:00:00 2001 From: Dan Selman Date: Fri, 11 Aug 2023 12:29:37 +0100 Subject: [PATCH 1/4] fix(filter) : remove imports of filtered types Signed-off-by: Dan Selman --- .../concerto-core/lib/basemodelmanager.js | 47 +++++++++++++++++-- .../concerto-core/lib/introspect/modelfile.js | 19 ++++++-- packages/concerto-core/test/modelmanager.js | 19 ++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/packages/concerto-core/lib/basemodelmanager.js b/packages/concerto-core/lib/basemodelmanager.js index 36249638d..5182b4bc6 100644 --- a/packages/concerto-core/lib/basemodelmanager.js +++ b/packages/concerto-core/lib/basemodelmanager.js @@ -741,10 +741,51 @@ class BaseModelManager { */ filter(predicate){ const modelManager = new BaseModelManager({...this.options}, this.processFile); - const filteredModels = Object.values(this.modelFiles) - .map((modelFile) => modelFile.filter(predicate, modelManager)) + const removedFqns = []; // the list of FQN of types that have been removed + + // remove the types from model files, populating removedFqns + let filteredModels = Object.values(this.modelFiles) + .map((modelFile) => modelFile.filter(predicate, modelManager, removedFqns)) .filter(Boolean); - modelManager.addModelFiles(filteredModels); + + // remove concerto model files - as these are automatically added + // when we recreate the model manager below + filteredModels = filteredModels.filter(mf => !mf.isSystemModelFile()); + + // now update filteredModels to remove any imports of removed types + const modelsWithValidImports = filteredModels.map( modelFile => { + const ast = modelFile.getAst(); + let modified = false; + removedFqns.forEach( removedFqn => { + const ns = ModelUtil.getNamespace(removedFqn); + const isSystemImport = ns.startsWith('concerto@') || ns === 'concerto'; + if(!isSystemImport && modelFile.getImports().includes(removedFqn)) { + const removeName = ModelUtil.getShortName(removedFqn); + const removeNamespace = ModelUtil.getNamespace(removedFqn); + ast.imports = ast.imports.filter(imp => { + const remove = ModelUtil.getShortName(imp.$class) === 'ImportType' && + imp.name === removeName && + imp.namespace === removeNamespace; + return !remove; + }); + ast.imports.forEach( imp => { + if(imp.namespace === removeNamespace) { + if(ModelUtil.getShortName(imp.$class) === 'ImportTypes') { + imp.types = imp.types.filter((type) => type !== removeName); + } + } + }); + modified = true; + } + }); + if(modified) { + return new ModelFile(this, ast, undefined, modelFile.fileName); + } + else { + return modelFile; + } + }); + modelManager.addModelFiles(modelsWithValidImports); return modelManager; } } diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index 85f51ba25..f525775ad 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -200,7 +200,8 @@ class ModelFile extends Decorated { /** * Returns the types that have been imported into this ModelFile. * - * @return {string[]} The array of imports for this ModelFile + * @return {string[]} The array of fully-qualified names for types imported by + * this ModelFile */ getImports() { let result = []; @@ -830,11 +831,23 @@ class ModelFile extends Decorated { * * @param {FilterFunction} predicate - the filter function over a Declaration object * @param {ModelManager} modelManager - the target ModelManager for the filtered ModelFile + * @param {string[]} removedDeclarations - an array that will be populated with the FQN of removed declarations * @returns {ModelFile?} - the filtered ModelFile * @private */ - filter(predicate, modelManager){ - const declarations = this.declarations?.filter(predicate).map(declaration => declaration.ast); + filter(predicate, modelManager, removedDeclarations){ + + let declarations = []; // ast for all included declarations + this.declarations?.forEach( declaration => { + const included = predicate(declaration); + if(!included) { + removedDeclarations.push(declaration.getFullyQualifiedName()); + } + else { + declarations.push(declaration.ast); + } + } ); + const ast = { ...this.ast, declarations: declarations, diff --git a/packages/concerto-core/test/modelmanager.js b/packages/concerto-core/test/modelmanager.js index d3b287843..d8afa02fb 100644 --- a/packages/concerto-core/test/modelmanager.js +++ b/packages/concerto-core/test/modelmanager.js @@ -1072,5 +1072,24 @@ concept Bar { filtered.validateModelFiles(); }); + + it('should remove imports for filtered types', () => { + modelManager.addCTOModel(`namespace child@1.0.0 + concept Used {} + concept Unused {} + `, 'child.cto', true); + + modelManager.addCTOModel(`namespace test@1.0.0 + import child@1.0.0.Unused + import child@1.0.0.Used + import child@1.0.0.{Used,Unused} + concept Person { + o Used used + } + `, 'test.cto'); + const filtered = modelManager.filter(declaration => + ['concerto@1.0.0.Concept','test@1.0.0.Person','child@1.0.0.Used'].includes(declaration.getFullyQualifiedName())); + filtered.validateModelFiles(); + }); }); }); From 5de6d5553315693ffa6871b808c6bc35de875890 Mon Sep 17 00:00:00 2001 From: Dan Selman Date: Fri, 11 Aug 2023 12:57:06 +0100 Subject: [PATCH 2/4] fix(filter) : improve the modification flag logic Signed-off-by: Dan Selman --- packages/concerto-core/lib/basemodelmanager.js | 12 ++++++++++-- packages/concerto-core/lib/introspect/modelfile.js | 1 - 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/concerto-core/lib/basemodelmanager.js b/packages/concerto-core/lib/basemodelmanager.js index 5182b4bc6..2786b261a 100644 --- a/packages/concerto-core/lib/basemodelmanager.js +++ b/packages/concerto-core/lib/basemodelmanager.js @@ -766,16 +766,24 @@ class BaseModelManager { const remove = ModelUtil.getShortName(imp.$class) === 'ImportType' && imp.name === removeName && imp.namespace === removeNamespace; + if(remove) { + modified = true; + } return !remove; }); ast.imports.forEach( imp => { if(imp.namespace === removeNamespace) { if(ModelUtil.getShortName(imp.$class) === 'ImportTypes') { - imp.types = imp.types.filter((type) => type !== removeName); + imp.types = imp.types.filter((type) => { + const remove = (type === removeName); + if(remove) { + modified = true; + } + return !remove; + }); } } }); - modified = true; } }); if(modified) { diff --git a/packages/concerto-core/lib/introspect/modelfile.js b/packages/concerto-core/lib/introspect/modelfile.js index f525775ad..497e68cf6 100644 --- a/packages/concerto-core/lib/introspect/modelfile.js +++ b/packages/concerto-core/lib/introspect/modelfile.js @@ -836,7 +836,6 @@ class ModelFile extends Decorated { * @private */ filter(predicate, modelManager, removedDeclarations){ - let declarations = []; // ast for all included declarations this.declarations?.forEach( declaration => { const included = predicate(declaration); From 0f8c18261b809d2b653baf3fa2389792235eed82 Mon Sep 17 00:00:00 2001 From: Dan Selman Date: Fri, 11 Aug 2023 12:58:07 +0100 Subject: [PATCH 3/4] fix(filter) : improve the modification flag logic Signed-off-by: Dan Selman --- packages/concerto-core/test/modelmanager.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/concerto-core/test/modelmanager.js b/packages/concerto-core/test/modelmanager.js index d8afa02fb..19f307900 100644 --- a/packages/concerto-core/test/modelmanager.js +++ b/packages/concerto-core/test/modelmanager.js @@ -1079,12 +1079,18 @@ concept Bar { concept Unused {} `, 'child.cto', true); + modelManager.addCTOModel(`namespace cousin@1.0.0 + concept Used2 {} + `, 'cousin.cto', true); + modelManager.addCTOModel(`namespace test@1.0.0 import child@1.0.0.Unused import child@1.0.0.Used + import cousing@1.0.0.Used2 import child@1.0.0.{Used,Unused} concept Person { o Used used + o Used2 used2 } `, 'test.cto'); const filtered = modelManager.filter(declaration => From a58d788181ba2be5c66aaa4989bbb140c0d2e162 Mon Sep 17 00:00:00 2001 From: Dan Selman Date: Fri, 11 Aug 2023 13:02:33 +0100 Subject: [PATCH 4/4] tests Signed-off-by: Dan Selman --- packages/concerto-core/test/modelmanager.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/concerto-core/test/modelmanager.js b/packages/concerto-core/test/modelmanager.js index 19f307900..9a982bc2c 100644 --- a/packages/concerto-core/test/modelmanager.js +++ b/packages/concerto-core/test/modelmanager.js @@ -1080,21 +1080,25 @@ concept Bar { `, 'child.cto', true); modelManager.addCTOModel(`namespace cousin@1.0.0 - concept Used2 {} + concept AlsoUsed {} `, 'cousin.cto', true); + modelManager.addCTOModel(`namespace orphan@1.0.0 + concept Orphan {} + `, 'orphan.cto', true); + modelManager.addCTOModel(`namespace test@1.0.0 import child@1.0.0.Unused import child@1.0.0.Used - import cousing@1.0.0.Used2 + import cousin@1.0.0.AlsoUsed import child@1.0.0.{Used,Unused} concept Person { o Used used - o Used2 used2 + o AlsoUsed alsoUsed } `, 'test.cto'); const filtered = modelManager.filter(declaration => - ['concerto@1.0.0.Concept','test@1.0.0.Person','child@1.0.0.Used'].includes(declaration.getFullyQualifiedName())); + ['concerto@1.0.0.Concept','test@1.0.0.Person','child@1.0.0.Used', 'cousin@1.0.0.AlsoUsed'].includes(declaration.getFullyQualifiedName())); filtered.validateModelFiles(); }); });