From f70040c97d79bd6a0a9e77383c5bdc7844b3918f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedek=20Horv=C3=A1th?= Date: Mon, 17 Jun 2024 10:44:16 +0200 Subject: [PATCH] feat(model-api): check if is sub-concept but only based on the IConcept UID In MPS, we mostly have MPSConcepts, SimpleConcepts and GeneratedConcepts at runtime. If we use MPSConcept, then the IConcept.isSubConceptOf assumes that the superConcept is an MPSConcept. If not, then the method returns false. This makes it difficult to check if this and superConcept are the same superConcept if is a SimpleConcept. GeneratedConcept.isSubConceptOf checks the equality with the superConcept, which means if the superConcept is a different object that structurally may represent the same Concept then it returns false (e.g. a GeneratedConcept and a SimpleConcept with the same Concept UID and name would return false). In order to simplify all these cases we could just check the UID of the Concept and assume that if UID matches then the Concepts should also be the same. This implementation is similar to how SConceptAdapterById checks the isSubConcept in MPS. This adapter is often used in MPS, that is wrapped by MPSConcept at runtime. --- .../kotlin/org/modelix/model/api/IConcept.kt | 19 ++++++++ .../org/modelix/model/api/IConceptTests.kt | 47 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 model-api/src/commonTest/kotlin/org/modelix/model/api/IConceptTests.kt diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt index 2c3ff5d017..da3d0f109f 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/IConcept.kt @@ -181,3 +181,22 @@ interface IConcept { fun IConcept?.isSubConceptOf(superConcept: IConcept?) = this?.isSubConceptOf(superConcept) == true fun IConcept.conceptAlias() = getConceptProperty("alias") + +/** + * Checks if this is a sub-concept of the [IConcept] that is identified by the [superConceptReference]'s UID. + * + * @param superConceptReference a reference to the potential super-concept + * @return true if this concept (or any of its ancestors) has the same UID as the [superConceptReference] + */ +fun IConcept.isSubConceptOf(superConceptReference: IConceptReference): Boolean { + if (this.getUID() == superConceptReference.getUID()) { + return true + } else { + for (parent in getDirectSuperConcepts()) { + if (parent.isSubConceptOf(superConceptReference)) { + return true + } + } + } + return false +} diff --git a/model-api/src/commonTest/kotlin/org/modelix/model/api/IConceptTests.kt b/model-api/src/commonTest/kotlin/org/modelix/model/api/IConceptTests.kt new file mode 100644 index 0000000000..6b464418b1 --- /dev/null +++ b/model-api/src/commonTest/kotlin/org/modelix/model/api/IConceptTests.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.modelix.model.api + +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class IConceptTests { + + @Test + fun isModuleSubConceptOfModuleBasedOnUid() { + val moduleConcept = BuiltinLanguages.MPSRepositoryConcepts.Module + val isSubConcept = moduleConcept.isSubConceptOf(BuiltinLanguages.MPSRepositoryConcepts.Module.getReference()) + assertTrue(isSubConcept) + } + + @Test + fun isModelNotSubConceptOfModuleBasedOnUid() { + val modelConcept = BuiltinLanguages.MPSRepositoryConcepts.Model + val moduleConcept = BuiltinLanguages.MPSRepositoryConcepts.Module.getReference() + val isNotSubConcept = modelConcept.isSubConceptOf(moduleConcept) + assertFalse(isNotSubConcept) + } + + @Test + fun isModuleSubConceptOfNamedConceptBasedOnUid() { + val moduleConcept = BuiltinLanguages.MPSRepositoryConcepts.Module + val namedConcept = BuiltinLanguages.jetbrains_mps_lang_core.INamedConcept.getReference() + val isSubConcept = moduleConcept.isSubConceptOf(namedConcept) + assertTrue(isSubConcept) + } +}