diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependency.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependency.java index 0c068618940d8..e22e23a961671 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependency.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/maven/dependency/ResolvedDependency.java @@ -5,6 +5,7 @@ import io.quarkus.bootstrap.workspace.ArtifactSources; import io.quarkus.bootstrap.workspace.WorkspaceModule; import io.quarkus.paths.EmptyPathTree; +import io.quarkus.paths.FilteredPathTree; import io.quarkus.paths.MultiRootPathTree; import io.quarkus.paths.PathCollection; import io.quarkus.paths.PathFilter; @@ -36,7 +37,7 @@ default PathTree getContentTree(PathFilter pathFilter) { final WorkspaceModule module = getWorkspaceModule(); final PathTree workspaceTree = module == null ? EmptyPathTree.getInstance() : module.getContentTree(getClassifier()); if (!workspaceTree.isEmpty()) { - return workspaceTree; + return pathFilter == null ? workspaceTree : new FilteredPathTree(workspaceTree, pathFilter); } final PathCollection paths = getResolvedPaths(); if (paths == null || paths.isEmpty()) { diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java new file mode 100644 index 0000000000000..11e125a04fbf1 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java @@ -0,0 +1,99 @@ +package io.quarkus.paths; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.jar.Manifest; + +public class FilteredPathTree implements PathTree { + + private final PathTree original; + protected final PathFilter filter; + + public FilteredPathTree(PathTree tree, PathFilter filter) { + this.original = Objects.requireNonNull(tree, "tree is null"); + this.filter = Objects.requireNonNull(filter, "filter is null"); + } + + @Override + public Collection getRoots() { + return original.getRoots(); + } + + @Override + public Manifest getManifest() { + return original.getManifest(); + } + + @Override + public void walk(PathVisitor visitor) { + original.walk(visit -> { + if (visit != null && filter.isVisible(visit.getRelativePath("/"))) { + visitor.visitPath(visit); + } + }); + } + + @Override + public T apply(String relativePath, Function func) { + if (!PathFilter.isVisible(filter, relativePath)) { + return func.apply(null); + } + return original.apply(relativePath, func); + } + + @Override + public void accept(String relativePath, Consumer consumer) { + if (!PathFilter.isVisible(filter, relativePath)) { + consumer.accept(null); + } else { + original.accept(relativePath, consumer); + } + } + + @Override + public boolean contains(String relativePath) { + return PathFilter.isVisible(filter, relativePath) && original.contains(relativePath); + } + + @Override + public OpenPathTree open() { + return new OpenFilteredPathTree(original.open(), filter); + } + + private static class OpenFilteredPathTree extends FilteredPathTree implements OpenPathTree { + + private final OpenPathTree original; + + private OpenFilteredPathTree(OpenPathTree original, PathFilter filter) { + super(original, filter); + this.original = original; + } + + @Override + public PathTree getOriginalTree() { + return original.getOriginalTree(); + } + + @Override + public boolean isOpen() { + return original.isOpen(); + } + + @Override + public Path getPath(String relativePath) { + if (!PathFilter.isVisible(filter, relativePath)) { + return null; + } + return original.getPath(relativePath); + } + + @Override + public void close() throws IOException { + original.close(); + } + } +} diff --git a/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/paths/FilteredPathTreeTest.java b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/paths/FilteredPathTreeTest.java new file mode 100644 index 0000000000000..03a76ffea14f8 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/test/java/io/quarkus/paths/FilteredPathTreeTest.java @@ -0,0 +1,152 @@ +package io.quarkus.paths; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import io.quarkus.fs.util.ZipUtils; + +public class FilteredPathTreeTest { + + @TempDir + static Path testDir; + static Path testJar; + + private static void createFile(String path) throws Exception { + var file = testDir.resolve(path); + Files.createDirectories(file.getParent()); + Files.createFile(file); + } + + @BeforeAll + public static void beforeAll() throws Exception { + createFile("META-INF/jandex.idx"); + createFile("org/toolbox/Axe.class"); + createFile("org/toolbox/Hammer.class"); + createFile("org/toolbox/Saw.class"); + createFile("README.md"); + testJar = testDir.resolve("test.jar"); + ZipUtils.zip(testDir, testJar); + } + + @Test + public void unfilteredTestDir() { + var pathTree = PathTree.ofDirectoryOrArchive(testDir); + assertThat(getAllPaths(pathTree)).containsExactlyInAnyOrder( + "", + "META-INF", + "META-INF/jandex.idx", + "org", + "org/toolbox", + "org/toolbox/Axe.class", + "org/toolbox/Hammer.class", + "org/toolbox/Saw.class", + "README.md", + "test.jar"); + } + + @Test + public void unfilteredTestJar() { + var pathTree = PathTree.ofDirectoryOrArchive(testJar); + assertThat(getAllPaths(pathTree)).containsExactlyInAnyOrder( + "", + "META-INF", + "META-INF/jandex.idx", + "org", + "org/toolbox", + "org/toolbox/Axe.class", + "org/toolbox/Hammer.class", + "org/toolbox/Saw.class", + "README.md", + "test.jar"); + } + + @Test + public void dirIncludeToolbox() { + var pathTree = PathTree.ofDirectoryOrArchive(testDir, PathFilter.forIncludes(List.of("*/toolbox/**"))); + assertThat(getAllPaths(pathTree)).containsExactlyInAnyOrder( + "org/toolbox/Axe.class", + "org/toolbox/Hammer.class", + "org/toolbox/Saw.class"); + } + + @Test + public void jarIncludeToolbox() { + var pathTree = PathTree.ofDirectoryOrArchive(testJar, PathFilter.forIncludes(List.of("*/toolbox/**"))); + assertThat(getAllPaths(pathTree)).containsExactlyInAnyOrder( + "org/toolbox/Axe.class", + "org/toolbox/Hammer.class", + "org/toolbox/Saw.class"); + } + + @Test + public void dirIncludeToolboxExcludeHammer() { + var pathTree = PathTree.ofDirectoryOrArchive(testDir, new PathFilter( + List.of("*/toolbox/**"), + List.of("**/Hammer.class"))); + assertThat(getAllPaths(pathTree)).containsExactlyInAnyOrder( + "org/toolbox/Axe.class", + "org/toolbox/Saw.class"); + } + + @Test + public void jarIncludeToolboxExcludeHammer() { + var pathTree = PathTree.ofDirectoryOrArchive(testJar, new PathFilter( + List.of("*/toolbox/**"), + List.of("**/Hammer.class"))); + assertThat(getAllPaths(pathTree)).containsExactlyInAnyOrder( + "org/toolbox/Axe.class", + "org/toolbox/Saw.class"); + } + + @Test + public void filteredPathTree() throws Exception { + var originalFilter = new PathFilter( + List.of("*/toolbox/**"), + List.of("**/Hammer.class")); + var outerFilter = new PathFilter( + List.of("**/Axe.class"), + List.of("**/Saw.class")); + + var pathTree = PathTree.ofDirectoryOrArchive(testDir, originalFilter); + pathTree = new FilteredPathTree(pathTree, outerFilter); + assertFilteredPathTree(pathTree); + try (var openTree = pathTree.open()) { + assertFilteredPathTree(openTree); + } + + pathTree = PathTree.ofDirectoryOrArchive(testJar, originalFilter); + pathTree = new FilteredPathTree(pathTree, outerFilter); + assertFilteredPathTree(pathTree); + try (var openTree = pathTree.open()) { + assertFilteredPathTree(openTree); + } + } + + private static void assertFilteredPathTree(PathTree pathTree) { + assertThat(getAllPaths(pathTree)).containsExactlyInAnyOrder( + "org/toolbox/Axe.class"); + + assertThat(pathTree.isEmpty()).isFalse(); + assertThat(pathTree.contains("org/toolbox/Axe.class")).isTrue(); + assertThat(pathTree.apply("org/toolbox/Axe.class", Objects::nonNull)).isTrue(); + assertThat(pathTree.apply("org/toolbox/Saw.class", Objects::nonNull)).isFalse(); + pathTree.accept("org/toolbox/Axe.class", visit -> assertThat(visit).isNotNull()); + pathTree.accept("org/toolbox/Saw.class", visit -> assertThat(visit).isNull()); + } + + private static Set getAllPaths(PathTree pathTree) { + final Set paths = new HashSet<>(); + pathTree.walk(visit -> paths.add(visit.getRelativePath("/"))); + return paths; + } +}