Skip to content

Commit

Permalink
Added a method to walk a subtree of a given PathTree
Browse files Browse the repository at this point in the history
  • Loading branch information
aloubyansky committed Jun 9, 2024
1 parent 9222a8a commit 67ca83d
Show file tree
Hide file tree
Showing 14 changed files with 311 additions and 280 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static java.util.function.Predicate.not;
import static java.util.stream.Collectors.toMap;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
Expand Down Expand Up @@ -37,6 +38,7 @@
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import jakarta.inject.Singleton;

Expand Down Expand Up @@ -97,8 +99,6 @@
import io.quarkus.maven.dependency.DependencyFlags;
import io.quarkus.maven.dependency.ResolvedDependency;
import io.quarkus.panache.common.deployment.PanacheEntityClassesBuildItem;
import io.quarkus.paths.FilteredPathTree;
import io.quarkus.paths.PathFilter;
import io.quarkus.paths.PathTree;
import io.quarkus.qute.CheckedTemplate;
import io.quarkus.qute.Engine;
Expand Down Expand Up @@ -2163,18 +2163,89 @@ private void scanPathTree(PathTree pathTree, TemplateRootsBuildItem templateRoot
BuildProducer<TemplatePathBuildItem> templatePaths,
BuildProducer<NativeImageResourceBuildItem> nativeImageResources,
QuteConfig config) {
final boolean isWindows = File.separatorChar == '\\';
for (String templateRoot : templateRoots) {
pathTree.accept(templateRoot, visit -> {
if (visit != null) {
// if template root is found in this tree then walk over its subtree
scanTemplateRootSubtree(
new FilteredPathTree(pathTree, PathFilter.forIncludes(List.of(templateRoot + "/**"))),
visit.getRelativePath(), watchedPaths, templatePaths, nativeImageResources, config);
// On Windows 'Templates' path may be returned when 'templates' is requested.
// containsPath makes sure paths, such as Templates, are filtered out
if (isWindows && !containsCaseSensitivePath(pathTree, templateRoot)) {
continue;
}
pathTree.walkIfContains(templateRoot, visit -> {
if (Files.isRegularFile(visit.getPath())) {
LOGGER.debugf("Found template: %s", visit.getPath());
// remove templateRoot + /
final String relativePath = visit.getRelativePath();
String templatePath = relativePath.substring(templateRoot.length() + 1);
if (config.templatePathExclude.matcher(templatePath).matches()) {
LOGGER.debugf("Template file excluded: %s", visit.getPath());
return;
}
produceTemplateBuildItems(templatePaths, watchedPaths, nativeImageResources,
relativePath, templatePath, visit.getPath(), config);
}
});
}
}

/**
* Checks whether a path tree contains a given relative path respecting case sensitivity.
*
* <p>
* Path API on Windows may resolve {@code templates} to {@code Templates}. This method
* helps verify whether a given relative path actually exists.
*
* @param pathTree path tree
* @param relativePath relative path to check
* @return true if a path tree contains a given relative path
*/
private static boolean containsCaseSensitivePath(PathTree pathTree, String relativePath) {
// this should always be false, since relatvePath is meant to be a resource path, not an FS path
// but just in case
if (File.separatorChar != '/') {
relativePath = relativePath.replace(File.separatorChar, '/');
}
final String[] pathElements = relativePath.split("/");
try (var openTree = pathTree.open()) {
for (var root : openTree.getRoots()) {
if (containsCaseSensitivePath(root, pathElements)) {
return true;
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return false;
}

private static boolean containsCaseSensitivePath(Path root, String[] pathElements) {
var parent = root;
for (String pathElement : pathElements) {
if (!Files.isDirectory(parent)) {
return false;
}
try (Stream<Path> stream = Files.list(parent)) {
var i = stream.iterator();
Path match = null;
while (i.hasNext()) {
final Path next = i.next();
if (pathElement.equals(next.getFileName().toString())) {
match = next;
break;
}
}
if (match == null) {
return false;
}
parent = match;
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (Exception e) {
throw e;
}
}
return true;
}

@BuildStep
TemplateFilePathsBuildItem collectTemplateFilePaths(QuteConfig config, List<TemplatePathBuildItem> templatePaths) {
Set<String> filePaths = new HashSet<String>();
Expand Down Expand Up @@ -3382,27 +3453,6 @@ private static void produceTemplateBuildItems(BuildProducer<TemplatePathBuildIte
readTemplateContent(originalPath, config.defaultCharset)));
}

private void scanTemplateRootSubtree(PathTree pathTree, String templateRoot,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedPaths,
BuildProducer<TemplatePathBuildItem> templatePaths,
BuildProducer<NativeImageResourceBuildItem> nativeImageResources,
QuteConfig config) {
pathTree.walk(visit -> {
if (Files.isRegularFile(visit.getPath())) {
LOGGER.debugf("Found template: %s", visit.getPath());
// remove templateRoot + /
final String relativePath = visit.getRelativePath();
String templatePath = relativePath.substring(templateRoot.length() + 1);
if (config.templatePathExclude.matcher(templatePath).matches()) {
LOGGER.debugf("Template file excluded: %s", visit.getPath());
return;
}
produceTemplateBuildItems(templatePaths, watchedPaths, nativeImageResources,
relativePath, templatePath, visit.getPath(), config);
}
});
}

private static boolean isExcluded(TypeCheck check, Iterable<Predicate<TypeCheck>> excludes) {
for (Predicate<TypeCheck> exclude : excludes) {
if (exclude.test(check)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild;
import io.quarkus.paths.FilteredPathTree;
import io.quarkus.paths.PathFilter;
import io.quarkus.paths.PathVisitor;
import io.quarkus.vertx.core.deployment.CoreVertxBuildItem;
import io.quarkus.vertx.http.deployment.spi.AdditionalStaticResourceBuildItem;
Expand Down Expand Up @@ -86,7 +84,7 @@ public void nativeImageResource(Optional<StaticResourcesBuildItem> staticResourc
final Set<String> collectedDirs = new HashSet<>();
visitRuntimeMetaInfResources(visit -> {
if (Files.isDirectory(visit.getPath())) {
final String relativePath = visit.getRelativePath("/");
final String relativePath = visit.getRelativePath();
if (collectedDirs.add(relativePath)) {
producer.produce(new NativeImageResourceBuildItem(relativePath));
}
Expand All @@ -105,7 +103,7 @@ private Set<StaticResourcesBuildItem.Entry> getClasspathResources() {
visitRuntimeMetaInfResources(visit -> {
if (!Files.isDirectory(visit.getPath())) {
knownPaths.add(new StaticResourcesBuildItem.Entry(
visit.getRelativePath("/").substring(StaticResourcesRecorder.META_INF_RESOURCES.length()),
visit.getRelativePath().substring(StaticResourcesRecorder.META_INF_RESOURCES.length()),
false));
}
});
Expand All @@ -121,13 +119,10 @@ private static void visitRuntimeMetaInfResources(PathVisitor visitor) {
final List<ClassPathElement> elements = QuarkusClassLoader.getElements(StaticResourcesRecorder.META_INF_RESOURCES,
false);
if (!elements.isEmpty()) {
final PathFilter filter = PathFilter.forIncludes(List.of(
StaticResourcesRecorder.META_INF_RESOURCES + "/**",
StaticResourcesRecorder.META_INF_RESOURCES));
for (var element : elements) {
if (element.isRuntime()) {
element.apply(tree -> {
new FilteredPathTree(tree, filter).walk(visitor);
tree.walkIfContains(StaticResourcesRecorder.META_INF_RESOURCES, visitor);
return null;
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,25 @@ public Collection<Path> getRoots() {
public void walk(PathVisitor visitor) {
try (FileSystem fs = openFs()) {
final Path dir = fs.getPath("/");
PathTreeVisit.walk(archive, dir, pathFilter, getMultiReleaseMapping(), visitor);
PathTreeVisit.walk(archive, dir, dir, pathFilter, getMultiReleaseMapping(), visitor);
} catch (IOException e) {
throw new UncheckedIOException("Failed to read " + archive, e);
}
}

@Override
public void walkIfContains(String relativePath, PathVisitor visitor) {
ensureResourcePath(relativePath);
if (!PathFilter.isVisible(pathFilter, relativePath)) {
return;
}
try (FileSystem fs = openFs()) {
for (Path root : fs.getRootDirectories()) {
final Path walkDir = root.resolve(relativePath);
if (Files.exists(walkDir)) {
PathTreeVisit.walk(archive, root, walkDir, pathFilter, getMultiReleaseMapping(), visitor);
}
}
} catch (IOException e) {
throw new UncheckedIOException("Failed to read " + archive, e);
}
Expand Down Expand Up @@ -287,6 +305,17 @@ public void walk(PathVisitor visitor) {
}
}

@Override
public void walkIfContains(String relativePath, PathVisitor visitor) {
lock.readLock().lock();
try {
ensureOpen();
super.walkIfContains(relativePath, visitor);
} finally {
lock.readLock().unlock();
}
}

@Override
public boolean contains(String relativePath) {
lock.readLock().lock();
Expand Down
Loading

0 comments on commit 67ca83d

Please sign in to comment.