1
16 package org.flywaydb.core.api;
17
18 import org.flywaydb.core.api.logging.Log;
19 import org.flywaydb.core.api.logging.LogFactory;
20
21 import java.io.File;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
25
28 public final class Location implements Comparable<Location> {
29 private static final Log LOG = LogFactory.getLog(Location.class);
30
31
34 private static final String CLASSPATH_PREFIX = "classpath:";
35
36
39 public static final String FILESYSTEM_PREFIX = "filesystem:";
40
41
44 private final String prefix;
45
46
49 private String rawPath;
50
51
54 private String rootPath;
55
56 private Pattern pathRegex = null;
57
58
63 public Location(String descriptor) {
64 String normalizedDescriptor = descriptor.trim();
65
66 if (normalizedDescriptor.contains(":")) {
67 prefix = normalizedDescriptor.substring(0, normalizedDescriptor.indexOf(":") + 1);
68 rawPath = normalizedDescriptor.substring(normalizedDescriptor.indexOf(":") + 1);
69 } else {
70 prefix = CLASSPATH_PREFIX;
71 rawPath = normalizedDescriptor;
72 }
73
74 if (isClassPath()) {
75 if (rawPath.contains(".")) {
76 LOG.warn("Use of dots (.) as path separators will be deprecated in Flyway 7. Path: " + rawPath);
77 }
78 rawPath = rawPath.replace(".", "/");
79 if (rawPath.startsWith("/")) {
80 rawPath = rawPath.substring(1);
81 }
82 if (rawPath.endsWith("/")) {
83 rawPath = rawPath.substring(0, rawPath.length() - 1);
84 }
85 processRawPath();
86 } else if (isFileSystem()) {
87 processRawPath();
88 rootPath = new File(rootPath).getPath();
89
90 if (pathRegex == null) {
91
92 rawPath = new File(rawPath).getPath();
93 }
94 } else {
95 throw new FlywayException("Unknown prefix for location (should be either filesystem: or classpath:): "
96 + normalizedDescriptor);
97 }
98
99 if (rawPath.endsWith(File.separator)) {
100 rawPath = rawPath.substring(0, rawPath.length() - 1);
101 }
102 }
103
104
111 private void processRawPath() {
112 if (rawPath.contains("*") || rawPath.contains("?")) {
113
114
115 String seperator = isFileSystem() ? File.separator : "/";
116 String escapedSeperator = seperator.replace("\\", "\\\\").replace("/", "\\/");
117
118
119 String[] pathSplit = rawPath.split("[\\\\/]");
120
121 StringBuilder rootPart = new StringBuilder();
122 StringBuilder patternPart = new StringBuilder();
123
124 boolean endsInFile = false;
125 boolean skipSeperator = false;
126 boolean inPattern = false;
127 for (String pathPart : pathSplit) {
128 endsInFile = false;
129
130 if (pathPart.contains("*") || pathPart.contains("?")) {
131 inPattern = true;
132 }
133
134 if (inPattern) {
135 if (skipSeperator) {
136 skipSeperator = false;
137 } else {
138 patternPart.append("/");
139 }
140
141 String regex;
142 if ("**".equals(pathPart)) {
143 regex = "([^/]+/)*?";
144
145
146 skipSeperator = true;
147 } else {
148 endsInFile = pathPart.contains(".");
149
150 regex = pathPart;
151 regex = regex.replace(".", "\\.");
152 regex = regex.replace("?", "[^/]");
153 regex = regex.replace("*", "[^/]+?");
154 }
155
156 patternPart.append(regex);
157 } else {
158 rootPart.append(seperator).append(pathPart);
159 }
160 }
161
162
163 rootPath = rootPart.length() > 0 ? rootPart.toString().substring(1) : "";
164
165
166 String pattern = patternPart.toString().substring(1);
167
168
169 pattern = pattern.replace("/", escapedSeperator);
170
171
172 if (rootPart.length() > 0) {
173 pattern = rootPath.replace(seperator, escapedSeperator) + escapedSeperator + pattern;
174 }
175
176
177 if (!endsInFile) {
178 pattern = pattern + escapedSeperator + "(?<relpath>.*)";
179 }
180
181 pathRegex = Pattern.compile(pattern);
182 } else {
183 rootPath = rawPath;
184 }
185 }
186
187
190 public boolean matchesPath(String path) {
191 if (pathRegex == null) {
192 return true;
193 }
194
195 return pathRegex.matcher(path).matches();
196 }
197
198
203 public String getPathRelativeToThis(String path) {
204 if (pathRegex != null && pathRegex.pattern().contains("?<relpath>")) {
205 Matcher matcher = pathRegex.matcher(path);
206 if (matcher.matches()) {
207 String relPath = matcher.group("relpath");
208 if (relPath != null && relPath.length() > 0) {
209 return relPath;
210 }
211 }
212 }
213
214 return rootPath.length() > 0 ? path.substring(rootPath.length() + 1) : path;
215 }
216
217
222 public boolean isClassPath() {
223 return CLASSPATH_PREFIX.equals(prefix);
224 }
225
226
231 public boolean isFileSystem() {
232 return FILESYSTEM_PREFIX.equals(prefix);
233 }
234
235
241 @SuppressWarnings("SimplifiableIfStatement")
242 public boolean isParentOf(Location other) {
243 if (pathRegex != null || other.pathRegex != null) {
244 return false;
245 }
246
247 if (isClassPath() && other.isClassPath()) {
248 return (other.getDescriptor() + "/").startsWith(getDescriptor() + "/");
249 }
250 if (isFileSystem() && other.isFileSystem()) {
251 return (other.getDescriptor() + File.separator).startsWith(getDescriptor() + File.separator);
252 }
253 return false;
254 }
255
256
259 public String getPrefix() {
260 return prefix;
261 }
262
263
266 public String getRootPath() {
267 return rootPath;
268 }
269
270
273 public String getPath() {
274 return rawPath;
275 }
276
277
280 public Pattern getPathRegex() {
281 return pathRegex;
282 }
283
284
287 public String getDescriptor() {
288 return prefix + rawPath;
289 }
290
291 @SuppressWarnings("NullableProblems")
292 public int compareTo(Location o) {
293 return getDescriptor().compareTo(o.getDescriptor());
294 }
295
296 @Override
297 public boolean equals(Object o) {
298 if (this == o) return true;
299 if (o == null || getClass() != o.getClass()) return false;
300
301 Location location = (Location) o;
302
303 return getDescriptor().equals(location.getDescriptor());
304 }
305
306 @Override
307 public int hashCode() {
308 return getDescriptor().hashCode();
309 }
310
311
314 @Override
315 public String toString() {
316 return getDescriptor();
317 }
318 }