Skip to content

Commit

Permalink
#321: Respect imported domains/subdomains in PlantUML generator (#352)
Browse files Browse the repository at this point in the history
  • Loading branch information
stefan-ka authored Jan 4, 2024
1 parent 7f091ba commit 2440137
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 17 deletions.
5 changes: 5 additions & 0 deletions org.contextmapper.dsl.ide.tests/.classpath
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="xtend-gen">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src-gen">
<attributes>
<attribute name="test" value="true"/>
Expand Down
3 changes: 2 additions & 1 deletion org.contextmapper.dsl.tests/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Require-Bundle: org.contextmapper.dsl,
org.eclipse.osgi,
org.freemarker.freemarker;bundle-version="2.3.30",
junit-jupiter-api,
junit-jupiter-params
junit-jupiter-params,
org.slf4j.api;bundle-version="1.7.30"
Bundle-RequiredExecutionEnvironment: JavaSE-11
Export-Package: org.contextmapper.dsl.tests;x-internal=true,
org.contextmapper.tactic.dsl.tests;x-internal=true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Domain OtherDomain {

Subdomain OtherSubdomain {

Entity OtherEntity
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Domain OtherDomain {

Subdomain OtherSubdomain1 {
Entity OtherEntity1
}

Subdomain OtherSubdomain2 {
Entity OtherEntity1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import "./imported-file-test-1.cml"

Domain DirectDomain {

Subdomain DirectSubdomain {

Entity DirectEntity
}
}

ContextMap testMap {

}

BoundedContext context1 implements DirectSubdomain
BoundedContext context2 implements OtherSubdomain {
Aggregate MyAggregate {
Entity MyEntity {
aggregateRoot
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import "./imported-file-test-2.cml"

Domain DirectDomain {

Subdomain DirectSubdomain {

Entity DirectEntity
}
}

ContextMap testMap {

}

BoundedContext context1 implements DirectSubdomain
BoundedContext context2 implements OtherDomain {
Aggregate MyAggregate {
Entity MyEntity {
aggregateRoot
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

Domain TestDomain {

Subdomain TestSubDomain1 {

}

Subdomain TestSubDomain2 {

}

}

Domain OtherDomain {

Subdomain TestSubDomain3 {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2023 The Context Mapper Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.contextmapper.dsl.cml;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
import java.util.Set;
import java.util.stream.Collectors;

import org.contextmapper.dsl.AbstractCMLInputFileTest;
import org.contextmapper.dsl.contextMappingDSL.ContextMappingModel;
import org.contextmapper.dsl.contextMappingDSL.Domain;
import org.contextmapper.dsl.contextMappingDSL.Subdomain;
import org.junit.jupiter.api.Test;

public class CMLModelDomainAndSubdomainResolverTest extends AbstractCMLInputFileTest {

@Test
public void canResolveSubDomainsInSingleFile() throws IOException {
// given
CMLResource input = getOriginalResourceOfTestCML("simple-single-file-test.cml");

// when
Set<Subdomain> declaredSubdomains = new CMLModelDomainAndSubdomainResolver(input.getContextMappingModel())
.resolveAllSubdomains();
Set<String> subdomainNames = declaredSubdomains.stream().map(Subdomain::getName).collect(Collectors.toSet());

// then
assertEquals(3, declaredSubdomains.size());
assertTrue(subdomainNames.contains("TestSubDomain1"));
assertTrue(subdomainNames.contains("TestSubDomain2"));
assertTrue(subdomainNames.contains("TestSubDomain3"));
}

@Test
public void canResolveImportedSubdomains() throws IOException {
// given
CMLResource input = getOriginalResourceOfTestCML("root-file-with-imports-test-1.cml");

// when
Set<Subdomain> declaredSubdomains = new CMLModelDomainAndSubdomainResolver(input.getContextMappingModel())
.resolveAllSubdomains();
Set<String> subdomainNames = declaredSubdomains.stream().map(Subdomain::getName).collect(Collectors.toSet());

// then
assertEquals(2, declaredSubdomains.size());
assertTrue(subdomainNames.contains("DirectSubdomain"));
assertTrue(subdomainNames.contains("OtherSubdomain"));
}

@Test
public void canResolveImportedDomains() throws IOException {
// given
CMLResource input = getOriginalResourceOfTestCML("root-file-with-imports-test-2.cml");

// when
Set<Subdomain> declaredSubdomains = new CMLModelDomainAndSubdomainResolver(input.getContextMappingModel())
.resolveAllSubdomains();
Set<String> subdomainNames = declaredSubdomains.stream().map(Subdomain::getName).collect(Collectors.toSet());

// then
assertEquals(3, declaredSubdomains.size());
assertTrue(subdomainNames.contains("DirectSubdomain"));
assertTrue(subdomainNames.contains("OtherSubdomain1"));
assertTrue(subdomainNames.contains("OtherSubdomain2"));
}

@Test
public void canResolveDomain4Subdomain() throws IOException {
// given
CMLResource input = getOriginalResourceOfTestCML("simple-single-file-test.cml");
ContextMappingModel model = input.getContextMappingModel();
CMLModelDomainAndSubdomainResolver resolver = new CMLModelDomainAndSubdomainResolver(model);
Set<Subdomain> allSubdomains = resolver.resolveAllSubdomains();
Subdomain subdomain1 = allSubdomains.stream().filter(s -> s.getName().equals("TestSubDomain1")).findAny().get();
Subdomain subdomain3 = allSubdomains.stream().filter(s -> s.getName().equals("TestSubDomain3")).findAny().get();

// when
Domain domain1 = resolver.resolveDomain4Subdomain(subdomain1.getName());
Domain domain2 = resolver.resolveDomain4Subdomain(subdomain3.getName());

// then
assertEquals("TestDomain", domain1.getName());
assertEquals("OtherDomain", domain2.getName());
}

@Override
protected String getTestFileDirectory() {
return "/integ-test-files/subdomain-resolving/";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2023 The Context Mapper Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.contextmapper.dsl.cml;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.contextmapper.dsl.contextMappingDSL.BoundedContext;
import org.contextmapper.dsl.contextMappingDSL.ContextMappingModel;
import org.contextmapper.dsl.contextMappingDSL.Domain;
import org.contextmapper.dsl.contextMappingDSL.Subdomain;

public class CMLModelDomainAndSubdomainResolver {

private final ContextMappingModel model;
private final Map<String, Subdomain> declaredSubdomains;
private final Map<String, Domain> domainsBySubdomainNames;

public CMLModelDomainAndSubdomainResolver(final ContextMappingModel model) {
this.model = model;
this.declaredSubdomains = new HashMap<>();
this.domainsBySubdomainNames = new HashMap<>();
initAllDeclaredSubdomainsIncludingImportedOnes();
}

/**
*
* Resolves all subdomains that are:
*
* 1) declared in the "root" CML file/model
*
* 2) subdomains implemented by Bounded Contexts of the root model, but declared
* in an imported CML file/model
*
*/
public Set<Subdomain> resolveAllSubdomains() {
Map<String, Subdomain> subdomains4Model = new HashMap<>();
for (Domain domain : model.getDomains()) {
domain.getSubdomains().forEach(subdomain -> {
subdomains4Model.put(subdomain.getName(), this.declaredSubdomains.get(subdomain.getName()));
});
}
for (BoundedContext boundedContext : model.getBoundedContexts()) {
boundedContext.getImplementedDomainParts().forEach(domainPart -> {
if (domainPart instanceof Domain) {
Domain domain = (Domain) domainPart;
domain.getSubdomains().forEach(subdomain -> {
subdomains4Model.put(subdomain.getName(), this.declaredSubdomains.get(subdomain.getName()));
});
} else if (domainPart instanceof Subdomain) {
Subdomain subdomain = (Subdomain) domainPart;
subdomains4Model.put(subdomain.getName(), this.declaredSubdomains.get(subdomain.getName()));
}
});
}
return new HashSet<>(subdomains4Model.values());
}

/**
*
* Takes a name of the subdomain and returns the domain to which it belongs.
*
*/
public Domain resolveDomain4Subdomain(final String subdomainName) {
return this.domainsBySubdomainNames.get(subdomainName);
}

private void initAllDeclaredSubdomainsIncludingImportedOnes() {
addDeclaredSubdomains(this.model);
if (this.model.eResource() != null) {
for (CMLResource cmlResource : new CMLImportResolver()
.resolveImportedResources(new CMLResource(this.model.eResource()))) {
addDeclaredSubdomains(cmlResource.getContextMappingModel());
}
}
}

private void addDeclaredSubdomains(final ContextMappingModel model) {
for (Domain domain : model.getDomains()) {
domain.getSubdomains().forEach(subdomain -> {
declaredSubdomains.put(subdomain.getName(), subdomain);
domainsBySubdomainNames.put(subdomain.getName(), domain);
});
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@
import java.util.List;
import java.util.Optional;

import org.contextmapper.dsl.cml.CMLModelDomainAndSubdomainResolver;
import org.contextmapper.dsl.contextMappingDSL.Aggregate;
import org.contextmapper.dsl.contextMappingDSL.BoundedContext;
import org.contextmapper.dsl.contextMappingDSL.ContextMappingModel;
import org.contextmapper.dsl.contextMappingDSL.Domain;
import org.contextmapper.dsl.contextMappingDSL.Flow;
import org.contextmapper.dsl.contextMappingDSL.SculptorModule;
import org.contextmapper.dsl.contextMappingDSL.Subdomain;
import org.contextmapper.dsl.contextMappingDSL.UseCase;
import org.contextmapper.dsl.contextMappingDSL.UserRequirement;
import org.contextmapper.dsl.generator.exception.GeneratorInputException;
Expand All @@ -48,9 +47,12 @@ public class PlantUMLGenerator extends AbstractContextMappingModelGenerator {

private static final String PLANT_UML_FILE_EXT = "puml";

private CMLModelDomainAndSubdomainResolver subdomainResolver;

@Override
protected void generateFromContextMappingModel(ContextMappingModel model, IFileSystemAccess2 fsa,
URI inputFileURI) {
this.subdomainResolver = new CMLModelDomainAndSubdomainResolver(this.contextMappingModel);
checkPreconditions();
String fileName = inputFileURI.trimFileExtension().lastSegment();

Expand Down Expand Up @@ -97,13 +99,13 @@ protected void generateFromContextMappingModel(ContextMappingModel model, IFileS
}

// generate class diagrams for subdomains (that have entities)
for (Domain domain : model.getDomains()) {
domain.getSubdomains().stream().filter(subdomain -> !subdomain.getEntities().isEmpty())
.forEach(subdomain -> {
fsa.generateFile(fileName + "_SD_" + subdomain.getName() + "." + PLANT_UML_FILE_EXT,
new PlantUMLSubdomainClassDiagramCreator(domain.getName()).createDiagram(subdomain));
});
}
subdomainResolver.resolveAllSubdomains().stream().filter(subdomain -> !subdomain.getEntities().isEmpty())
.forEach(subdomain -> {
fsa.generateFile(fileName + "_SD_" + subdomain.getName() + "." + PLANT_UML_FILE_EXT,
new PlantUMLSubdomainClassDiagramCreator(
subdomainResolver.resolveDomain4Subdomain(subdomain.getName()).getName())
.createDiagram(subdomain));
});

// generate Use Case diagram out of user requirements, if available
if (!model.getUserRequirements().isEmpty())
Expand Down Expand Up @@ -155,13 +157,8 @@ private List<Aggregate> getAggregatesWithStatesAndTransitions(BoundedContext bc)
}

private boolean modelHasSubdomainWithEntities() {
for (Domain domain : this.contextMappingModel.getDomains()) {
Optional<Subdomain> optSubdomain = domain.getSubdomains().stream()
.filter(subdomain -> !subdomain.getEntities().isEmpty()).findAny();
if (optSubdomain.isPresent())
return true;
}
return false;
return subdomainResolver.resolveAllSubdomains().stream().filter(subdomain -> !subdomain.getEntities().isEmpty())
.findAny().isPresent();
}

}

0 comments on commit 2440137

Please sign in to comment.