From d2b3be921daad09c9774384309a23fb292882343 Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Thu, 1 Apr 2021 17:04:14 +0100 Subject: [PATCH 01/14] Add syntax for model casts --- .../inf/gts_morpher/tests/syntax/DEVSMM.ecore | 7 ++ .../gts_morpher/tests/syntax/DEVSModel.xmi | 17 +++++ .../syntax/ParsingAndValidationTests.xtend | 62 ++++++++++++++++++ uk.ac.kcl.inf.gts_morpher.ui/plugin.xml_gen | 46 ++++++------- .../.launch/Launch Runtime Eclipse.launch | 65 ++++++++++--------- .../model/generated/GTSMorpher.ecore | 5 ++ .../model/generated/GTSMorpher.genmodel | 9 ++- uk.ac.kcl.inf.gts_morpher/plugin.xml_gen | 10 +++ .../ac/kcl/inf/gts_morpher/GTSMorpher.xtext | 7 +- .../util/GTSSpecificationHelper.xtend | 2 + 10 files changed, 172 insertions(+), 58 deletions(-) create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/DEVSModel.xmi create mode 100644 uk.ac.kcl.inf.gts_morpher/plugin.xml_gen diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/DEVSMM.ecore b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/DEVSMM.ecore index 48f61001..e3faf515 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/DEVSMM.ecore +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/DEVSMM.ecore @@ -37,4 +37,11 @@ + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/DEVSModel.xmi b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/DEVSModel.xmi new file mode 100644 index 00000000..4aebf663 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/DEVSModel.xmi @@ -0,0 +1,17 @@ + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend index 44cd8252..a0e8cf21 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend @@ -75,6 +75,11 @@ class ParsingAndValidationTests extends AbstractTest { private def createInterfaceResourceSet() { #["storing_server.ecore", "DEVSMM.ecore", "storing_server.henshin", "devsmm.henshin"].createResourceSet } + + private def createModelResourceSet() { + #["storing_server.ecore", "DEVSMM.ecore", "DEVSModel.xmi", "storing_server.henshin", "devsmm.henshin"].createResourceSet + } + /** * Tests basic parsing and linking for a sunshine case @@ -3102,4 +3107,61 @@ class ParsingAndValidationTests extends AbstractTest { result.assertError(GtsMorpherPackage.Literals.STRING_PARAMETER, GTSMorpherValidator.INVALID_UNIT_CALL_PARAMETER_TYPE) } + + /** + * Tests basic parsing of model casting + */ + @Test + def void parsingBasicModelCasting() { + // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files + // Then would use «serverURI.toString» etc. below + val result = parseHelper.parse(''' + gts ServerSystem { + metamodel: "server" + behaviour: "serverRules" + } + + gts ServerInterface interface_of { ServerSystem } + + gts DEVSMMSystem { + metamodel: "devsmm" + behaviour: "devsmmRules" + } + + map ServerToDEVSMM { + from ServerInterface + to DEVSMMSystem + + type_mapping { + class server.Server => devsmm.Machine + reference server.Server.Out => devsmm.Machine.out + } + + behaviour_mapping { + rule process to process { + object input => in_part + link [in_queue->input:elts] => [tray->in_part:parts] + } + } + } + + gts DEVSMMWithServer { + weave: { + map1: interface_of (ServerSystem) + map2: ServerToDEVSMM + } + } + + model derivedModel = "DEVSModel.xmi" (instance of DEVSMMSystem) as DEVSMMWithServer + ''', createModelResourceSet) + assertNotNull("Did not produce parse result", result) + assertTrue("Found parse errors: " + result.eResource.errors, result.eResource.errors.isEmpty) + + assertNotNull("Did not resolve src GTS of model cast", result.modelCasts.head.srcGTS) + assertNotNull("Did not resolve tgt GTS of model cast", result.modelCasts.head.tgtGTS) + + assertTrue("Found linking errors: " + result.eResource.errors, result.eResource.errors.isEmpty) + + result.assertNoError(GTSMorpherValidator.WEAVE_WITH_DIFFERENT_SOURCES) + } } diff --git a/uk.ac.kcl.inf.gts_morpher.ui/plugin.xml_gen b/uk.ac.kcl.inf.gts_morpher.ui/plugin.xml_gen index 671395dc..180998f9 100644 --- a/uk.ac.kcl.inf.gts_morpher.ui/plugin.xml_gen +++ b/uk.ac.kcl.inf.gts_morpher.ui/plugin.xml_gen @@ -26,11 +26,11 @@ - - + - - + + - - - - + + - - + - + - - - - - - + + + + + + @@ -309,7 +309,7 @@ - @@ -369,7 +369,7 @@ - diff --git a/uk.ac.kcl.inf.gts_morpher/.launch/Launch Runtime Eclipse.launch b/uk.ac.kcl.inf.gts_morpher/.launch/Launch Runtime Eclipse.launch index aa1d84ac..cf949c80 100644 --- a/uk.ac.kcl.inf.gts_morpher/.launch/Launch Runtime Eclipse.launch +++ b/uk.ac.kcl.inf.gts_morpher/.launch/Launch Runtime Eclipse.launch @@ -1,35 +1,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.ecore b/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.ecore index 28380713..241a7b86 100644 --- a/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.ecore +++ b/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.ecore @@ -145,4 +145,9 @@ + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.genmodel b/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.genmodel index e1ded47c..17614dd3 100644 --- a/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.genmodel +++ b/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.genmodel @@ -1,9 +1,9 @@ + complianceLevel="8.0" copyrightFields="false" runtimeVersion="2.20" usedGenPackages="platform:/resource/org.eclipse.emf.henshin.model/model/henshin.genmodel#//henshin"> @@ -132,5 +132,10 @@ + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher/plugin.xml_gen b/uk.ac.kcl.inf.gts_morpher/plugin.xml_gen new file mode 100644 index 00000000..d1e6f042 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher/plugin.xml_gen @@ -0,0 +1,10 @@ + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/GTSMorpher.xtext b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/GTSMorpher.xtext index b85ff522..abf6dcce 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/GTSMorpher.xtext +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/GTSMorpher.xtext @@ -12,7 +12,8 @@ GTSSpecificationModule: GTSSpecificationModuleMember: GTSSpecification | GTSFamilySpecification | - GTSMapping + GTSMapping | + ModelCast ; GTSMapping: @@ -197,6 +198,10 @@ RuleParameterMapping: "param" source = [gts::Parameter | ID] "=>" target = [gts::Parameter | ID] ; +ModelCast: + "model" name=ID "=" modelFile=STRING ("(" "instance" "of" srcGTS = [GTSSpecification] ")")? "as" tgtGTS = [GTSSpecification] +; + LinkName : "[" ID "->" ID ":" ID "]" ; diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/util/GTSSpecificationHelper.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/util/GTSSpecificationHelper.xtend index 0d57340a..16595ab9 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/util/GTSSpecificationHelper.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/util/GTSSpecificationHelper.xtend @@ -43,10 +43,12 @@ import uk.ac.kcl.inf.gts_morpher.gtsMorpher.UnitParameter import uk.ac.kcl.inf.gts_morpher.util.MultiResourceOnChangeEvictingCache.IClearableItem import static extension uk.ac.kcl.inf.gts_morpher.util.EMFHelper.* +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.ModelCast class GTSSpecificationHelper { static def getGtss (GTSSpecificationModule module) { module.members.filter(GTSSpecification) } + static def getModelCasts (GTSSpecificationModule module) { module.members.filter(ModelCast) } static def getGts_families (GTSSpecificationModule module) { module.members.filter(GTSFamilySpecification) } From 39d1620fd4222cd71161f6026be4afe411e2daed Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Thu, 1 Apr 2021 18:29:58 +0100 Subject: [PATCH 02/14] Basic validation of model casts Check model file can be read and agrees with source GTS meta-model, if any. --- .../syntax/ParsingAndValidationTests.xtend | 52 +++++++++++++++++++ .../validation/GTSMorpherValidator.xtend | 47 ++++++++++++++--- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend index a0e8cf21..ae177315 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend @@ -3164,4 +3164,56 @@ class ParsingAndValidationTests extends AbstractTest { result.assertNoError(GTSMorpherValidator.WEAVE_WITH_DIFFERENT_SOURCES) } + + /** + * Tests validation of model casting + */ + @Test + def void validationBasicModelCasting() { + // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files + // Then would use «serverURI.toString» etc. below + val result = parseHelper.parse(''' + gts ServerSystem { + metamodel: "server" + behaviour: "serverRules" + } + + gts ServerInterface interface_of { ServerSystem } + + gts DEVSMMSystem { + metamodel: "devsmm" + behaviour: "devsmmRules" + } + + map ServerToDEVSMM { + from ServerInterface + to DEVSMMSystem + + type_mapping { + class server.Server => devsmm.Machine + reference server.Server.Out => devsmm.Machine.out + } + + behaviour_mapping { + rule process to process { + object input => in_part + link [in_queue->input:elts] => [tray->in_part:parts] + } + } + } + + gts DEVSMMWithServer { + weave: { + map1: interface_of (ServerSystem) + map2: ServerToDEVSMM + } + } + + model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of DEVSMMSystem) as DEVSMMWithServer + ''', createModelResourceSet) + assertNotNull("Did not produce parse result", result) + assertTrue("Found parse errors: " + result.eResource.errors, result.eResource.errors.isEmpty) + + result.assertNoError(GTSMorpherValidator.MODEL_CAST_INVALID_SOURCE_TYPE) + } } diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend index e4c549fd..d9f05fb2 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend @@ -8,12 +8,16 @@ import java.util.HashSet import java.util.Map import java.util.Map.Entry import java.util.Set +import org.eclipse.emf.common.util.URI +import org.eclipse.emf.common.util.WrappedException import org.eclipse.emf.ecore.EClass import org.eclipse.emf.ecore.EClassifier import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.EPackage import org.eclipse.emf.ecore.EReference import org.eclipse.emf.ecore.EStructuralFeature import org.eclipse.emf.ecore.EcorePackage +import org.eclipse.emf.henshin.model.Attribute import org.eclipse.emf.henshin.model.Edge import org.eclipse.emf.henshin.model.HenshinPackage import org.eclipse.emf.henshin.model.Node @@ -35,11 +39,13 @@ import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSSpecification import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSWeave import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GtsMorpherPackage import uk.ac.kcl.inf.gts_morpher.gtsMorpher.LinkMapping +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.ModelCast import uk.ac.kcl.inf.gts_morpher.gtsMorpher.NumericParameter import uk.ac.kcl.inf.gts_morpher.gtsMorpher.ObjectMapping import uk.ac.kcl.inf.gts_morpher.gtsMorpher.ReferenceMapping import uk.ac.kcl.inf.gts_morpher.gtsMorpher.RuleMapping import uk.ac.kcl.inf.gts_morpher.gtsMorpher.RuleParameterMapping +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.SlotMapping import uk.ac.kcl.inf.gts_morpher.gtsMorpher.StringParameter import uk.ac.kcl.inf.gts_morpher.gtsMorpher.TypeGraphMapping import uk.ac.kcl.inf.gts_morpher.gtsMorpher.UnitCall @@ -57,15 +63,13 @@ import static extension uk.ac.kcl.inf.gts_morpher.util.GTSSpecificationHelper.* import static extension uk.ac.kcl.inf.gts_morpher.util.HenshinChecker.isIdentityRule import static extension uk.ac.kcl.inf.gts_morpher.util.MorphismCompleter.* import static extension uk.ac.kcl.inf.gts_morpher.validation.GTSMorpherValidatorHelper.* -import org.eclipse.emf.henshin.model.Attribute -import uk.ac.kcl.inf.gts_morpher.gtsMorpher.SlotMapping /** * This class contains custom validation rules. * * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation */ -class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorpherValidatorHelper.IssueAcceptor{ +class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorpherValidatorHelper.IssueAcceptor { public static val DUPLICATE_CLASS_MAPPING = MappingConverter.DUPLICATE_CLASS_MAPPING public static val DUPLICATE_REFERENCE_MAPPING = MappingConverter.DUPLICATE_REFERENCE_MAPPING public static val DUPLICATE_ATTRIBUTE_MAPPING = MappingConverter.DUPLICATE_ATTRIBUTE_MAPPING @@ -102,6 +106,7 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp public static val INCLUSION_CANNOT_BE_WITHOUT_TO_VIRTUAL = 'uk.ac.kcl.inf.gts_morpher.xdsml_compose.INCLUSION_CANNOT_BE_WITHOUT_TO_VIRTUAL' public static val INCLUSION_CANNOT_BE_ALLOW_FROM_EMPTY = 'uk.ac.kcl.inf.gts_morpher.xdsml_compose.INCLUSION_CANNOT_BE_ALLOW_FROM_EMPTY' public static val INCLUSION_MUST_HAVE_SAME_SOURCE_AND_TARGET = 'uk.ac.kcl.inf.gts_morpher.xdsml_compose.INCLUSION_MUST_HAVE_SAME_SOURCE_AND_TARGET' + public static val MODEL_CAST_INVALID_SOURCE_TYPE = 'uk.ac.kcl.inf.gts_morpher.xdsml_compose.MODEL_CAST_INVALID_SOURCE_TYPE' /** * Check that the rules in a GTS specification refer to the metamodel package @@ -490,6 +495,35 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp ] } + @Check + def checkModelCast(ModelCast modelCast) { + modelCast.checkModelSourceType + } + + private def checkModelSourceType(ModelCast modelCast) { + if ((modelCast.modelFile !== null) && (!modelCast.modelFile.empty) && (modelCast.srcGTS !== null) && + (modelCast.srcGTS.metamodel !== null)) { + try { + val modelResource = modelCast.eResource.resourceSet.getResource(URI.createFileURI(modelCast.modelFile), + true) + val metaModel = modelResource.contents.head.eClass.eResource.contents.head as EPackage + + if (metaModel !== modelCast.srcGTS.metamodel) { + error("The meta-model of the source model must correspond to the meta-model of the source GTS.", + GtsMorpherPackage.Literals.MODEL_CAST__MODEL_FILE, MODEL_CAST_INVALID_SOURCE_TYPE) + } + } + catch (WrappedException we) { + error('''Could not load model file «we.exception.message».''', GtsMorpherPackage.Literals.MODEL_CAST__MODEL_FILE, + MODEL_CAST_INVALID_SOURCE_TYPE) + } + catch (Exception e) { + error('''Could not load model file «e.message».''', GtsMorpherPackage.Literals.MODEL_CAST__MODEL_FILE, + MODEL_CAST_INVALID_SOURCE_TYPE) + } + } + } + private dispatch def checkIsValidMapping(Void spec) {} private dispatch def checkIsValidMapping(GTSMappingRefOrInterfaceSpec spec) { @@ -533,14 +567,13 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp private def mapMessage(Entry> mappingChoices) { var message = '''«if (mappingChoices.key instanceof EClass) {'''class'''} else {'''reference'''}» «mappingChoices.key.qualifiedName» to any of [«mappingChoices.value.map[eo | eo.qualifiedName].join(', ')»]''' - + if (message.length > 150) { message = message.substring(1, 147) + '...' } - + message } - private def issueData(EObject source, EObject target) '''«if (source instanceof EClass) {'''class'''} else {'''reference'''}»:«source.qualifiedName»=>«target.qualifiedName»''' @@ -598,7 +631,7 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp val _mapping = mapping.extractMapping mapping.isInCompleteMapping(_mapping) } - + override warning(String message, EObject source, EStructuralFeature feature, String code) { super.warning(message, source, feature, code) } From 397f7f6ead80df6d455afc54db8d1672da57b1cc Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Thu, 15 Apr 2021 14:24:38 +0100 Subject: [PATCH 03/14] Test source-errors are actually thrown for model casts --- .../syntax/ParsingAndValidationTests.xtend | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend index ae177315..6cb51a14 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend @@ -31,6 +31,7 @@ import static org.junit.Assert.* import static extension uk.ac.kcl.inf.gts_morpher.util.GTSSpecificationHelper.* import static extension uk.ac.kcl.inf.util.henshinsupport.NamingHelper.* +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.ModelCast @RunWith(XtextRunner) @InjectWith(GTSMorpherInjectorProvider) @@ -3216,4 +3217,56 @@ class ParsingAndValidationTests extends AbstractTest { result.assertNoError(GTSMorpherValidator.MODEL_CAST_INVALID_SOURCE_TYPE) } + + /** + * Tests validation of model casting + */ + @Test + def void validationBasicModelCastingWithSourceError() { + // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files + // Then would use «serverURI.toString» etc. below + val result = parseHelper.parse(''' + gts ServerSystem { + metamodel: "server" + behaviour: "serverRules" + } + + gts ServerInterface interface_of { ServerSystem } + + gts DEVSMMSystem { + metamodel: "devsmm" + behaviour: "devsmmRules" + } + + map ServerToDEVSMM { + from ServerInterface + to DEVSMMSystem + + type_mapping { + class server.Server => devsmm.Machine + reference server.Server.Out => devsmm.Machine.out + } + + behaviour_mapping { + rule process to process { + object input => in_part + link [in_queue->input:elts] => [tray->in_part:parts] + } + } + } + + gts DEVSMMWithServer { + weave: { + map1: interface_of (ServerSystem) + map2: ServerToDEVSMM + } + } + + model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of ServerSystem) as DEVSMMWithServer + ''', createModelResourceSet) + assertNotNull("Did not produce parse result", result) + assertTrue("Found parse errors: " + result.eResource.errors, result.eResource.errors.isEmpty) + + result.eContents.head.assertError(GtsMorpherPackage.Literals.MODEL_CAST, GTSMorpherValidator.MODEL_CAST_INVALID_SOURCE_TYPE) + } } From 5c3b06e91c3b87460183892b66cbb3ddacf56f65 Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Thu, 15 Apr 2021 14:30:30 +0100 Subject: [PATCH 04/14] Introduced tests for target-type checks on model casts --- .../syntax/ParsingAndValidationTests.xtend | 53 +++++++++++++++++++ .../validation/GTSMorpherValidator.xtend | 1 + 2 files changed, 54 insertions(+) diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend index 6cb51a14..dcd6fbe8 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend @@ -3216,6 +3216,7 @@ class ParsingAndValidationTests extends AbstractTest { assertTrue("Found parse errors: " + result.eResource.errors, result.eResource.errors.isEmpty) result.assertNoError(GTSMorpherValidator.MODEL_CAST_INVALID_SOURCE_TYPE) + result.assertNoError(GTSMorpherValidator.MODEL_CAST_INVALID_TARGET_TYPE) } /** @@ -3269,4 +3270,56 @@ class ParsingAndValidationTests extends AbstractTest { result.eContents.head.assertError(GtsMorpherPackage.Literals.MODEL_CAST, GTSMorpherValidator.MODEL_CAST_INVALID_SOURCE_TYPE) } + + /** + * Tests validation of model casting + */ + @Test + def void validationBasicModelCastingTargetTypeError() { + // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files + // Then would use «serverURI.toString» etc. below + val result = parseHelper.parse(''' + gts ServerSystem { + metamodel: "server" + behaviour: "serverRules" + } + + gts ServerInterface interface_of { ServerSystem } + + gts DEVSMMSystem { + metamodel: "devsmm" + behaviour: "devsmmRules" + } + + map ServerToDEVSMM { + from ServerInterface + to DEVSMMSystem + + type_mapping { + class server.Server => devsmm.Machine + reference server.Server.Out => devsmm.Machine.out + } + + behaviour_mapping { + rule process to process { + object input => in_part + link [in_queue->input:elts] => [tray->in_part:parts] + } + } + } + + gts DEVSMMWithServer { + weave: { + map1: interface_of (ServerSystem) + map2: ServerToDEVSMM + } + } + + model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of DEVSMMSystem) as ServerSystem + ''', createModelResourceSet) + assertNotNull("Did not produce parse result", result) + assertTrue("Found parse errors: " + result.eResource.errors, result.eResource.errors.isEmpty) + + result.eContents.head.assertError(GtsMorpherPackage.Literals.MODEL_CAST, GTSMorpherValidator.MODEL_CAST_INVALID_TARGET_TYPE) + } } diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend index d9f05fb2..91640708 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend @@ -107,6 +107,7 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp public static val INCLUSION_CANNOT_BE_ALLOW_FROM_EMPTY = 'uk.ac.kcl.inf.gts_morpher.xdsml_compose.INCLUSION_CANNOT_BE_ALLOW_FROM_EMPTY' public static val INCLUSION_MUST_HAVE_SAME_SOURCE_AND_TARGET = 'uk.ac.kcl.inf.gts_morpher.xdsml_compose.INCLUSION_MUST_HAVE_SAME_SOURCE_AND_TARGET' public static val MODEL_CAST_INVALID_SOURCE_TYPE = 'uk.ac.kcl.inf.gts_morpher.xdsml_compose.MODEL_CAST_INVALID_SOURCE_TYPE' + public static val MODEL_CAST_INVALID_TARGET_TYPE = 'uk.ac.kcl.inf.gts_morpher.xdsml_compose.MODEL_CAST_INVALID_TARGET_TYPE' /** * Check that the rules in a GTS specification refer to the metamodel package From 331e484b44f393f6971e47745648f2f847914738 Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Thu, 15 Apr 2021 14:31:23 +0100 Subject: [PATCH 05/14] Gracefully handle null.isInterfaceElement() --- .../src/uk/ac/kcl/inf/gts_morpher/util/EMFHelper.xtend | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/util/EMFHelper.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/util/EMFHelper.xtend index 67d15977..6fa300a2 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/util/EMFHelper.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/util/EMFHelper.xtend @@ -64,6 +64,10 @@ class EMFHelper { } } + static dispatch def boolean isInterfaceElement(Void v) { + false + } + static dispatch def boolean isInterfaceElement(EObject eo) { true } From 138d96e75f3d4d00519554702e669b5253ba3001 Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Thu, 15 Apr 2021 14:59:20 +0100 Subject: [PATCH 06/14] Basic validation of derivation traces for model casts --- .../syntax/ParsingAndValidationTests.xtend | 1 - .../validation/GTSMorpherValidator.xtend | 55 +++++++++++++++++-- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend index dcd6fbe8..6e766de8 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/syntax/ParsingAndValidationTests.xtend @@ -31,7 +31,6 @@ import static org.junit.Assert.* import static extension uk.ac.kcl.inf.gts_morpher.util.GTSSpecificationHelper.* import static extension uk.ac.kcl.inf.util.henshinsupport.NamingHelper.* -import uk.ac.kcl.inf.gts_morpher.gtsMorpher.ModelCast @RunWith(XtextRunner) @InjectWith(GTSMorpherInjectorProvider) diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend index 91640708..376a3a7a 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend @@ -35,6 +35,8 @@ import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMapping import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMappingInterfaceSpec import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMappingRef import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMappingRefOrInterfaceSpec +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSReference +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSSelection import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSSpecification import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSWeave import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GtsMorpherPackage @@ -499,6 +501,7 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp @Check def checkModelCast(ModelCast modelCast) { modelCast.checkModelSourceType + modelCast.checkModelTargetType } private def checkModelSourceType(ModelCast modelCast) { @@ -513,18 +516,58 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp error("The meta-model of the source model must correspond to the meta-model of the source GTS.", GtsMorpherPackage.Literals.MODEL_CAST__MODEL_FILE, MODEL_CAST_INVALID_SOURCE_TYPE) } - } - catch (WrappedException we) { - error('''Could not load model file «we.exception.message».''', GtsMorpherPackage.Literals.MODEL_CAST__MODEL_FILE, - MODEL_CAST_INVALID_SOURCE_TYPE) - } - catch (Exception e) { + } catch (WrappedException we) { + error('''Could not load model file «we.exception.message».''', + GtsMorpherPackage.Literals.MODEL_CAST__MODEL_FILE, MODEL_CAST_INVALID_SOURCE_TYPE) + } catch (Exception e) { error('''Could not load model file «e.message».''', GtsMorpherPackage.Literals.MODEL_CAST__MODEL_FILE, MODEL_CAST_INVALID_SOURCE_TYPE) } } } + private def checkModelTargetType(ModelCast modelCast) { + if (modelCast.tgtGTS !== null) { + if (modelCast.srcGTS !== null) { + if (!modelCast.tgtGTS.canBeDerivedFrom(modelCast.srcGTS)) { + error('''Target GTS cannot be derived from source GTS.''', + GtsMorpherPackage.Literals.MODEL_CAST__TGT_GTS, MODEL_CAST_INVALID_TARGET_TYPE) + } + } else { + // TODO: add validation for case where the source GTS must be inferred + } + } + } + + // TODO: This should probably actually implement a proper depth-first search given that, in principle, the derivation structure could be a DAG. + private dispatch def boolean canBeDerivedFrom(GTSSpecification target, GTSSpecification source) { + (target === source) || target.gts.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSSelection target, GTSSpecification source) { + false + } + + private dispatch def boolean canBeDerivedFrom(GTSWeave target, GTSSpecification source) { + target.mapping1.canBeDerivedFrom(source) || target.mapping2.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSMappingRef target, GTSSpecification source) { + target.ref.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSMappingInterfaceSpec target, GTSSpecification source) { + target.gts_ref.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSMapping target, GTSSpecification source) { + target.source.canBeDerivedFrom(source) || target.target.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSReference target, GTSSpecification source) { + target.ref.canBeDerivedFrom(source) + } + private dispatch def checkIsValidMapping(Void spec) {} private dispatch def checkIsValidMapping(GTSMappingRefOrInterfaceSpec spec) { From b4c6d3fb9fed5c26e73bccb25e0e4d0da5a242a0 Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Thu, 15 Apr 2021 19:22:33 +0100 Subject: [PATCH 07/14] WiP: On the way to a proper DfS tracing implementation --- .../model/generated/GTSMorpher.ecore | 9 +- .../model/generated/GTSMorpher.genmodel | 1 + .../ac/kcl/inf/gts_morpher/GTSMorpher.xtext | 8 ++ .../gts_morpher/modelcaster/GTSTrace.xtend | 102 ++++++++++++++++++ 4 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend diff --git a/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.ecore b/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.ecore index 241a7b86..d73eaf75 100644 --- a/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.ecore +++ b/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.ecore @@ -8,7 +8,7 @@ - + @@ -33,13 +33,13 @@ - + - + @@ -72,9 +72,10 @@ - + + diff --git a/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.genmodel b/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.genmodel index 17614dd3..a81508b8 100644 --- a/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.genmodel +++ b/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.genmodel @@ -68,6 +68,7 @@ + diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/GTSMorpher.xtext b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/GTSMorpher.xtext index abf6dcce..746f59ba 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/GTSMorpher.xtext +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/GTSMorpher.xtext @@ -111,6 +111,14 @@ GTSMappingInterfaceSpec: "interface_of" "(" gts_ref=[GTSSpecification] ")" ; +// Helper not used in grammar, but constructing a meta-model interface so we can access all of these in a GTS trace +GTSTraceMember: + GTSSpecification | + GTSMapping | + GTSSelection | + GTSMappingInterfaceSpec +; + UnitCallList: steps += UnitCall ("," steps += UnitCall)* ; diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend new file mode 100644 index 00000000..b9cec3f8 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend @@ -0,0 +1,102 @@ +package uk.ac.kcl.inf.gts_morpher.modelcaster + +import java.util.ArrayList +import java.util.HashMap +import java.util.HashSet +import java.util.List +import java.util.Set +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSSpecification +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSTraceMember + +/** + * A GTS trace is a path that connects a source and a target GTS through a number of transformations in a GTSMorpher specification. + * For example, the source GTS might be the root of a family specification, from which a member gets picked, that is then mapped + * and woven to construct the target GTS. + * + * The source GTS is always the first member of the GTSTrace and the target GTS is always the last member of the GTSTrace. + */ +interface GTSTrace extends List { + def getSource() { head } + + def getTarget() { last } + + static class GTSTraceImpl extends ArrayList implements GTSTrace { + new(GTSTraceMember... trace) { + super() + this += trace + } + } + + static class GTSTraceHelper extends HashMap> { + def Set findTraces(GTSSpecification source, GTSSpecification target) { + val Set visited = new HashSet + + collectTraces(source, target, visited) + } + + // TODO: Refactor to separate DFS logic and actual selection of followers + private def Set collectTraces(GTSSpecification source, GTSTraceMember target, + Set visited) { + if (visited.contains(target)) { + if (get(target) !== null) { + return get(target) + } else { + return emptySet + } + } + + visited += target + + val traces = if (source === target) { + #{new GTSTraceImpl(source, target) as GTSTrace} + } else { + expandAll(target.stepDown.flatMap[source.collectTraces(it, visited)].toSet, target) + } + + put(target, traces) + + traces + } + + private def expandAll(Set traces, GTSTraceMember newTarget) { + traces.map[new GTSTraceImpl(it + #{newTarget}) as GTSTrace].toSet + } + + private dispatch def Iterable stepDown (GTSTraceMember node) { #[] } + private dispatch def Iterable stepDown (GTSSpecification node) { #[node.gts] } + + /* + private dispatch def boolean canBeDerivedFrom(GTSSpecification target, GTSSpecification source) { + (target === source) || target.gts.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSSelection target, GTSSpecification source) { + false + } + + private dispatch def boolean canBeDerivedFrom(GTSWeave target, GTSSpecification source) { + target.mapping1.canBeDerivedFrom(source) || target.mapping2.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSMappingRef target, GTSSpecification source) { + target.ref.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSMappingInterfaceSpec target, GTSSpecification source) { + target.gts_ref.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSMapping target, GTSSpecification source) { + target.source.canBeDerivedFrom(source) || target.target.canBeDerivedFrom(source) + } + + private dispatch def boolean canBeDerivedFrom(GTSReference target, GTSSpecification source) { + target.ref.canBeDerivedFrom(source) + } + */ + } + + static def findTracesTo(GTSSpecification source, GTSSpecification target) { + new GTSTraceHelper().findTraces(source, target) + } +} From c04f1fb1beb7094491f796cd88530ff8683b2e6f Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Fri, 16 Apr 2021 08:34:20 +0100 Subject: [PATCH 08/14] Better implementation of GTS traces --- .../gts_morpher/modelcaster/GTSTrace.xtend | 116 ++++++++++-------- .../validation/GTSMorpherValidator.xtend | 34 +---- 2 files changed, 71 insertions(+), 79 deletions(-) diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend index b9cec3f8..144f64e0 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend @@ -5,8 +5,20 @@ import java.util.HashMap import java.util.HashSet import java.util.List import java.util.Set +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSFamilyChoice +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSFamilyReference +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSFamilySpecification +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSFamilySpecificationOrReference +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSLiteral +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMapping +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMappingInterfaceSpec +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMappingRef +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMappingRefOrInterfaceSpec +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSReference import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSSpecification +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSSpecificationOrReference import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSTraceMember +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSWeave /** * A GTS trace is a path that connects a source and a target GTS through a number of transformations in a GTSMorpher specification. @@ -15,26 +27,31 @@ import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSTraceMember * * The source GTS is always the first member of the GTSTrace and the target GTS is always the last member of the GTSTrace. */ -interface GTSTrace extends List { - def getSource() { head } - - def getTarget() { last } +class GTSTrace extends ArrayList { + /** + * Constructs the set of traces through which source can be transformed into target. + */ + // TODO: Implement caching + static def Set findTracesTo(GTSSpecification source, GTSSpecification target) { + new GTSTraceHelper().findTraces(source, target) + } - static class GTSTraceImpl extends ArrayList implements GTSTrace { - new(GTSTraceMember... trace) { - super() - this += trace - } + private new(GTSTraceMember... trace) { + super() + this += trace } - static class GTSTraceHelper extends HashMap> { - def Set findTraces(GTSSpecification source, GTSSpecification target) { + def getSource() { head as GTSSpecification } + + def getTarget() { last as GTSSpecification } + + private static class GTSTraceHelper extends HashMap> { + def Set findTraces(GTSSpecification source, GTSSpecification target) { val Set visited = new HashSet collectTraces(source, target, visited) } - // TODO: Refactor to separate DFS logic and actual selection of followers private def Set collectTraces(GTSSpecification source, GTSTraceMember target, Set visited) { if (visited.contains(target)) { @@ -48,55 +65,56 @@ interface GTSTrace extends List { visited += target val traces = if (source === target) { - #{new GTSTraceImpl(source, target) as GTSTrace} - } else { - expandAll(target.stepDown.flatMap[source.collectTraces(it, visited)].toSet, target) - } - + #{new GTSTrace(source, target)} + } else { + expandAll(target.stepDown.flatMap[source.collectTraces(it, visited)].toSet, target) + } + put(target, traces) - + traces } - + private def expandAll(Set traces, GTSTraceMember newTarget) { - traces.map[new GTSTraceImpl(it + #{newTarget}) as GTSTrace].toSet + traces.map[new GTSTrace(it + #{newTarget})].toSet } - - private dispatch def Iterable stepDown (GTSTraceMember node) { #[] } - private dispatch def Iterable stepDown (GTSSpecification node) { #[node.gts] } - /* - private dispatch def boolean canBeDerivedFrom(GTSSpecification target, GTSSpecification source) { - (target === source) || target.gts.canBeDerivedFrom(source) - } + private dispatch def Iterable stepDown(GTSTraceMember node) { #[] } - private dispatch def boolean canBeDerivedFrom(GTSSelection target, GTSSpecification source) { - false - } + private dispatch def Iterable stepDown(GTSSpecification node) { #[node.gts] } - private dispatch def boolean canBeDerivedFrom(GTSWeave target, GTSSpecification source) { - target.mapping1.canBeDerivedFrom(source) || target.mapping2.canBeDerivedFrom(source) - } + private dispatch def Iterable stepDown(GTSMapping node) { + #[node.source.resolve, node.target.resolve] + } - private dispatch def boolean canBeDerivedFrom(GTSMappingRef target, GTSSpecification source) { - target.ref.canBeDerivedFrom(source) - } + private dispatch def Iterable stepDown(GTSLiteral node) { #[] } - private dispatch def boolean canBeDerivedFrom(GTSMappingInterfaceSpec target, GTSSpecification source) { - target.gts_ref.canBeDerivedFrom(source) - } + private dispatch def Iterable stepDown(GTSFamilyChoice node) { #[node.family.resolve] } - private dispatch def boolean canBeDerivedFrom(GTSMapping target, GTSSpecification source) { - target.source.canBeDerivedFrom(source) || target.target.canBeDerivedFrom(source) - } + private dispatch def Iterable stepDown(GTSReference node) { #[node.ref] } - private dispatch def boolean canBeDerivedFrom(GTSReference target, GTSSpecification source) { - target.ref.canBeDerivedFrom(source) - } - */ - } + private dispatch def Iterable stepDown(GTSWeave node) { + #[node.mapping1.resolve, node.mapping2.resolve] + } - static def findTracesTo(GTSSpecification source, GTSSpecification target) { - new GTSTraceHelper().findTraces(source, target) + private dispatch def Iterable stepDown(GTSMappingInterfaceSpec node) { #[node.gts_ref] } + + private dispatch def GTSTraceMember resolve(GTSSpecificationOrReference ref) { null } + + private dispatch def GTSTraceMember resolve(GTSSpecification ref) { ref } + + private dispatch def GTSTraceMember resolve(GTSReference ref) { ref.ref } + + private dispatch def GTSTraceMember resolve(GTSFamilySpecificationOrReference ref) { null } + + private dispatch def GTSTraceMember resolve(GTSFamilySpecification ref) { ref.root_gts.resolve } + + private dispatch def GTSTraceMember resolve(GTSFamilyReference ref) { ref.ref.resolve } + + private dispatch def GTSTraceMember resolve(GTSMappingRefOrInterfaceSpec ref) { null } + + private dispatch def GTSTraceMember resolve(GTSMappingRef ref) { ref.ref } + + private dispatch def GTSTraceMember resolve(GTSMappingInterfaceSpec ref) { ref.gts_ref } } } diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend index 376a3a7a..6b24af2b 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/validation/GTSMorpherValidator.xtend @@ -35,8 +35,6 @@ import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMapping import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMappingInterfaceSpec import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMappingRef import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSMappingRefOrInterfaceSpec -import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSReference -import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSSelection import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSSpecification import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSWeave import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GtsMorpherPackage @@ -60,6 +58,7 @@ import uk.ac.kcl.inf.gts_morpher.util.ValueHolder import static uk.ac.kcl.inf.gts_morpher.util.MappingConverter.* import static uk.ac.kcl.inf.gts_morpher.util.MorphismChecker.* +import static extension uk.ac.kcl.inf.gts_morpher.modelcaster.GTSTrace.* import static extension uk.ac.kcl.inf.gts_morpher.util.EMFHelper.* import static extension uk.ac.kcl.inf.gts_morpher.util.GTSSpecificationHelper.* import static extension uk.ac.kcl.inf.gts_morpher.util.HenshinChecker.isIdentityRule @@ -530,7 +529,7 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp if (modelCast.tgtGTS !== null) { if (modelCast.srcGTS !== null) { if (!modelCast.tgtGTS.canBeDerivedFrom(modelCast.srcGTS)) { - error('''Target GTS cannot be derived from source GTS.''', + error('''Target GTS cannot be uniquely derived from source GTS.''', GtsMorpherPackage.Literals.MODEL_CAST__TGT_GTS, MODEL_CAST_INVALID_TARGET_TYPE) } } else { @@ -539,33 +538,8 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp } } - // TODO: This should probably actually implement a proper depth-first search given that, in principle, the derivation structure could be a DAG. - private dispatch def boolean canBeDerivedFrom(GTSSpecification target, GTSSpecification source) { - (target === source) || target.gts.canBeDerivedFrom(source) - } - - private dispatch def boolean canBeDerivedFrom(GTSSelection target, GTSSpecification source) { - false - } - - private dispatch def boolean canBeDerivedFrom(GTSWeave target, GTSSpecification source) { - target.mapping1.canBeDerivedFrom(source) || target.mapping2.canBeDerivedFrom(source) - } - - private dispatch def boolean canBeDerivedFrom(GTSMappingRef target, GTSSpecification source) { - target.ref.canBeDerivedFrom(source) - } - - private dispatch def boolean canBeDerivedFrom(GTSMappingInterfaceSpec target, GTSSpecification source) { - target.gts_ref.canBeDerivedFrom(source) - } - - private dispatch def boolean canBeDerivedFrom(GTSMapping target, GTSSpecification source) { - target.source.canBeDerivedFrom(source) || target.target.canBeDerivedFrom(source) - } - - private dispatch def boolean canBeDerivedFrom(GTSReference target, GTSSpecification source) { - target.ref.canBeDerivedFrom(source) + private def canBeDerivedFrom(GTSSpecification target, GTSSpecification source) { + source.findTracesTo(target).size == 1 } private dispatch def checkIsValidMapping(Void spec) {} From 779b140f4bbf24ec4547032df928924969124f8d Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Wed, 5 May 2021 14:36:18 +0100 Subject: [PATCH 09/14] Cache traces --- .../kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend index 144f64e0..71e07478 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend @@ -3,8 +3,8 @@ package uk.ac.kcl.inf.gts_morpher.modelcaster import java.util.ArrayList import java.util.HashMap import java.util.HashSet -import java.util.List import java.util.Set +import org.eclipse.xtext.util.OnChangeEvictingCache import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSFamilyChoice import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSFamilyReference import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSFamilySpecification @@ -28,12 +28,20 @@ import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSWeave * The source GTS is always the first member of the GTSTrace and the target GTS is always the last member of the GTSTrace. */ class GTSTrace extends ArrayList { + + static val traceCache = new OnChangeEvictingCache + /** * Constructs the set of traces through which source can be transformed into target. */ - // TODO: Implement caching static def Set findTracesTo(GTSSpecification source, GTSSpecification target) { - new GTSTraceHelper().findTraces(source, target) + val key = source -> target + + traceCache.get(key, source.eResource) [ + traceCache.get(key, target.eResource) [ + new GTSTraceHelper().findTraces(source, target) + ] + ] } private new(GTSTraceMember... trace) { From c35425f9ad84116788b980b15daba00d9bd57051 Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Wed, 5 May 2021 15:11:05 +0100 Subject: [PATCH 10/14] Upgraded JUnit --- uk.ac.kcl.inf.gts_morpher.tests/.classpath | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uk.ac.kcl.inf.gts_morpher.tests/.classpath b/uk.ac.kcl.inf.gts_morpher.tests/.classpath index 6f6caf47..e0272a87 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/.classpath +++ b/uk.ac.kcl.inf.gts_morpher.tests/.classpath @@ -15,12 +15,12 @@ - + - + - + From c29e8a65da25a94fd0bec389086b6fa1981184dd Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Wed, 5 May 2021 16:00:36 +0100 Subject: [PATCH 11/14] WiP: Testing trace generation There's still a bug here... --- .../ac/kcl/inf/gts_morpher/tests/HasSize.java | 36 ++ .../ac/kcl/inf/gts_morpher/tests/IsEmpty.java | 30 ++ .../inf/gts_morpher/tests/IsTraceWith.java | 44 +++ .../tests/modelcaster/DEVSMM.ecore | 47 +++ .../tests/modelcaster/DEVSModel.xmi | 17 + .../tests/modelcaster/ModelcasterTest.xtend | 199 ++++++++++ .../tests/modelcaster/devsmm.henshin | 87 +++++ .../tests/modelcaster/devsmm.henshin_diagram | 175 +++++++++ .../tests/modelcaster/storing_server.ecore | 38 ++ .../tests/modelcaster/storing_server.emf | 37 ++ .../tests/modelcaster/storing_server.henshin | 185 +++++++++ .../storing_server.henshin_diagram | 352 ++++++++++++++++++ .../META-INF/MANIFEST.MF | 3 +- .../gts_morpher/modelcaster/GTSTrace.xtend | 3 +- 14 files changed, 1251 insertions(+), 2 deletions(-) create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/HasSize.java create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/IsEmpty.java create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/IsTraceWith.java create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/DEVSMM.ecore create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/DEVSModel.xmi create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/devsmm.henshin create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/devsmm.henshin_diagram create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.ecore create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.emf create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin_diagram diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/HasSize.java b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/HasSize.java new file mode 100644 index 00000000..a9e8d8a4 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/HasSize.java @@ -0,0 +1,36 @@ +package uk.ac.kcl.inf.gts_morpher.tests; + +import java.lang.reflect.InvocationTargetException; + +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +/* Matches any class that has a size() method + * that returns an int */ +public class HasSize extends TypeSafeMatcher +{ + @Factory + public static Matcher hasSize(int val) + { + return new HasSize(val); + } + + private int val; + + private HasSize(int val) { + this.val = val; + } + + @Override + protected boolean matchesSafely(final T item) + { + try { return ((int) item.getClass().getMethod("size", (Class[]) null).invoke(item) == val); } + catch (final NoSuchMethodException e) { return false; } + catch (final InvocationTargetException | IllegalAccessException e) { throw new RuntimeException(e); } + } + + @Override + public void describeTo(final Description description) { description.appendText("is of size " + val); } +} \ No newline at end of file diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/IsEmpty.java b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/IsEmpty.java new file mode 100644 index 00000000..7395facf --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/IsEmpty.java @@ -0,0 +1,30 @@ +package uk.ac.kcl.inf.gts_morpher.tests; + +import java.lang.reflect.InvocationTargetException; + +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +/* Matches any class that has an isEmpty() method + * that returns a boolean */ +public class IsEmpty extends TypeSafeMatcher +{ + @Factory + public static Matcher empty() + { + return new IsEmpty(); + } + + @Override + protected boolean matchesSafely(final T item) + { + try { return (boolean) item.getClass().getMethod("isEmpty", (Class[]) null).invoke(item); } + catch (final NoSuchMethodException e) { return false; } + catch (final InvocationTargetException | IllegalAccessException e) { throw new RuntimeException(e); } + } + + @Override + public void describeTo(final Description description) { description.appendText("is empty"); } +} \ No newline at end of file diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/IsTraceWith.java b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/IsTraceWith.java new file mode 100644 index 00000000..b8729c6b --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/IsTraceWith.java @@ -0,0 +1,44 @@ +package uk.ac.kcl.inf.gts_morpher.tests; + +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSTraceMember; +import uk.ac.kcl.inf.gts_morpher.modelcaster.GTSTrace; + +/* Checks trace equality */ +public class IsTraceWith extends TypeSafeMatcher +{ + @Factory + public static Matcher isTraceWith(GTSTraceMember... members) + { + return new IsTraceWith(members); + } + + private GTSTraceMember[] members; + + private IsTraceWith(GTSTraceMember... members) { + this.members = members; + } + + @Override + protected boolean matchesSafely(final GTSTrace item) + { + if (item.size() == members.length) { + for (int i = 0; i < item.size(); i++) { + if (item.get(i) != members[i]) { + return false; + } + } + + return true; + } else { + return false; + } + } + + @Override + public void describeTo(final Description description) { description.appendText("is trae of " + members); } +} \ No newline at end of file diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/DEVSMM.ecore b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/DEVSMM.ecore new file mode 100644 index 00000000..e3faf515 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/DEVSMM.ecore @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/DEVSModel.xmi b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/DEVSModel.xmi new file mode 100644 index 00000000..4aebf663 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/DEVSModel.xmi @@ -0,0 +1,17 @@ + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend new file mode 100644 index 00000000..40778d28 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend @@ -0,0 +1,199 @@ +package uk.ac.kcl.inf.gts_morpher.tests.modelcaster + +import com.google.inject.Inject +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.XtextRunner +import org.eclipse.xtext.testing.util.ParseHelper +import org.junit.Test +import org.junit.runner.RunWith +import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSSpecificationModule +import uk.ac.kcl.inf.gts_morpher.tests.AbstractTest +import uk.ac.kcl.inf.gts_morpher.tests.GTSMorpherInjectorProvider + +import static org.hamcrest.CoreMatchers.* +import static org.hamcrest.MatcherAssert.* +import static uk.ac.kcl.inf.gts_morpher.tests.IsEmpty.* +import static uk.ac.kcl.inf.gts_morpher.tests.HasSize.* +import static uk.ac.kcl.inf.gts_morpher.tests.IsTraceWith.* + +import static extension uk.ac.kcl.inf.gts_morpher.modelcaster.GTSTrace.* +import static extension uk.ac.kcl.inf.gts_morpher.util.GTSSpecificationHelper.* +import org.eclipse.xtext.debug.IStratumBreakpointSupport.DefaultImpl + +@RunWith(XtextRunner) +@InjectWith(GTSMorpherInjectorProvider) +class ModelcasterTest extends AbstractTest { + @Inject + ParseHelper parseHelper + + private def createModelResourceSet() { + #["storing_server.ecore", "DEVSMM.ecore", "DEVSModel.xmi", "storing_server.henshin", "devsmm.henshin"].createResourceSet + } + + /** + * Test trace generation + */ + @Test + def void validationBasicTrace() { + // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files + // Then would use «serverURI.toString» etc. below + val result = parseHelper.parse(''' + gts ServerSystem { + metamodel: "server" + behaviour: "serverRules" + } + + gts ServerInterface interface_of { ServerSystem } + + gts DEVSMMSystem { + metamodel: "devsmm" + behaviour: "devsmmRules" + } + + map ServerToDEVSMM { + from ServerInterface + to DEVSMMSystem + + type_mapping { + class server.Server => devsmm.Machine + reference server.Server.Out => devsmm.Machine.out + } + + behaviour_mapping { + rule process to process { + object input => in_part + link [in_queue->input:elts] => [tray->in_part:parts] + } + } + } + + gts DEVSMMWithServer { + weave: { + map1: interface_of (ServerSystem) + map2: ServerToDEVSMM + } + } + + model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of DEVSMMSystem) as DEVSMMWithServer + ''', createModelResourceSet) + assertThat ("Did not produce parse result", result, is(notNullValue)) + assertThat("Found parse errors: " + result.eResource.errors, result.eResource.errors, is(empty)) + + val derivedModel = result.modelCasts.head + val traces = derivedModel.srcGTS.findTracesTo(derivedModel.tgtGTS) + assertThat("Too many traces found", traces, hasSize(1)) + + assertThat("Trace too long", traces.head, hasSize(4)) + assertThat("Incorrect trace", traces.head, isTraceWith( + result.gtss.get(2), + result.mappings.head, + result.gtss.get(3).gts, + result.gtss.get(3) + )) + + } + +// /** +// * Tests validation of model casting +// */ +// @Test +// def void validationBasicModelCastingWithSourceError() { +// // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files +// // Then would use «serverURI.toString» etc. below +// val result = parseHelper.parse(''' +// gts ServerSystem { +// metamodel: "server" +// behaviour: "serverRules" +// } +// +// gts ServerInterface interface_of { ServerSystem } +// +// gts DEVSMMSystem { +// metamodel: "devsmm" +// behaviour: "devsmmRules" +// } +// +// map ServerToDEVSMM { +// from ServerInterface +// to DEVSMMSystem +// +// type_mapping { +// class server.Server => devsmm.Machine +// reference server.Server.Out => devsmm.Machine.out +// } +// +// behaviour_mapping { +// rule process to process { +// object input => in_part +// link [in_queue->input:elts] => [tray->in_part:parts] +// } +// } +// } +// +// gts DEVSMMWithServer { +// weave: { +// map1: interface_of (ServerSystem) +// map2: ServerToDEVSMM +// } +// } +// +// model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of ServerSystem) as DEVSMMWithServer +// ''', createModelResourceSet) +// assertNotNull("Did not produce parse result", result) +// assertTrue("Found parse errors: " + result.eResource.errors, result.eResource.errors.isEmpty) +// +// result.eContents.head.assertError(GtsMorpherPackage.Literals.MODEL_CAST, GTSMorpherValidator.MODEL_CAST_INVALID_SOURCE_TYPE) +// } +// +// /** +// * Tests validation of model casting +// */ +// @Test +// def void validationBasicModelCastingTargetTypeError() { +// // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files +// // Then would use «serverURI.toString» etc. below +// val result = parseHelper.parse(''' +// gts ServerSystem { +// metamodel: "server" +// behaviour: "serverRules" +// } +// +// gts ServerInterface interface_of { ServerSystem } +// +// gts DEVSMMSystem { +// metamodel: "devsmm" +// behaviour: "devsmmRules" +// } +// +// map ServerToDEVSMM { +// from ServerInterface +// to DEVSMMSystem +// +// type_mapping { +// class server.Server => devsmm.Machine +// reference server.Server.Out => devsmm.Machine.out +// } +// +// behaviour_mapping { +// rule process to process { +// object input => in_part +// link [in_queue->input:elts] => [tray->in_part:parts] +// } +// } +// } +// +// gts DEVSMMWithServer { +// weave: { +// map1: interface_of (ServerSystem) +// map2: ServerToDEVSMM +// } +// } +// +// model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of DEVSMMSystem) as ServerSystem +// ''', createModelResourceSet) +// assertNotNull("Did not produce parse result", result) +// assertTrue("Found parse errors: " + result.eResource.errors, result.eResource.errors.isEmpty) +// +// +// } +} diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/devsmm.henshin b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/devsmm.henshin new file mode 100644 index 00000000..bc420ed2 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/devsmm.henshin @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/devsmm.henshin_diagram b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/devsmm.henshin_diagram new file mode 100644 index 00000000..0305e778 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/devsmm.henshin_diagram @@ -0,0 +1,175 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.ecore b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.ecore new file mode 100644 index 00000000..83ad5add --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.ecore @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.emf b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.emf new file mode 100644 index 00000000..240beda5 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.emf @@ -0,0 +1,37 @@ +@namespace(uri="http://server/1.0", prefix="server") +package server; + +@Interface +class Server { + @Interface + ref Queue[*] Out; + @Interface + ref Queue[*] In; +} + +@Interface +class Queue { + @Interface + ref Element[*] elts; + @Interface + attr EInt count1; + attr EInt count2; +} + +@Interface +class Element { +} + +@Interface +class Input extends Element { +} + +@Interface +class Output extends Element { +} + +class ServerObserver { + ref Server[1] server; + ref Element[*] produced; +} + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin new file mode 100644 index 00000000..60bbfd81 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin_diagram b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin_diagram new file mode 100644 index 00000000..836f8a21 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin_diagram @@ -0,0 +1,352 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher/META-INF/MANIFEST.MF b/uk.ac.kcl.inf.gts_morpher/META-INF/MANIFEST.MF index 6bc07339..af6941cd 100644 --- a/uk.ac.kcl.inf.gts_morpher/META-INF/MANIFEST.MF +++ b/uk.ac.kcl.inf.gts_morpher/META-INF/MANIFEST.MF @@ -34,5 +34,6 @@ Export-Package: uk.ac.kcl.inf.gts_morpher, uk.ac.kcl.inf.gts_morpher.serializer, uk.ac.kcl.inf.gts_morpher.services, uk.ac.kcl.inf.gts_morpher.util, - uk.ac.kcl.inf.gts_morpher.validation + uk.ac.kcl.inf.gts_morpher.validation, + uk.ac.kcl.inf.gts_morpher.modelcaster Import-Package: org.apache.log4j diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend index 71e07478..1976b824 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend @@ -73,7 +73,8 @@ class GTSTrace extends ArrayList { visited += target val traces = if (source === target) { - #{new GTSTrace(source, target)} + // FIXME: This is wrong, as this way every non-trivial trace will start with a duplicate occurence of the source GTS + #{new GTSTrace(source, target)} } else { expandAll(target.stepDown.flatMap[source.collectTraces(it, visited)].toSet, target) } From 9de357797b60ea36a0a1a96856e08bc3600b72d4 Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Tue, 29 Jun 2021 16:18:10 +0100 Subject: [PATCH 12/14] Fixed bug in trace creation --- .../inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend | 3 +-- .../src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend index 40778d28..7c0788d6 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend @@ -12,13 +12,12 @@ import uk.ac.kcl.inf.gts_morpher.tests.GTSMorpherInjectorProvider import static org.hamcrest.CoreMatchers.* import static org.hamcrest.MatcherAssert.* -import static uk.ac.kcl.inf.gts_morpher.tests.IsEmpty.* import static uk.ac.kcl.inf.gts_morpher.tests.HasSize.* +import static uk.ac.kcl.inf.gts_morpher.tests.IsEmpty.* import static uk.ac.kcl.inf.gts_morpher.tests.IsTraceWith.* import static extension uk.ac.kcl.inf.gts_morpher.modelcaster.GTSTrace.* import static extension uk.ac.kcl.inf.gts_morpher.util.GTSSpecificationHelper.* -import org.eclipse.xtext.debug.IStratumBreakpointSupport.DefaultImpl @RunWith(XtextRunner) @InjectWith(GTSMorpherInjectorProvider) diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend index 1976b824..80b453db 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend @@ -73,8 +73,7 @@ class GTSTrace extends ArrayList { visited += target val traces = if (source === target) { - // FIXME: This is wrong, as this way every non-trivial trace will start with a duplicate occurence of the source GTS - #{new GTSTrace(source, target)} + #{new GTSTrace(source)} } else { expandAll(target.stepDown.flatMap[source.collectTraces(it, visited)].toSet, target) } From 6a455c7fed45d4ca2fd324e64c124633726110b1 Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Tue, 29 Jun 2021 17:06:16 +0100 Subject: [PATCH 13/14] Test for model transformation --- .../inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend | 2 ++ .../src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend index 7c0788d6..a47ae272 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend @@ -90,6 +90,8 @@ class ModelcasterTest extends AbstractTest { result.gtss.get(3) )) + val transformedModel = traces.head.transformedModel + assertThat("No transformed model", transformedModel, is(notNullValue)) } // /** diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend index 80b453db..273f22ba 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend @@ -4,6 +4,7 @@ import java.util.ArrayList import java.util.HashMap import java.util.HashSet import java.util.Set +import org.eclipse.emf.ecore.EObject import org.eclipse.xtext.util.OnChangeEvictingCache import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSFamilyChoice import uk.ac.kcl.inf.gts_morpher.gtsMorpher.GTSFamilyReference @@ -52,6 +53,10 @@ class GTSTrace extends ArrayList { def getSource() { head as GTSSpecification } def getTarget() { last as GTSSpecification } + + def EObject getTransformedModel() { + null + } private static class GTSTraceHelper extends HashMap> { def Set findTraces(GTSSpecification source, GTSSpecification target) { From a69c10b943859f783b3566db0a1eb5942e3b7387 Mon Sep 17 00:00:00 2001 From: Steffen Zschaler Date: Tue, 6 Jul 2021 14:32:39 +0100 Subject: [PATCH 14/14] Towards a test case for model transform --- .../src/pls.ecore | 6 + .../src/pls.henshin_diagram | 2 +- .../src/server.ecore | 11 + .../src/server_polish.gts | 1 + .../tests/modelcaster/ModelcasterTest.xtend | 206 ++++---- .../gts_morpher/tests/modelcaster/pls.ecore | 25 + .../inf/gts_morpher/tests/modelcaster/pls.emf | 55 ++ .../gts_morpher/tests/modelcaster/pls.henshin | 55 ++ .../tests/modelcaster/pls.henshin_diagram | 113 ++++ .../tests/modelcaster/server.ecore | 29 ++ .../gts_morpher/tests/modelcaster/server.emf | 32 ++ .../tests/modelcaster/server.henshin | 58 +++ .../tests/modelcaster/server.henshin_diagram | 123 +++++ .../tests/modelcaster/server_polish.gts | 46 ++ .../tests/modelcaster/storing_server.ecore | 2 +- .../tests/modelcaster/storing_server.emf | 4 +- .../tests/modelcaster/storing_server.henshin | 2 +- .../tests/modelcaster/transformers.henshin | 493 ++++++++++++++++++ .../modelcaster/transformers.henshin_diagram | 474 +++++++++++++++++ .../gts_morpher/modelcaster/GTSTrace.xtend | 33 +- 20 files changed, 1655 insertions(+), 115 deletions(-) create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.ecore create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.emf create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.henshin create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.henshin_diagram create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.ecore create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.emf create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.henshin create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.henshin_diagram create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server_polish.gts create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/transformers.henshin create mode 100644 uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/transformers.henshin_diagram diff --git a/uk.ac.kcl.inf.gts_morpher.examples/src/pls.ecore b/uk.ac.kcl.inf.gts_morpher.examples/src/pls.ecore index f86ade37..844e7690 100644 --- a/uk.ac.kcl.inf.gts_morpher.examples/src/pls.ecore +++ b/uk.ac.kcl.inf.gts_morpher.examples/src/pls.ecore @@ -22,4 +22,10 @@ + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.examples/src/pls.henshin_diagram b/uk.ac.kcl.inf.gts_morpher.examples/src/pls.henshin_diagram index 32314fee..052b9126 100644 --- a/uk.ac.kcl.inf.gts_morpher.examples/src/pls.henshin_diagram +++ b/uk.ac.kcl.inf.gts_morpher.examples/src/pls.henshin_diagram @@ -1,5 +1,5 @@ - +
diff --git a/uk.ac.kcl.inf.gts_morpher.examples/src/server.ecore b/uk.ac.kcl.inf.gts_morpher.examples/src/server.ecore index 6b4f0fa4..a7ec6067 100644 --- a/uk.ac.kcl.inf.gts_morpher.examples/src/server.ecore +++ b/uk.ac.kcl.inf.gts_morpher.examples/src/server.ecore @@ -26,4 +26,15 @@ + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.examples/src/server_polish.gts b/uk.ac.kcl.inf.gts_morpher.examples/src/server_polish.gts index a8a8d09d..29f72b2f 100644 --- a/uk.ac.kcl.inf.gts_morpher.examples/src/server_polish.gts +++ b/uk.ac.kcl.inf.gts_morpher.examples/src/server_polish.gts @@ -34,6 +34,7 @@ auto-complete unique map Server2PLS { class server.Server => pls.Polisher class server.InputQueue => pls.Tray class server.OutputQueue => pls.Conveyor + reference server.ServerModel.servers => pls.PLSModel.machines } } diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend index a47ae272..8ec69fea 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend @@ -26,7 +26,16 @@ class ModelcasterTest extends AbstractTest { ParseHelper parseHelper private def createModelResourceSet() { - #["storing_server.ecore", "DEVSMM.ecore", "DEVSModel.xmi", "storing_server.henshin", "devsmm.henshin"].createResourceSet + #["storing_server.ecore", + "DEVSMM.ecore", + "DEVSModel.xmi", + "storing_server.henshin", + "devsmm.henshin", + "pls.ecore", + "pls.henshin", + "server.ecore", + "server.henshin", + "transformers.henshin"].createResourceSet } /** @@ -36,10 +45,11 @@ class ModelcasterTest extends AbstractTest { def void validationBasicTrace() { // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files // Then would use «serverURI.toString» etc. below + val rs = createModelResourceSet val result = parseHelper.parse(''' gts ServerSystem { - metamodel: "server" - behaviour: "serverRules" + metamodel: "storing_server" + behaviour: "storing_serverRules" } gts ServerInterface interface_of { ServerSystem } @@ -74,7 +84,7 @@ class ModelcasterTest extends AbstractTest { } model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of DEVSMMSystem) as DEVSMMWithServer - ''', createModelResourceSet) + ''', rs) assertThat ("Did not produce parse result", result, is(notNullValue)) assertThat("Found parse errors: " + result.eResource.errors, result.eResource.errors, is(empty)) @@ -90,111 +100,91 @@ class ModelcasterTest extends AbstractTest { result.gtss.get(3) )) - val transformedModel = traces.head.transformedModel + val wovenGTS = result.gtss.last + val wovenMetamodel = wovenGTS.metamodel + + val transformedModel = traces.head.transformModel(rs.getResource("DEVSModel.xmi".createFileURI, true).contents.head) assertThat("No transformed model", transformedModel, is(notNullValue)) + assertThat("Incorrect metamodel of transformed model", transformedModel.eClass.EPackage, is(sameInstance(wovenMetamodel))) } -// /** -// * Tests validation of model casting -// */ -// @Test -// def void validationBasicModelCastingWithSourceError() { -// // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files -// // Then would use «serverURI.toString» etc. below -// val result = parseHelper.parse(''' -// gts ServerSystem { -// metamodel: "server" -// behaviour: "serverRules" -// } -// -// gts ServerInterface interface_of { ServerSystem } -// -// gts DEVSMMSystem { -// metamodel: "devsmm" -// behaviour: "devsmmRules" -// } -// -// map ServerToDEVSMM { -// from ServerInterface -// to DEVSMMSystem -// -// type_mapping { -// class server.Server => devsmm.Machine -// reference server.Server.Out => devsmm.Machine.out -// } -// -// behaviour_mapping { -// rule process to process { -// object input => in_part -// link [in_queue->input:elts] => [tray->in_part:parts] -// } -// } -// } -// -// gts DEVSMMWithServer { -// weave: { -// map1: interface_of (ServerSystem) -// map2: ServerToDEVSMM -// } -// } -// -// model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of ServerSystem) as DEVSMMWithServer -// ''', createModelResourceSet) -// assertNotNull("Did not produce parse result", result) -// assertTrue("Found parse errors: " + result.eResource.errors, result.eResource.errors.isEmpty) -// -// result.eContents.head.assertError(GtsMorpherPackage.Literals.MODEL_CAST, GTSMorpherValidator.MODEL_CAST_INVALID_SOURCE_TYPE) -// } -// -// /** -// * Tests validation of model casting -// */ -// @Test -// def void validationBasicModelCastingTargetTypeError() { -// // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files -// // Then would use «serverURI.toString» etc. below -// val result = parseHelper.parse(''' -// gts ServerSystem { -// metamodel: "server" -// behaviour: "serverRules" -// } -// -// gts ServerInterface interface_of { ServerSystem } -// -// gts DEVSMMSystem { -// metamodel: "devsmm" -// behaviour: "devsmmRules" -// } -// -// map ServerToDEVSMM { -// from ServerInterface -// to DEVSMMSystem -// -// type_mapping { -// class server.Server => devsmm.Machine -// reference server.Server.Out => devsmm.Machine.out -// } -// -// behaviour_mapping { -// rule process to process { -// object input => in_part -// link [in_queue->input:elts] => [tray->in_part:parts] -// } -// } -// } -// -// gts DEVSMMWithServer { -// weave: { -// map1: interface_of (ServerSystem) -// map2: ServerToDEVSMM -// } -// } -// -// model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of DEVSMMSystem) as ServerSystem -// ''', createModelResourceSet) -// assertNotNull("Did not produce parse result", result) -// assertTrue("Found parse errors: " + result.eResource.errors, result.eResource.errors.isEmpty) -// -// -// } + /** + * Test trace generation + */ + @Test + def void validationBasicModelTransform() { + // TODO At some point may want to change this so it works with actual URLs rather than relying on Xtext/Ecore to pick up and search all the available ecore files + // Then would use «serverURI.toString» etc. below + val rs = createModelResourceSet + val result = parseHelper.parse(''' + gts_family ServerFamily { + { + metamodel: "server" + behaviour: "serverRules" + } + + transformers: "transformerRules" + } + + export gts AdaptedServer { + family: ServerFamily + + using [ + addSubClass (server.Queue, "InputQueue"), + addSubClass (server.Queue, "OutputQueue"), + reTypeToSubClass (serverRules.process, server.Queue, server.InputQueue, "iq"), + reTypeToSubClass (serverRules.process, server.Queue, server.OutputQueue, "oq"), + mvAssocDown (server.Server.in, server.InputQueue), + mvAssocDown (server.Server.out, server.OutputQueue) + ] + } + + auto-complete unique map Server2PLS { + from interface_of { + AdaptedServer + } + + to gts pls { + metamodel: "pls" + behaviour: "plsRules" + } + + type_mapping { + class server.Server => pls.Polisher + class server.InputQueue => pls.Tray + class server.OutputQueue => pls.Conveyor + } + } + + export gts ServerPLS { + weave(dontLabelNonKernelElements,preferMap2TargetNames): { + map1: interface_of(AdaptedServer) + map2: Server2PLS + } + } + + model derivedModel = "«class.getResource("DEVSModel.xmi").path»" (instance of pls) as ServerPLS + ''', rs) + assertThat ("Did not produce parse result", result, is(notNullValue)) + assertThat("Found parse errors: " + result.eResource.errors, result.eResource.errors, is(empty)) + + val derivedModel = result.modelCasts.head + val traces = derivedModel.srcGTS.findTracesTo(derivedModel.tgtGTS) + assertThat("Too many traces found", traces, hasSize(1)) + + assertThat("Trace too long", traces.head, hasSize(4)) + assertThat("Incorrect trace", traces.head, isTraceWith( + result.gtss.get(2), + result.mappings.head, + result.gtss.get(3).gts, + result.gtss.get(3) + )) + + val wovenGTS = result.gtss.last + val wovenMetamodel = wovenGTS.metamodel + + val transformedModel = traces.head.transformModel(rs.getResource("DEVSModel.xmi".createFileURI, true).contents.head) + assertThat("No transformed model", transformedModel, is(notNullValue)) + assertThat("Incorrect metamodel of transformed model", transformedModel.eClass.EPackage, is(sameInstance(wovenMetamodel))) + } } diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.ecore b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.ecore new file mode 100644 index 00000000..b8003066 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.ecore @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.emf b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.emf new file mode 100644 index 00000000..ef306c91 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.emf @@ -0,0 +1,55 @@ +@namespace(uri="http://pls/1.0", prefix="pls") +package pls; + +class Machine { + ref Conveyor out; + ref Tray in; +} + +class Conveyor extends Container { + ref Tray out; +} + +class Tray extends Container { + +} + +class Container { + ref Part[*] parts; +} + +class Polisher extends Machine { + +} + +class Assemble extends Machine { + +} + +class Generator extends Machine { + +} + +class GenHead extends Generator { + +} + +class GenHandle extends Generator { + +} + +class Part { + +} + +class Hammer extends Part { + +} + +class Head extends Part { + +} + +class Handle extends Part { + +} \ No newline at end of file diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.henshin b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.henshin new file mode 100644 index 00000000..e8a469b8 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.henshin @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.henshin_diagram b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.henshin_diagram new file mode 100644 index 00000000..2251c0f8 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/pls.henshin_diagram @@ -0,0 +1,113 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.ecore b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.ecore new file mode 100644 index 00000000..99bcef8d --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.ecore @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.emf b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.emf new file mode 100644 index 00000000..0364b9fa --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.emf @@ -0,0 +1,32 @@ +@namespace(uri="http://server/1.0", prefix="server") +package server; + +@Interface +class Server { + @Interface + ref Queue in; + @Interface + ref Queue out; + ref Output made; +} + +@Interface +class Queue { + @Interface + ref Element[*] elts; +} + +@Interface +abstract class Element { + +} + +@Interface +class Input extends Element { + +} + +@Interface +class Output extends Element { + +} \ No newline at end of file diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.henshin b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.henshin new file mode 100644 index 00000000..04e50369 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.henshin @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.henshin_diagram b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.henshin_diagram new file mode 100644 index 00000000..8649607c --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server.henshin_diagram @@ -0,0 +1,123 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server_polish.gts b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server_polish.gts new file mode 100644 index 00000000..47b89c3c --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/server_polish.gts @@ -0,0 +1,46 @@ +gts_family ServerFamily { + { + metamodel: "server" + behaviour: "serverRules" + } + + transformers: "transformerRules" +} + +export gts AdaptedServer { + family: ServerFamily + + using [ + addSubClass (server.Queue, "InputQueue"), + addSubClass (server.Queue, "OutputQueue"), + reTypeToSubClass (serverRules.process, server.Queue, server.InputQueue, "iq"), + reTypeToSubClass (serverRules.process, server.Queue, server.OutputQueue, "oq"), + mvAssocDown (server.Server.in, server.InputQueue), + mvAssocDown (server.Server.out, server.OutputQueue) + ] +} + +auto-complete unique map Server2PLS { + from interface_of { + AdaptedServer + } + + to { + metamodel: "pls" + behaviour: "plsRules" + } + + type_mapping { + class server.Server => pls.Polisher + class server.InputQueue => pls.Tray + class server.OutputQueue => pls.Conveyor + } +} + +export gts ServerPLS { + weave(dontLabelNonKernelElements,preferMap2TargetNames): { + map1: interface_of(AdaptedServer) + map2: Server2PLS + } +} + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.ecore b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.ecore index 83ad5add..ec3bb874 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.ecore +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.ecore @@ -1,6 +1,6 @@ + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="storing_server" nsURI="http://storing_server/1.0" nsPrefix="storing_server"> diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.emf b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.emf index 240beda5..4324c58f 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.emf +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.emf @@ -1,5 +1,5 @@ -@namespace(uri="http://server/1.0", prefix="server") -package server; +@namespace(uri="http://storing_server/1.0", prefix="storing_server") +package storing_server; @Interface class Server { diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin index 60bbfd81..85f9a3f5 100644 --- a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/storing_server.henshin @@ -1,5 +1,5 @@ - + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/transformers.henshin b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/transformers.henshin new file mode 100644 index 00000000..d605542a --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/transformers.henshin @@ -0,0 +1,493 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/transformers.henshin_diagram b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/transformers.henshin_diagram new file mode 100644 index 00000000..1774e931 --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/transformers.henshin_diagram @@ -0,0 +1,474 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend index 273f22ba..9276aacb 100644 --- a/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend @@ -54,8 +54,37 @@ class GTSTrace extends ArrayList { def getTarget() { last as GTSSpecification } - def EObject getTransformedModel() { - null + def EObject transformModel(EObject srcModel) { + new GTSTraceEvaluator(srcModel, this).get(srcModel) + } + + /* + * FIXME: This is wrong: I really need something that will collapse a trace into a mapping from source meta-model elements into target + * meta-model elements and then I use this to transform source models + */ + private static class GTSTraceEvaluator extends HashMap { + var EObject srcModel + var GTSTrace trace + + new(EObject srcModel, GTSTrace trace) { + this.srcModel = srcModel + this.trace = trace + + doTransform + } + + private def doTransform() { + trace.drop(1) // Forget the source GTS + .forEach[doOneTransform] + } + +// private dispatch def doOneTransform (GTSTraceMember step) { } + private dispatch def doOneTransform (GTSMapping step) { } + private dispatch def doOneTransform (GTSSpecification step) { } + private dispatch def doOneTransform (GTSLiteral step) { } + private dispatch def doOneTransform (GTSFamilyChoice step) { } + private dispatch def doOneTransform (GTSReference step) { } + private dispatch def doOneTransform (GTSWeave step) { } } private static class GTSTraceHelper extends HashMap> {