diff --git a/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlComponents.java b/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlComponents.java index 1e920bd339..f9ddff7d5f 100644 --- a/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlComponents.java +++ b/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlComponents.java @@ -17,12 +17,14 @@ import java.util.Collection; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import com.google.common.collect.FluentIterable; import static com.tngtech.archunit.library.plantuml.rules.PlantUmlComponent.Functions.TO_EXISTING_ALIAS; +import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; @@ -43,19 +45,16 @@ Collection getComponentsWithAlias() { return componentsByAlias.values(); } - PlantUmlComponent findComponentWith(String nameOrAlias) { + Optional tryFindComponentWith(String nameOrAlias) { ComponentName componentName = new ComponentName(nameOrAlias); Alias alias = new Alias(nameOrAlias); PlantUmlComponent result = componentsByAlias.containsKey(alias) ? componentsByAlias.get(alias) : componentsByName.get(componentName); - if (result == null) { - throw new IllegalDiagramException("There is no Component with name or alias = '%s'. %s", nameOrAlias, - "Components must be specified separately from dependencies."); - } - return result; + return Optional.ofNullable(result); } PlantUmlComponent findComponentWith(ComponentIdentifier identifier) { - return componentsByName.get(identifier.getComponentName()); + return requireNonNull(componentsByName.get(identifier.getComponentName()), + () -> String.format("Unknown component '%s'", identifier.getComponentName())); } private static final Predicate WITH_ALIAS = input -> input.getAlias().isPresent(); diff --git a/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlParser.java b/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlParser.java index 420952ac41..14e6c34b2c 100644 --- a/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlParser.java +++ b/archunit/src/main/java/com/tngtech/archunit/library/plantuml/rules/PlantUmlParser.java @@ -28,6 +28,8 @@ import com.google.common.io.CharStreams; import com.tngtech.archunit.library.plantuml.rules.PlantUmlPatterns.PlantUmlComponentMatcher; import com.tngtech.archunit.library.plantuml.rules.PlantUmlPatterns.PlantUmlDependencyMatcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; import static java.nio.charset.StandardCharsets.UTF_8; @@ -35,6 +37,8 @@ import static java.util.stream.Collectors.toSet; class PlantUmlParser { + private static final Logger log = LoggerFactory.getLogger(PlantUmlParser.class); + private final PlantUmlPatterns plantUmlPatterns = new PlantUmlPatterns(); PlantUmlDiagram parse(URL url) { @@ -78,9 +82,16 @@ private Set parseComponents(List plantUmlDiagramLines private ImmutableList parseDependencies(PlantUmlComponents plantUmlComponents, List plantUmlDiagramLines) { ImmutableList.Builder result = ImmutableList.builder(); for (PlantUmlDependencyMatcher matcher : plantUmlPatterns.matchDependencies(plantUmlDiagramLines)) { - PlantUmlComponent origin = findComponentMatching(plantUmlComponents, matcher.matchOrigin()); - PlantUmlComponent target = findComponentMatching(plantUmlComponents, matcher.matchTarget()); - result.add(new ParsedDependency(origin.getIdentifier(), target.getIdentifier())); + Optional origin = tryFindComponentMatching(plantUmlComponents, matcher.matchOrigin()); + Optional target = tryFindComponentMatching(plantUmlComponents, matcher.matchTarget()); + if (origin.isPresent() && target.isPresent()) { + result.add(new ParsedDependency(origin.get().getIdentifier(), target.get().getIdentifier())); + } else { + log.trace( + "Ignoring dependency from {} to {}, because origin or target couldn't be parsed", + matcher.matchOrigin(), matcher.matchTarget() + ); + } } return result.build(); } @@ -113,11 +124,11 @@ private ImmutableSet identifyStereotypes(PlantUmlComponentMatcher ma return result; } - private PlantUmlComponent findComponentMatching(PlantUmlComponents plantUmlComponents, String originOrTargetString) { + private Optional tryFindComponentMatching(PlantUmlComponents plantUmlComponents, String originOrTargetString) { originOrTargetString = originOrTargetString.trim() .replaceAll("^\\[", "") .replaceAll("]$", ""); - return plantUmlComponents.findComponentWith(originOrTargetString); + return plantUmlComponents.tryFindComponentWith(originOrTargetString); } } 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 12a02d67b8..4ddcf9814e 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 @@ -206,18 +206,6 @@ public void does_not_include_dependency_descriptions() { .isEqualTo(new ComponentName("otherComponent")); } - @Test - public void throws_exception_with_components_that_are_not_yet_defined() { - File file = TestDiagram.in(temporaryFolder) - .dependencyFrom("[NotYetDefined]").to("[AlsoNotYetDefined]") - .write(); - - assertThatThrownBy(() -> createDiagram(file)) - .isInstanceOf(IllegalDiagramException.class) - .hasMessageContaining("There is no Component with name or alias = 'NotYetDefined'") - .hasMessageContaining("Components must be specified separately from dependencies"); - } - @Test public void throws_exception_with_components_without_stereotypes() { File file = TestDiagram.in(temporaryFolder) @@ -418,6 +406,37 @@ public void parses_a_component_diagram_that_uses_alias_with_and_without_brackets assertThat(bar.getDependencies()).isEmpty(); } + @Test + public void ignores_components_that_are_not_yet_defined() { + File file = TestDiagram.in(temporaryFolder) + .dependencyFrom("[NotYetDefined]").to("[AlsoNotYetDefined]") + .write(); + + PlantUmlDiagram diagram = createDiagram(file); + + assertThat(diagram.getComponentsWithAlias()).isEmpty(); + } + + @Test + public void ignores_database_components() { + File file = TestDiagram.in(temporaryFolder) + .rawLine("database \"DB\"") + .component("componentA").withAlias("aliasA").withStereoTypes("..packageA..") + .component("componentB").withAlias("aliasB").withStereoTypes("..packageB..") + .dependencyFrom("aliasA").to("aliasB") + .dependencyFrom("aliasB").to("DB") + .dependencyFrom("componentA").to("DB") + .write(); + + PlantUmlDiagram diagram = createDiagram(file); + + PlantUmlComponent a = getComponentWithAlias(new Alias("aliasA"), diagram); + PlantUmlComponent b = getComponentWithAlias(new Alias("aliasB"), diagram); + + assertThat(a.getDependencies()).containsOnly(b); + assertThat(b.getDependencies()).isEmpty(); + } + private PlantUmlComponent getComponentWithName(String componentName, PlantUmlDiagram diagram) { PlantUmlComponent component = diagram.getAllComponents().stream() .filter(c -> c.getComponentName().asString().equals(componentName))