/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq;
import static org.jooq.FilePattern.Sort.SEMANTIC;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
import org.jooq.exception.IOException;
import org.jooq.tools.JooqLogger;
A utility class that can traverse a directory structure given some ant-style
file patterns, or classpath resources.
The following algorithm is applied when traversing sources:
- If
pattern
is a valid classpath resource, load the single Source
from there
- If
pattern
is a valid file on the file system, load the single Source
from there
- Match all files on the file system according to
pattern
(interpreted as an ant-style file pattern), and load all of the Source
items given sort()
. An example pattern is src/main/resources/**/*.sql
This is INTERNAL API. Please do not use directly as API may change
incompatibly.
Author: Lukas Eder
/**
* A utility class that can traverse a directory structure given some ant-style
* file patterns, or classpath resources.
* <p>
* The following algorithm is applied when traversing sources:
* <p>
* <ul>
* <li>If <code>pattern</code> is a valid classpath resource, load the single
* {@link Source} from there</li>
* <li>If <code>pattern</code> is a valid file on the file system, load the
* single {@link Source} from there</li>
* <li>Match all files on the file system according to <code>pattern</code>
* (interpreted as an ant-style file pattern), and load all of the
* {@link Source} items given {@link #sort()}. An example pattern is
* <code>src/main/resources/**/*.sql</code></li>
* </ul>
* <p>
* This is INTERNAL API. Please do not use directly as API may change
* incompatibly.
*
* @author Lukas Eder
*/
@Internal
public final class FilePattern {
private static final JooqLogger log = JooqLogger.getLogger(FilePattern.class);
private final Sort sort;
private final Comparator<File> comparator;
private final File basedir;
private final String pattern;
private final String encoding;
public FilePattern() {
this(
(Sort) null,
(File) null,
"**",
"UTF-8"
);
}
private FilePattern(
Sort sort,
File basedir,
String pattern,
String encoding
) {
this.sort = sort;
this.comparator = fileComparator(sort);
this.basedir = basedir == null ? new File(".") : basedir;
this.pattern = pattern;
this.encoding = encoding;
}
public final Sort sort() {
return sort;
}
public final FilePattern sort(Sort newSort) {
return new FilePattern(
newSort,
basedir,
pattern,
encoding
);
}
public final File basedir() {
return basedir;
}
public final FilePattern basedir(File newBasedir) {
return new FilePattern(
sort,
newBasedir,
pattern,
encoding
);
}
public final String pattern() {
return pattern;
}
public final FilePattern pattern(String newPattern) {
return new FilePattern(
sort,
basedir,
newPattern,
encoding
);
}
public final String encoding() {
return encoding;
}
public final FilePattern encoding(String newEncoding) {
return new FilePattern(
sort,
basedir,
pattern,
newEncoding
);
}
private static final Comparator<File> fileComparator(Sort sort) {
if (sort == null)
sort = SEMANTIC;
switch (sort) {
case ALPHANUMERIC:
return new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
return o1.compareTo(o2);
}
};
case NONE:
return null;
case FLYWAY:
return FlywayFileComparator.INSTANCE;
case SEMANTIC:
return FileComparator.INSTANCE;
default:
throw new IllegalArgumentException("Unsupported sort: " + sort);
}
}
Retrieve a set of Source
items from this pattern. Throws: - IOException – if something goes wrong while loading file contents.
/**
* Retrieve a set of {@link Source} items from this pattern.
*
* @throws IOException if something goes wrong while loading file contents.
*/
public final List<Source> collect() {
final List<Source> list = new ArrayList<>();
load(new Loader() {
@Override
public void load(Source source) {
list.add(source);
}
});
return list;
}
Load a set of Source
items from this pattern. Throws: - IOException – if something goes wrong while loading file contents.
/**
* Load a set of {@link Source} items from this pattern.
*
* @throws IOException if something goes wrong while loading file contents.
*/
public final void load(Loader loader) {
boolean loaded = false;
URL url = null;
try {
url = FilePattern.class.getResource(pattern);
}
// [#10143] Starting with Java 7, and especially when running on the module path,
// there could be an InvalidPathException here.
catch (Exception ignore) {}
File file = null;
try {
if (url != null) {
log.info("Reading from classpath: " + pattern);
load0(new File(url.toURI()), loader);
loaded = true;
}
else {
file = new File(pattern);
if (file.exists()) {
load(file, comparator, null, loader);
loaded = true;
}
else if (!pattern.contains("*") && !pattern.contains("?")) {
load(new File(basedir, pattern), comparator, null, loader);
loaded = true;
}
else {
// [#9726] The wildcard could be in the middle of a path segment, which
// has to be ignored, e.g. the prefix of a/b*/c is a/
String prefix = pattern.replaceAll("[^\\/]*?[*?].*", "");
file = new File(prefix);
if (!file.isAbsolute())
file = new File(basedir, prefix).getAbsoluteFile();
Pattern regex = Pattern.compile("^.*?"
+ pattern
.replace("\\", "/")
.replace(".", "\\.")
.replace("?", "[^/]")
.replace("**", ".+?")
.replace("*", "[^/]*")
+ "$"
);
load(file, comparator, regex, loader);
loaded = true;
}
}
}
// It is quite unlikely that a classpath URL doesn't produce a valid URI
catch (URISyntaxException e) {
throw new RuntimeException(e);
}
catch (java.io.IOException e) {
throw new IOException("Error while loading pattern", e);
}
if (!loaded)
log.error("Could not find source(s) : " + pattern);
}
private final void load(
File file,
Comparator<File> fileComparator,
Pattern regex,
Loader loader
) throws java.io.IOException {
if (file.isFile()) {
if (regex == null || regex.matcher(file.getCanonicalPath().replace("\\", "/")).matches()) {
log.info("Reading from: " + file + " [*]");
load0(file, loader);
}
}
else if (file.isDirectory()) {
log.info("Reading from: " + file);
File[] files = file.listFiles();
if (files != null) {
if (fileComparator != null)
Arrays.sort(files, fileComparator);
for (File f : files)
load(f, comparator, regex, loader);
}
}
}
private final void load0(File file, Loader loader) {
try {
loader.load(Source.of(file, encoding));
}
catch (RuntimeException e) {
log.error("Error while loading file: " + file);
throw e;
}
}
@Override
public String toString() {
return pattern;
}
A callback interface that allows for loading a Source
. /**
* A callback interface that allows for loading a {@link Source}.
*/
@FunctionalInterface
public interface Loader {
void load(Source source);
}
The sort algorithm to be applied to directory contents.
/**
* The sort algorithm to be applied to directory contents.
*/
public enum Sort {
Semantic, version aware sorting (the default).
For example:
version-1
version-2
version-10
/**
* Semantic, version aware sorting (the default).
* <p>
* For example:
*
* <pre>
* version-1
* version-2
* version-10
* </pre>
*/
SEMANTIC,
Standard alphanumeric sorting.
For example:
version-1
version-10
version-2
/**
* Standard alphanumeric sorting.
* <p>
* For example:
*
* <pre>
* version-1
* version-10
* version-2
* </pre>
*/
ALPHANUMERIC,
Flyway compatible sorting.
/**
* Flyway compatible sorting.
*/
FLYWAY,
No explicit sorting (may be non deterministic, depending on the file
system).
/**
* No explicit sorting (may be non deterministic, depending on the file
* system).
*/
NONE;
public static final Sort of(String sort) {
if ("alphanumeric".equals(sort))
return ALPHANUMERIC;
else if ("none".equals(sort))
return NONE;
else if ("flyway".equals(sort))
return FLYWAY;
else
return SEMANTIC;
}
}
}