From 58e4eae442b85777a62cb53fa0d414df7a4db4f9 Mon Sep 17 00:00:00 2001 From: Stefan Kapferer Date: Mon, 19 Oct 2026 23:10:55 +0200 Subject: [PATCH] Fix loading CML files with imports via relative paths (#364) StandaloneContextMapper.loadCML(String) created a relative URI from a plain file path. Resolving the imports of such a resource against its URI failed with 'resolve against non-hierarchical or relative base', e.g. when generating PlantUML diagrams with the CLI. Plain file paths are now converted into absolute file URIs; strings that already are URIs (file:, platform:, ...) are used as before. --- .../standalone/imports/BoundedContexts.cml | 22 +++++++++++++++++++ .../standalone/imports/ContextMap.cml | 7 ++++++ .../dsl/standalone/StandaloneAPITest.java | 17 ++++++++++++++ .../standalone/StandaloneContextMapper.java | 12 +++++++++- 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 org.contextmapper.dsl.tests/integ-test-files/standalone/imports/BoundedContexts.cml create mode 100644 org.contextmapper.dsl.tests/integ-test-files/standalone/imports/ContextMap.cml diff --git a/org.contextmapper.dsl.tests/integ-test-files/standalone/imports/BoundedContexts.cml b/org.contextmapper.dsl.tests/integ-test-files/standalone/imports/BoundedContexts.cml new file mode 100644 index 00000000..f6562f1d --- /dev/null +++ b/org.contextmapper.dsl.tests/integ-test-files/standalone/imports/BoundedContexts.cml @@ -0,0 +1,22 @@ +Domain Insurance { + Subdomain CustomerDomain + Subdomain PolicyDomain +} + +BoundedContext CustomerManagement implements CustomerDomain { + Aggregate Customers { + Entity Customer { + aggregateRoot + String firstname + } + } +} + +BoundedContext PolicyManagement implements PolicyDomain { + Aggregate Policies { + Entity Policy { + aggregateRoot + String policyNr + } + } +} diff --git a/org.contextmapper.dsl.tests/integ-test-files/standalone/imports/ContextMap.cml b/org.contextmapper.dsl.tests/integ-test-files/standalone/imports/ContextMap.cml new file mode 100644 index 00000000..65da91e3 --- /dev/null +++ b/org.contextmapper.dsl.tests/integ-test-files/standalone/imports/ContextMap.cml @@ -0,0 +1,7 @@ +import "./BoundedContexts.cml" + +ContextMap InsuranceContextMap { + contains CustomerManagement, PolicyManagement + + CustomerManagement [U,OHS,PL] -> [D,ACL] PolicyManagement +} diff --git a/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/standalone/StandaloneAPITest.java b/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/standalone/StandaloneAPITest.java index 29ce5579..a8beedd8 100644 --- a/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/standalone/StandaloneAPITest.java +++ b/org.contextmapper.dsl.tests/src/org/contextmapper/dsl/standalone/StandaloneAPITest.java @@ -29,6 +29,7 @@ import org.contextmapper.dsl.contextMappingDSL.ContextMappingDSLFactory; import org.contextmapper.dsl.contextMappingDSL.ContextMappingModel; import org.contextmapper.dsl.generator.ContextMapGenerator; +import org.contextmapper.dsl.generator.PlantUMLGenerator; import org.contextmapper.dsl.generator.contextmap.ContextMapFormat; import org.contextmapper.dsl.refactoring.SplitBoundedContextByOwner; import org.eclipse.xtext.resource.SaveOptions; @@ -138,6 +139,22 @@ public void canGenerateIntoCustomDirectory() { assertTrue(expectedOutput.exists()); } + @Test + public void canLoadCMLWithImportsViaRelativePathAndCallGenerator() { + // given + StandaloneContextMapperAPI contextMapper = ContextMapperStandaloneSetup.getStandaloneAPI(); + CMLResource cml = contextMapper.loadCML("./integ-test-files/standalone/imports/ContextMap.cml"); + File expectedOutput = new File("./out/imports/ContextMap_ContextMap.puml"); + ensureFileDoesNotExist(expectedOutput); + + // when + contextMapper.callGenerator(cml, new PlantUMLGenerator(), "./out/imports"); + + // then + assertTrue(cml.getURI().isFile()); + assertTrue(expectedOutput.exists()); + } + @Test public void canApplyRefactoring() throws IOException { // given diff --git a/org.contextmapper.dsl/src/org/contextmapper/dsl/standalone/StandaloneContextMapper.java b/org.contextmapper.dsl/src/org/contextmapper/dsl/standalone/StandaloneContextMapper.java index 3c86b082..c2909d1b 100644 --- a/org.contextmapper.dsl/src/org/contextmapper/dsl/standalone/StandaloneContextMapper.java +++ b/org.contextmapper.dsl/src/org/contextmapper/dsl/standalone/StandaloneContextMapper.java @@ -43,7 +43,17 @@ public StandaloneContextMapper() { @Override public CMLResource loadCML(String filepath) { - return new CMLResource(new ResourceSetImpl().getResource(URI.createURI(filepath), true)); + return new CMLResource(new ResourceSetImpl().getResource(toURI(filepath), true)); + } + + private URI toURI(String filepath) { + URI uri = URI.createURI(filepath); + // plain file paths (relative or absolute, without scheme) are converted into absolute file URIs; + // otherwise imports cannot be resolved against the URI of the loaded resource (see issue #364) + // a single letter scheme is a Windows drive letter (C:\...) + if (uri.scheme() == null || uri.scheme().length() == 1) + return URI.createFileURI(new File(filepath).getAbsolutePath()); + return uri; } @Override