如何通过EMFTVM编程执行ATL转换时获取跟踪模型相对路径
问题背景
以基础ATL转换Families2Persons为例,其ATL代码如下:
-- @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 ) }
(注:为节省篇幅省略了辅助函数,与官方教程示例一致)
在Eclipse环境中通过EMFTVM运行该转换时,生成的trace.xmi片段引用路径为相对路径,符合预期:
<links> <sourceElements name="s" defaultFor="/"> <object href="../sample-Families.xmi#/0/@father"/> </sourceElements> <targetElements name="t"> <object href="../person-emftvm.xmi#/0"/> </targetElements> </links>
但通过Java代码调用EMFTVM API执行转换时,输出模型内容正确,但跟踪模型中的引用路径变成了绝对路径:
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()); } }
生成的跟踪模型片段如下:
<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>
问题需求
如何修改Java代码,使编程执行ATL转换时生成的跟踪模型引用路径与Eclipse运行结果一致,使用相对路径?
已尝试方法
- 将
createFileURI()替换为createURI(),出现编译错误; - 在
save()方法中使用不同XMLResource选项,无效果; - 直接向
createFileURI()传入相对路径,出现文件未找到错误。
解决方案
问题核心在于EMFTVM生成跟踪引用时,会根据资源的URI类型决定使用绝对还是相对路径。当前代码通过createFileURI()生成了绝对文件URI,导致跟踪引用也采用绝对路径。要生成相对路径,需让所有资源的URI基于同一基准目录,并配置EMF使用相对路径规则。
步骤1:修正资源URI生成逻辑
放弃通过绝对路径拼接的方式,基于项目基准目录生成相对URI,避免冗余路径片段:
public static File baseDir; static { try { // 获取标准基准目录,自动去除./../这类冗余片段 baseDir = new File(".").getCanonicalFile(); } catch (IOException e) { e.printStackTrace(); } } public static URI resourceURI(String relativePath) { File targetFile = new File(baseDir, relativePath); // 生成基于基准目录的相对URI return URI.createURI(targetFile.toURI().toString()).makeRelativeTo(baseDir.toURI()); }
步骤2:配置ResourceSet的URI转换器
给ResourceSet添加URI规范化配置,确保路径解析和生成的一致性:
ResourceSet rs = new ResourceSetImpl(); // 启用URI规范化,自动处理路径冗余 rs.getURIConverter().getOptions().put(URIConverter.OPTION_NORMALIZE, true);
步骤3:保存模型时强制使用相对路径
保存trace模型时,添加OPTION_USE_RELATIVE_LINKS选项,让EMF生成相对路径引用:
// 构建保存配置 Map<String, Object> saveOptions = new HashMap<>(); saveOptions.put(XMLResource.OPTION_USE_RELATIVE_LINKS, true); saveOptions.put(XMLResource.OPTION_FORMATTED, true); // 可选,格式化输出XML // 应用配置保存模型 traceOutModel.getResource().save(saveOptions); outModel.getResource().save(saveOptions); inModel.getResource().save(saveOptions);
完整修正后的关键代码片段
public class RunTestTransformation { public static File baseDir; static { try { baseDir = new File(".").getCanonicalFile(); } catch (IOException e) { e.printStackTrace(); } } public static URI resourceURI(String relativePath) { File targetFile = new File(baseDir, relativePath); return URI.createURI(targetFile.toURI().toString()).makeRelativeTo(baseDir.toURI()); } 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(); // 配置URI转换器,启用路径规范化 rs.getURIConverter().getOptions().put(URIConverter.OPTION_NORMALIZE, true); // 注册元模型,调整相对路径格式 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); // 加载元模型到执行环境 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"; // 加载输入模型 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); // 加载并运行转换模块 ModuleResolver mr = new DefaultModuleResolver("Test/emftvm/", rs); TimingData td = new TimingData(); env.loadModule(mr, "Families2Persons"); td.finishLoading(); env.run(td); td.finish(); // 保存模型,使用相对路径配置 Map<String, Object> saveOptions = new HashMap<>(); saveOptions.put(XMLResource.OPTION_USE_RELATIVE_LINKS, true); saveOptions.put(XMLResource.OPTION_FORMATTED, true); inModel.getResource().save(saveOptions); traceOutModel.getResource().save(saveOptions); outModel.getResource().save(saveOptions); } }
原理说明
- 基准目录规范化:
getCanonicalFile()会将当前目录转换为标准路径,自动去除./、../这类冗余片段,确保所有资源路径基于统一基准。 - 相对URI生成:
makeRelativeTo()方法将绝对URI转换为相对于基准目录的相对URI,为后续生成相对引用打下基础。 - 保存选项控制:
OPTION_USE_RELATIVE_LINKS是核心配置,告诉EMF在保存资源时,优先使用相对路径引用其他关联资源,最终生成与Eclipse运行一致的跟踪模型。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

