001/*
002  Copyright 2010-2016 Boxfuse GmbH
003  <p/>
004  Licensed under the Apache License, Version 2.0 (the "License");
005  you may not use this file except in compliance with the License.
006  You may obtain a copy of the License at
007  <p/>
008  http://www.apache.org/licenses/LICENSE-2.0
009  <p/>
010  Unless required by applicable law or agreed to in writing, software
011  distributed under the License is distributed on an "AS IS" BASIS,
012  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  See the License for the specific language governing permissions and
014  limitations under the License.
015 */
016package io.avaje.classpath.scanner.internal.scanner.filesystem;
017
018import io.avaje.classpath.scanner.Resource;
019import io.avaje.classpath.scanner.core.Location;
020import io.avaje.classpath.scanner.internal.ScanLog;
021import org.slf4j.Logger;
022
023import java.io.File;
024import java.util.*;
025import java.util.function.Predicate;
026
027/**
028 * FileSystem scanner.
029 */
030public class FileSystemScanner {
031
032  private static final Logger log = ScanLog.log;
033
034  /**
035   * Scans the FileSystem for resources under the specified location, starting with the specified prefix and ending with
036   * the specified suffix.
037   *
038   * @param location  The location in the filesystem to start searching. Subdirectories are also searched.
039   * @param predicate The predicate used to match resources.
040   * @return The resources that were found.
041   */
042  public List<Resource> scanForResources(Location location, Predicate<String> predicate) {
043    String path = location.path();
044    File dir = new File(path);
045    if (!dir.isDirectory() || !dir.canRead()) {
046      log.debug("Unable to resolve location filesystem: {}", path);
047      return Collections.emptyList();
048    }
049    List<Resource> resources = new ArrayList<>();
050    for (String resourceName : findResourceNames(path, predicate)) {
051      resources.add(new FileSystemResource(resourceName));
052    }
053    return resources;
054  }
055
056  /**
057   * Finds the resources names present at this location and below on the classpath starting with this prefix and
058   * ending with this suffix.
059   */
060  private Set<String> findResourceNames(String path, Predicate<String> predicate) {
061    Set<String> resourceNames = findResourceNamesFromFileSystem(path, new File(path));
062    return filterResourceNames(resourceNames, predicate);
063  }
064
065  /**
066   * Finds all the resource names contained in this file system folder.
067   *
068   * @param scanRootLocation The root location of the scan on disk.
069   * @param folder           The folder to look for resources under on disk.
070   * @return The resource names;
071   */
072  Set<String> findResourceNamesFromFileSystem(String scanRootLocation, File folder) {
073    log.trace("scan path: {} ({})", folder.getPath(), scanRootLocation);
074    Set<String> resourceNames = new TreeSet<>();
075
076    File[] files = folder.listFiles();
077    if (files != null) {
078      for (File file : files) {
079        if (file.canRead()) {
080          if (file.isDirectory()) {
081            resourceNames.addAll(findResourceNamesFromFileSystem(scanRootLocation, file));
082          } else {
083            resourceNames.add(file.getPath());
084          }
085        }
086      }
087    }
088    return resourceNames;
089  }
090
091  /**
092   * Filters this list of resource names to only include the ones whose filename matches this prefix and this suffix.
093   */
094  private Set<String> filterResourceNames(Set<String> resourceNames, Predicate<String> predicate) {
095    Set<String> filteredResourceNames = new TreeSet<>();
096    for (String resourceName : resourceNames) {
097      if (predicate.test(resourceName)) {
098        filteredResourceNames.add(resourceName);
099      }
100    }
101    return filteredResourceNames;
102  }
103}