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