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.classpath;
017
018import io.avaje.classpath.scanner.internal.UrlUtils;
019import org.slf4j.Logger;
020import org.slf4j.LoggerFactory;
021
022import java.io.File;
023import java.net.URL;
024import java.util.Set;
025import java.util.TreeSet;
026
027/**
028 * ClassPathLocationScanner for the file system.
029 */
030public class FileSystemClassPathLocationScanner implements ClassPathLocationScanner {
031  private static final Logger LOG = LoggerFactory.getLogger(FileSystemClassPathLocationScanner.class);
032
033  public Set<String> findResourceNames(String location, URL locationUrl) {
034    String filePath = UrlUtils.toFilePath(locationUrl);
035    File folder = new File(filePath);
036    if (!folder.isDirectory()) {
037      LOG.debug("Skipping path as it is not a directory: " + filePath);
038      return new TreeSet<>();
039    }
040
041    String classPathRootOnDisk = filePath.substring(0, filePath.length() - location.length());
042    if (!classPathRootOnDisk.endsWith(File.separator)) {
043      classPathRootOnDisk = classPathRootOnDisk + File.separator;
044    }
045    LOG.debug("Scanning starting at classpath root in filesystem: " + classPathRootOnDisk);
046    return findResourceNamesFromFileSystem(classPathRootOnDisk, location, folder);
047  }
048
049  /**
050   * Finds all the resource names contained in this file system folder.
051   *
052   * @param classPathRootOnDisk The location of the classpath root on disk, with a trailing slash.
053   * @param scanRootLocation    The root location of the scan on the classpath, without leading or trailing slashes.
054   * @param folder              The folder to look for resources under on disk.
055   * @return The resource names;
056   */
057    /*private -> for testing*/
058  Set<String> findResourceNamesFromFileSystem(String classPathRootOnDisk, String scanRootLocation, File folder) {
059    LOG.debug("Scanning for resources in path: {} ({})", folder.getPath(), scanRootLocation);
060
061    Set<String> resourceNames = new TreeSet<>();
062
063    File[] files = folder.listFiles();
064    if (files != null) {
065      for (File file : files) {
066        if (file.canRead()) {
067          String resourcePath = toResourceNameOnClasspath(classPathRootOnDisk, file);
068          if (file.isDirectory()) {
069            if (!ignorePath(resourcePath)) {
070              resourceNames.addAll(findResourceNamesFromFileSystem(classPathRootOnDisk, scanRootLocation, file));
071            }
072          } else {
073            resourceNames.add(resourcePath);
074          }
075        }
076      }
077    }
078
079    return resourceNames;
080  }
081
082  private boolean ignorePath(String resourcePath) {
083    return resourcePath.startsWith("org/avaje/classpath") || resourcePath.startsWith("io/ebean");
084  }
085
086  /**
087   * Converts this file into a resource name on the classpath.
088   *
089   * @param classPathRootOnDisk The location of the classpath root on disk, with a trailing slash.
090   * @param file                The file.
091   * @return The resource name on the classpath.
092   */
093  private String toResourceNameOnClasspath(String classPathRootOnDisk, File file) {
094    String fileName = file.getAbsolutePath().replace("\\", "/");
095
096    //Cut off the part on disk leading to the root of the classpath
097    //This leaves a resource name starting with the scanRootLocation,
098    //   with no leading slash, containing subDirs and the fileName.
099    return fileName.substring(classPathRootOnDisk.length());
100  }
101}