javaeclipseeclipse-emfmdeeclipse-atl

How to get relative paths in the traces model when executing ATL transformation programmatically through EMFTVM?


Consider the basic ATL transformation Families2Persons, running it through EMFTVM and getting the traces as explained in the docs.

-- @atlcompiler emftvm
-- @path Families=/Test/Families.ecore
-- @path Persons=/Test/Persons.ecore

module Families2Persons;

create OUT: Persons, trace: Trace from IN: Families;

rule Member2Male {
    from
        s: Families!Member (not s.isFemale())
    to
        t: Persons!Male (
            fullName <- s.firstName + ' ' + s.familyName
        )
}

rule Member2Female {
    from
        s: Families!Member (s.isFemale())
    to
        t: Persons!Female (
            fullName <- s.firstName + ' ' + s.familyName
        )
}

I omitted the helper functions to save space, but I didn't change them compared with the tutorial example

When I ran the transformation using my Eclipse environment, everything worked fine and I got the result model and the traces as expected. Below is a snippet of the traces.xmi generated after running it.

   <links>
      <sourceElements name="s" defaultFor="/">
        <object href="../sample-Families.xmi#/0/@father"/>
      </sourceElements>
      <targetElements name="t">
        <object href="../person-emftvm.xmi#/0"/>
      </targetElements>
    </links>

Trying to run the same transformation programmatically (also adapting the Java code from the docs), I got the same model output, but my traces model includes the absolute path instead of the relative path like the previous example. Snippets below:

Java code:

public class RunTestTransformation {

    public static String here = new File(".").getAbsolutePath();

    public static URI resourceURI(String relativePath) {
        return URI.createFileURI(here + relativePath);
    }

    public static void main(String[] args) throws IOException {

        Map<String, Object> map = Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap();
        map.put("xmi", new XMIResourceFactoryImpl());
        map.put("ecore", new EcoreResourceFactoryImpl());
        map.put("emftvm", new EMFTVMResourceFactoryImpl());

        ExecEnv env = EmftvmFactory.eINSTANCE.createExecEnv();
        ResourceSet rs = new ResourceSetImpl();

        // Register the metamodels into resource set
        EPackage FamiliesPkg = (EPackage) rs.getResource(resourceURI("/../Test/Families.ecore"), true).getContents()
                .get(0);
        EPackage.Registry.INSTANCE.put(FamiliesPkg.getNsURI(), FamiliesPkg);
        EPackage PersonsPkg = (EPackage) rs.getResource(resourceURI("/../Test/Persons.ecore"), true).getContents()
                .get(0);
        EPackage.Registry.INSTANCE.put(PersonsPkg.getNsURI(), PersonsPkg);

        // Load metamodels into execenv
        Metamodel familiesMetaModel = EmftvmFactory.eINSTANCE.createMetamodel();
        familiesMetaModel.setResource(rs.getResource(resourceURI("/../Test/Families.ecore"), true));
        env.registerMetaModel("Families", familiesMetaModel);

        Metamodel personsMetaModel = EmftvmFactory.eINSTANCE.createMetamodel();
        personsMetaModel.setResource(rs.getResource(resourceURI("/../Test/Persons.ecore"), true));
        env.registerMetaModel("Persons", personsMetaModel);

        String relativeInputPath = "/../Test/sample-Families.xmi";
        String relativeTracePath = "/../Test/traces.xmi";
        String relativeOutputPath = "/../Test/persons.xmi";

        // Load models
        URI inputUri = resourceURI(relativeInputPath);
        Model inModel = EmftvmFactory.eINSTANCE.createModel();
        inModel.setResource(rs.getResource(inputUri, true));
        env.registerInputModel("IN", inModel);

        URI uriTrace = resourceURI(relativeTracePath);
        Model traceOutModel = EmftvmFactory.eINSTANCE.createModel();
        traceOutModel.setResource(rs.createResource(uriTrace));
        env.registerOutputModel("trace", traceOutModel);

        URI uriOut = resourceURI(relativeOutputPath);
        Model outModel = EmftvmFactory.eINSTANCE.createModel();
        outModel.setResource(rs.createResource(uriOut));
        env.registerOutputModel("OUT", outModel);

        // Load and run module
        ModuleResolver mr = new DefaultModuleResolver("./../Test/emftvm/", rs);
        TimingData td = new TimingData();
        env.loadModule(mr, "Families2Persons");
        td.finishLoading();
        env.run(td);
        td.finish();

        // Save models
        inModel.getResource().save(Collections.emptyMap());
        traceOutModel.getResource().save(Collections.emptyMap());
        outModel.getResource().save(Collections.emptyMap());
    }
}

Output trace excerpt:

   <links>
      <sourceElements name="s" defaultFor="/">
        <object href="file:/C:/Users/XXXX/./../Test/sample-Families.xmi#/0/@father"/>
      </sourceElements>
      <targetElements name="t">
        <object href="file:/C:/Users/XXXX/./../Test/persons.xmi#/0"/>
      </targetElements>
    </links>

How can I get the same result for traces both running through Eclipse and programmatically? I can understand that the problem is that my Eclipse environment knows about my workspace, but I don't know how to configure my Java code to behave similarly.

I've tried

  1. Change createFileURI() by createURI() -> Compilation error
  2. Different XMLResource options in the save() method -> Not effective
  3. Pass direct my "relativepath" to createFileURI() instead of appending it to the current Java file location (see method resourceURI() in the code) -> File not found

Solution

  • I guess the issue is with how you are loading your models programmatically. Currently you are using file URIs. I guess ATL being installed in your current running Eclipse, works mostly with 'platform:/resource' URIs. And probably when serializing the trace model, EMF identifies that both Resources are not far from each other and thus prefers serializing with relative URIs.

    So I think the fix depends on whether your code is supposed to be a standalone Java application or embedded in Eclipse. If the latter, then simply loading models with 'platform:/resource' might maybe work. Of course you will have to find some way to have your files into your running instance workspace location. If the former, I think you maybe have to play with the org.eclipse.emf.ecore.resource.URIConverter of your ResourceSet