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/.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 @@ - + - + - + 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..8ec69fea --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher.tests/src/uk/ac/kcl/inf/gts_morpher/tests/modelcaster/ModelcasterTest.xtend @@ -0,0 +1,190 @@ +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.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.* + +@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", + "pls.ecore", + "pls.henshin", + "server.ecore", + "server.henshin", + "transformers.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 rs = createModelResourceSet + val result = parseHelper.parse(''' + gts ServerSystem { + metamodel: "storing_server" + behaviour: "storing_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 + ''', 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))) + } + + /** + * 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/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/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 new file mode 100644 index 00000000..ec3bb874 --- /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..4324c58f --- /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://storing_server/1.0", prefix="storing_server") +package storing_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..85f9a3f5 --- /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.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.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..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 @@ -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,218 @@ 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) + } + + /** + * 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) + result.assertNoError(GTSMorpherValidator.MODEL_CAST_INVALID_TARGET_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) + } + + /** + * 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.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/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/model/generated/GTSMorpher.ecore b/uk.ac.kcl.inf.gts_morpher/model/generated/GTSMorpher.ecore index 28380713..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 @@ - + + @@ -145,4 +146,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..a81508b8 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"> @@ -68,6 +68,7 @@ + @@ -132,5 +133,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..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 @@ -12,7 +12,8 @@ GTSSpecificationModule: GTSSpecificationModuleMember: GTSSpecification | GTSFamilySpecification | - GTSMapping + GTSMapping | + ModelCast ; GTSMapping: @@ -110,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)* ; @@ -197,6 +206,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/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..9276aacb --- /dev/null +++ b/uk.ac.kcl.inf.gts_morpher/src/uk/ac/kcl/inf/gts_morpher/modelcaster/GTSTrace.xtend @@ -0,0 +1,162 @@ +package uk.ac.kcl.inf.gts_morpher.modelcaster + +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 +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. + * 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. + */ +class GTSTrace extends ArrayList { + + static val traceCache = new OnChangeEvictingCache + + /** + * Constructs the set of traces through which source can be transformed into target. + */ + static def Set findTracesTo(GTSSpecification source, GTSSpecification target) { + val key = source -> target + + traceCache.get(key, source.eResource) [ + traceCache.get(key, target.eResource) [ + new GTSTraceHelper().findTraces(source, target) + ] + ] + } + + private new(GTSTraceMember... trace) { + super() + this += trace + } + + def getSource() { head as GTSSpecification } + + def getTarget() { last as GTSSpecification } + + 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> { + def Set findTraces(GTSSpecification source, GTSSpecification target) { + val Set visited = new HashSet + + collectTraces(source, target, visited) + } + + 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 GTSTrace(source)} + } 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 GTSTrace(it + #{newTarget})].toSet + } + + private dispatch def Iterable stepDown(GTSTraceMember node) { #[] } + + private dispatch def Iterable stepDown(GTSSpecification node) { #[node.gts] } + + private dispatch def Iterable stepDown(GTSMapping node) { + #[node.source.resolve, node.target.resolve] + } + + private dispatch def Iterable stepDown(GTSLiteral node) { #[] } + + private dispatch def Iterable stepDown(GTSFamilyChoice node) { #[node.family.resolve] } + + private dispatch def Iterable stepDown(GTSReference node) { #[node.ref] } + + private dispatch def Iterable stepDown(GTSWeave node) { + #[node.mapping1.resolve, node.mapping2.resolve] + } + + 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/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 } 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) } 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..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 @@ -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 @@ -52,20 +58,19 @@ 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 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 +107,8 @@ 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' + 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 @@ -490,6 +497,51 @@ class GTSMorpherValidator extends AbstractGTSMorpherValidator implements GTSMorp ] } + @Check + def checkModelCast(ModelCast modelCast) { + modelCast.checkModelSourceType + modelCast.checkModelTargetType + } + + 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 def checkModelTargetType(ModelCast modelCast) { + if (modelCast.tgtGTS !== null) { + if (modelCast.srcGTS !== null) { + if (!modelCast.tgtGTS.canBeDerivedFrom(modelCast.srcGTS)) { + error('''Target GTS cannot be uniquely 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 + } + } + } + + private def canBeDerivedFrom(GTSSpecification target, GTSSpecification source) { + source.findTracesTo(target).size == 1 + } + private dispatch def checkIsValidMapping(Void spec) {} private dispatch def checkIsValidMapping(GTSMappingRefOrInterfaceSpec spec) { @@ -533,14 +585,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 +649,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) }