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.filesystem; 017 018import org.avaje.classpath.scanner.core.ClassPathScanException; 019import org.avaje.classpath.scanner.core.Location; 020import org.avaje.classpath.scanner.ResourceFilter; 021import org.avaje.classpath.scanner.Resource; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025import java.io.File; 026import java.io.IOException; 027import java.util.ArrayList; 028import java.util.Collections; 029import java.util.List; 030import java.util.Set; 031import java.util.TreeSet; 032 033/** 034 * FileSystem scanner. 035 */ 036public class FileSystemScanner { 037 038 private static final Logger LOG = LoggerFactory.getLogger(FileSystemScanner.class); 039 040 /** 041 * Scans the FileSystem for resources under the specified location, starting with the specified prefix and ending with 042 * the specified suffix. 043 * 044 * @param location The location in the filesystem to start searching. Subdirectories are also searched. 045 * @param predicate The predicate used to match resources. 046 * @return The resources that were found. 047 */ 048 public List<Resource> scanForResources(Location location, ResourceFilter predicate) { 049 050 String path = location.getPath(); 051 052 File dir = new File(path); 053 if (!dir.isDirectory() || !dir.canRead()) { 054 LOG.warn("Unable to resolve location filesystem:" + path); 055 return Collections.emptyList(); 056 } 057 058 List<Resource> resources = new ArrayList<Resource>(); 059 060 try { 061 Set<String> resourceNames = findResourceNames(path, predicate); 062 for (String resourceName : resourceNames) { 063 resources.add(new FileSystemResource(resourceName)); 064 LOG.debug("Found filesystem resource: " + resourceName); 065 } 066 return resources; 067 068 } catch (IOException e) { 069 throw new ClassPathScanException(e); 070 } 071 } 072 073 /** 074 * Finds the resources names present at this location and below on the classpath starting with this prefix and 075 * ending with this suffix. 076 */ 077 private Set<String> findResourceNames(String path, ResourceFilter predicate) throws IOException { 078 Set<String> resourceNames = findResourceNamesFromFileSystem(path, new File(path)); 079 return filterResourceNames(resourceNames, predicate); 080 } 081 082 /** 083 * Finds all the resource names contained in this file system folder. 084 * 085 * @param scanRootLocation The root location of the scan on disk. 086 * @param folder The folder to look for resources under on disk. 087 * @return The resource names; 088 * @throws IOException when the folder could not be read. 089 */ 090 @SuppressWarnings("ConstantConditions") 091 Set<String> findResourceNamesFromFileSystem(String scanRootLocation, File folder) throws IOException { 092 093 LOG.debug("scanning in path: {} ({})", folder.getPath(), scanRootLocation); 094 095 Set<String> resourceNames = new TreeSet<String>(); 096 097 File[] files = folder.listFiles(); 098 if (files != null) { 099 for (File file : files) { 100 if (file.canRead()) { 101 if (file.isDirectory()) { 102 resourceNames.addAll(findResourceNamesFromFileSystem(scanRootLocation, file)); 103 } else { 104 resourceNames.add(file.getPath()); 105 } 106 } 107 } 108 } 109 110 return resourceNames; 111 } 112 113 /** 114 * Filters this list of resource names to only include the ones whose filename matches this prefix and this suffix. 115 */ 116 private Set<String> filterResourceNames(Set<String> resourceNames, ResourceFilter predicate) { 117 Set<String> filteredResourceNames = new TreeSet<String>(); 118 for (String resourceName : resourceNames) { 119 if (predicate.isMatch(resourceName)) { 120 filteredResourceNames.add(resourceName); 121 } 122 } 123 return filteredResourceNames; 124 } 125}