1 /*
2  * Copyright 2010-2020 Redgate Software Ltd
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *         http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.flywaydb.core.internal.scanner.classpath;
17
18 import org.flywaydb.core.api.logging.Log;
19 import org.flywaydb.core.api.logging.LogFactory;
20 import org.flywaydb.core.internal.util.UrlUtils;
21
22 import java.io.File;
23 import java.net.URL;
24 import java.util.Set;
25 import java.util.TreeSet;
26
27 /**
28  * ClassPathLocationScanner for the file system.
29  */

30 public class FileSystemClassPathLocationScanner implements ClassPathLocationScanner {
31     private static final Log LOG = LogFactory.getLog(FileSystemClassPathLocationScanner.class);
32
33     public Set<String> findResourceNames(String location, URL locationUrl) {
34         String filePath = UrlUtils.toFilePath(locationUrl);
35         File folder = new File(filePath);
36         if (!folder.isDirectory()) {
37             LOG.debug("Skipping path as it is not a directory: " + filePath);
38             return new TreeSet<>();
39         }
40
41         String classPathRootOnDisk = filePath.substring(0, filePath.length() - location.length());
42         if (!classPathRootOnDisk.endsWith(File.separator)) {
43             classPathRootOnDisk = classPathRootOnDisk + File.separator;
44         }
45         LOG.debug("Scanning starting at classpath root in filesystem: " + classPathRootOnDisk);
46         return findResourceNamesFromFileSystem(classPathRootOnDisk, location, folder);
47     }
48
49     /**
50      * Finds all the resource names contained in this file system folder.
51      *
52      * @param classPathRootOnDisk The location of the classpath root on disk, with a trailing slash.
53      * @param scanRootLocation    The root location of the scan on the classpath, without leading or trailing slashes.
54      * @param folder              The folder to look for resources under on disk.
55      * @return The resource names;
56      */

57     /*private -> for testing*/
58     @SuppressWarnings("ConstantConditions")
59     Set<String> findResourceNamesFromFileSystem(String classPathRootOnDisk, String scanRootLocation, File folder) {
60         LOG.debug("Scanning for resources in path: " + folder.getPath() + " (" + scanRootLocation + ")");
61
62         Set<String> resourceNames = new TreeSet<>();
63
64         File[] files = folder.listFiles();
65         for (File file : files) {
66             if (file.canRead()) {
67                 if (file.isDirectory()) {
68                     resourceNames.addAll(findResourceNamesFromFileSystem(classPathRootOnDisk, scanRootLocation, file));
69                 } else {
70                     resourceNames.add(toResourceNameOnClasspath(classPathRootOnDisk, file));
71                 }
72             }
73         }
74
75         return resourceNames;
76     }
77
78     /**
79      * Converts this file into a resource name on the classpath.
80      *
81      * @param classPathRootOnDisk The location of the classpath root on disk, with a trailing slash.
82      * @param file                The file.
83      * @return The resource name on the classpath.
84      */

85     private String toResourceNameOnClasspath(String classPathRootOnDisk, File file) {
86         String fileName = file.getAbsolutePath().replace("\\""/");
87
88         //Cut off the part on disk leading to the root of the classpath
89         //This leaves a resource name starting with the scanRootLocation,
90         //   with no leading slash, containing subDirs and the fileName.
91         return fileName.substring(classPathRootOnDisk.length());
92     }
93 }