diff --git a/api/maven-api-model/src/main/mdo/maven.mdo b/api/maven-api-model/src/main/mdo/maven.mdo index 550da862be..1cf8f2ea49 100644 --- a/api/maven-api-model/src/main/mdo/maven.mdo +++ b/api/maven-api-model/src/main/mdo/maven.mdo @@ -1366,6 +1366,9 @@ } element contains informations required to exclude an artifact to the project. +

+ The {@code groupId} and {@code artifactId} fields are interpreted as glob patterns, + see {@link java.nio.file.FileSystem#getPathMatcher}. ]]> diff --git a/maven-core/src/main/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilter.java b/maven-core/src/main/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilter.java index c2d3ae1599..1defc48556 100644 --- a/maven-core/src/main/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilter.java +++ b/maven-core/src/main/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilter.java @@ -18,8 +18,13 @@ */ package org.apache.maven.artifact.resolver.filter; +import java.lang.reflect.Proxy; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.PathMatcher; import java.util.List; import java.util.function.Predicate; +import java.util.stream.Collectors; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Exclusion; @@ -28,37 +33,42 @@ import org.apache.maven.model.Exclusion; * Filter to exclude from a list of artifact patterns. */ public class ExclusionArtifactFilter implements ArtifactFilter { - private static final String WILDCARD = "*"; private final List exclusions; + private final List> predicates; public ExclusionArtifactFilter(List exclusions) { this.exclusions = exclusions; - } - - private Predicate sameArtifactId(Artifact artifact) { - return exclusion -> exclusion.getArtifactId().equals(artifact.getArtifactId()); - } - - private Predicate sameGroupId(Artifact artifact) { - return exclusion -> exclusion.getGroupId().equals(artifact.getGroupId()); - } - - private Predicate groupIdIsWildcard = exclusion -> WILDCARD.equals(exclusion.getGroupId()); - - private Predicate artifactIdIsWildcard = exclusion -> WILDCARD.equals(exclusion.getArtifactId()); - - private Predicate groupIdAndArtifactIdIsWildcard = groupIdIsWildcard.and(artifactIdIsWildcard); - - private Predicate exclude(Artifact artifact) { - return groupIdAndArtifactIdIsWildcard - .or(groupIdIsWildcard.and(sameArtifactId(artifact))) - .or(artifactIdIsWildcard.and(sameGroupId(artifact))) - .or(sameGroupId(artifact).and(sameArtifactId(artifact))); + this.predicates = + exclusions.stream().map(ExclusionArtifactFilter::toPredicate).collect(Collectors.toList()); } @Override public boolean include(Artifact artifact) { - return !exclusions.stream().anyMatch(exclude(artifact)); + return predicates.stream().noneMatch(p -> p.test(artifact)); + } + + private static Predicate toPredicate(Exclusion exclusion) { + PathMatcher groupId = FileSystems.getDefault().getPathMatcher("glob:" + exclusion.getGroupId()); + PathMatcher artifactId = FileSystems.getDefault().getPathMatcher("glob:" + exclusion.getArtifactId()); + Predicate predGroupId = a -> groupId.matches(createPathProxy(a.getGroupId())); + Predicate predArtifactId = a -> artifactId.matches(createPathProxy(a.getArtifactId())); + return predGroupId.and(predArtifactId); + } + + /** + * In order to reuse the glob matcher from the filesystem, we need + * to create Path instances. Those are only used with the toString method. + * This hack works because the only system-dependent thing is the path + * separator which should not be part of the groupId or artifactId. + */ + private static Path createPathProxy(String value) { + return (Path) Proxy.newProxyInstance( + ExclusionArtifactFilter.class.getClassLoader(), new Class[] {Path.class}, (proxy1, method, args) -> { + if ("toString".equals(method.getName())) { + return value; + } + throw new UnsupportedOperationException(); + }); } } diff --git a/maven-core/src/test/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilterTest.java b/maven-core/src/test/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilterTest.java index bcb5bf1f83..4347e1a8dc 100644 --- a/maven-core/src/test/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilterTest.java +++ b/maven-core/src/test/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilterTest.java @@ -33,12 +33,17 @@ import static org.mockito.Mockito.when; class ExclusionArtifactFilterTest { private Artifact artifact; + private Artifact artifact2; @BeforeEach void setup() { artifact = mock(Artifact.class); when(artifact.getGroupId()).thenReturn("org.apache.maven"); when(artifact.getArtifactId()).thenReturn("maven-core"); + + artifact2 = mock(Artifact.class); + when(artifact2.getGroupId()).thenReturn("org.junit.jupiter"); + when(artifact2.getArtifactId()).thenReturn("junit-jupiter-engine"); } @Test @@ -140,4 +145,26 @@ class ExclusionArtifactFilterTest { assertThat(filter.include(artifact), is(false)); } + + @Test + void testExcludeWithGlob() { + Exclusion exclusion = new Exclusion(); + exclusion.setGroupId("*"); + exclusion.setArtifactId("maven-*"); + ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); + + assertThat(filter.include(artifact), is(false)); + assertThat(filter.include(artifact2), is(true)); + } + + @Test + void testExcludeWithGlobStar() { + Exclusion exclusion = new Exclusion(); + exclusion.setGroupId("**"); + exclusion.setArtifactId("maven-**"); + ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); + + assertThat(filter.include(artifact), is(false)); + assertThat(filter.include(artifact2), is(true)); + } }