From 254f34004ffba3fe5efd41523ba26949b8700144 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 14 Jun 2024 13:51:29 +0200 Subject: [PATCH 1/2] prevent stackoverflow (recursive call) in the debugger. Signed-off-by: Ulli Hafner --- .../archunit/library/plantuml/rules/PlantUmlComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlComponent.java b/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlComponent.java index 68fa280731..5c323c4aa6 100644 --- a/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlComponent.java +++ b/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlComponent.java @@ -91,7 +91,7 @@ public String toString() { "componentName=" + componentName + ", stereotypes=" + stereotypes + ", alias=" + alias + - ", dependencies=" + dependencies + + ", dependencies=" + dependencies.size() + '}'; } From fa1a21c963892602521900aca80a11d9a5476340 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 14 Jun 2024 13:55:29 +0200 Subject: [PATCH 2/2] add support for packages and components in puml files It makes sense to use packages or components as the items that define the allowed project dependencies. Signed-off-by: Ulli Hafner --- .../plantuml/rules/PlantUmlPatterns.java | 14 ++++- .../plantuml/rules/PlantUmlParserTest.java | 58 +++++++++++++++++++ .../rules/components-long-syntax.puml | 9 +++ .../rules/components-short-syntax.puml | 8 +++ .../plantuml/rules/packages-long-syntax.puml | 9 +++ 5 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/components-long-syntax.puml create mode 100644 archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/components-short-syntax.puml create mode 100644 archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/packages-long-syntax.puml diff --git a/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlPatterns.java b/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlPatterns.java index a7ce0cfbca..6f29524fc1 100644 --- a/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlPatterns.java +++ b/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlPatterns.java @@ -36,6 +36,7 @@ class PlantUmlPatterns { private static final String OPTIONAL_COMPONENT_KEYWORD_FORMAT = "(?:component)?"; private static final String COMPONENT_NAME_GROUP_NAME = "componentName"; + private static final String LONG_COMPONENT_NAME_GROUP_NAME = "longComponentName"; private static final String COMPONENT_NAME_FORMAT = "\\[" + capture(anythingBut("\\[\\]"), COMPONENT_NAME_GROUP_NAME) + "]"; private static final String STEREOTYPE_FORMAT = "(?:<<" + capture(anythingBut("<>")) + ">>\\s*)"; @@ -46,9 +47,12 @@ class PlantUmlPatterns { private static final String COLOR_FORMAT = "\\s*(?:#" + anyOf("\\w|/\\\\-") + "+)?"; + private static final String COMPONENT_PACKAGE_IDENTIFIER = "(?:component|package)"; private static final Pattern PLANTUML_COMPONENT_PATTERN = Pattern.compile( - "^\\s*" + OPTIONAL_COMPONENT_KEYWORD_FORMAT - + "\\s*" + COMPONENT_NAME_FORMAT + "^\\s*" + + "(?:" + OPTIONAL_COMPONENT_KEYWORD_FORMAT + "\\s*" + COMPONENT_NAME_FORMAT + + "|" + COMPONENT_PACKAGE_IDENTIFIER + "\\s+\"" + + capture("[^\"]*", "longComponentName") + "\")" + "\\s*" + STEREOTYPE_FORMAT + "*" + ALIAS_FORMAT + COLOR_FORMAT @@ -105,7 +109,11 @@ static class PlantUmlComponentMatcher { } String matchComponentName() { - return componentMatcher.group(COMPONENT_NAME_GROUP_NAME); + String name = componentMatcher.group(COMPONENT_NAME_GROUP_NAME); + if (name == null) { + return componentMatcher.group(LONG_COMPONENT_NAME_GROUP_NAME); + } + return name; } Set matchStereoTypes() { diff --git a/archunit/src/test/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlParserTest.java b/archunit/src/test/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlParserTest.java index 873d8dde45..0a3e016d4d 100644 --- a/archunit/src/test/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlParserTest.java +++ b/archunit/src/test/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlParserTest.java @@ -34,6 +34,64 @@ public class PlantUmlParserTest { @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Test + public void parses_long_syntax_from_file() { + PlantUmlDiagram diagram = parseFile("components-long-syntax.puml"); + + verifyComponentLayers(diagram); + verifyComponentAlias(diagram); + } + + @Test + public void parses_packages_from_file() { + PlantUmlDiagram diagram = parseFile("packages-long-syntax.puml"); + + verifyComponentLayers(diagram); + verifyComponentAlias(diagram); + } + + private void verifyComponentAlias(PlantUmlDiagram diagram) { + assertThat(diagram.getAllComponents()).hasSize(3) + .satisfiesExactlyInAnyOrder( + c -> assertThat(c.getAlias()).contains(new Alias("web")), + c -> assertThat(c.getAlias()).contains(new Alias("usecase")), + c -> assertThat(c.getAlias()).contains(new Alias("persistence"))); + } + + @Test + public void parses_components_from_file() { + PlantUmlDiagram diagram = parseFile("components-short-syntax.puml"); + + verifyComponentLayers(diagram); + assertThat(diagram.getAllComponents()).hasSize(3) + .allSatisfy(c -> assertThat(c.getAlias()).isEmpty()); + } + + private PlantUmlDiagram parseFile(final String fileName) { + return parser.parse(PlantUmlParserTest.class.getResource(fileName)); + } + + private void verifyComponentLayers(PlantUmlDiagram diagram) { + assertThat(diagram.getAllComponents()).hasSize(3) + .satisfiesExactlyInAnyOrder( + c -> { + assertThat(c.getComponentName()).isEqualTo(new ComponentName("Web API")); + assertThat(c.getDependencies()).hasSize(1); + assertThat(c.getStereotypes()).contains(new Stereotype("..web")); + }, + c -> { + assertThat(c.getComponentName()).isEqualTo(new ComponentName("Use Cases")); + assertThat(c.getDependencies()).hasSize(1); + assertThat(c.getStereotypes()).contains(new Stereotype("..usecase")); + }, + c -> { + assertThat(c.getComponentName()).isEqualTo(new ComponentName("Persistence")); + assertThat(c.getDependencies()).isEmpty(); + assertThat(c.getStereotypes()).contains(new Stereotype("..persistence")); + } + ); + } + @Test public void parses_correct_number_of_components() { PlantUmlDiagram diagram = createDiagram(TestDiagram.in(temporaryFolder) diff --git a/archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/components-long-syntax.puml b/archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/components-long-syntax.puml new file mode 100644 index 0000000000..10c29b3b0b --- /dev/null +++ b/archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/components-long-syntax.puml @@ -0,0 +1,9 @@ +@startuml +component "Web API" <<..web>> as web +component "Use Cases" <<..usecase>> as usecase +component "Persistence" <<..persistence>> as persistence + +web --> usecase +usecase --> persistence + +@enduml diff --git a/archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/components-short-syntax.puml b/archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/components-short-syntax.puml new file mode 100644 index 0000000000..f6e9356fd6 --- /dev/null +++ b/archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/components-short-syntax.puml @@ -0,0 +1,8 @@ +@startuml +[Web API] <<..web>> +[Use Cases] <<..usecase>> +[Persistence] <<..persistence>> + +[Web API] --> [Use Cases] +[Use Cases] --> [Persistence] +@enduml diff --git a/archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/packages-long-syntax.puml b/archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/packages-long-syntax.puml new file mode 100644 index 0000000000..7450bbae82 --- /dev/null +++ b/archunit/src/test/resources/com/tngtech/archunit/library/plantuml/rules/packages-long-syntax.puml @@ -0,0 +1,9 @@ +@startuml +package "Web API" <<..web>> as web +package "Use Cases" <<..usecase>> as usecase +package "Persistence" <<..persistence>> as persistence + +web --> usecase +usecase --> persistence + +@enduml