I'm looking for a lib which would provide a method which would give me a list of files matching given Ant-like pattern.
For *foo/**/*.txt
I'd get
foo/x.txt
foo/bar/baz/.txt
myfoo/baz/boo/bar.txt
etc. I know it's achievable with DirWalker and
PathMatcher mat = FileSystems.getDefault().getPathMatcher("glob:" + filesPattern);
, but I'd rather some maintained lib. I expected Commons IO to have it but no.
Update: I'm happy with reusing Ant's code, but would prefer something smaller than whole Ant.
As of Java 7 there is a recursive directory scan. Java 8 can improve it a bit syntactically.
Path start = FileSystems.getDefault().getPath(",,,");
walk(start, "**.java");
One needs a glob matching class, best on directory level, so as to skip directories.
class Glob {
public boolean matchesFile(Path path) {
return ...;
}
public boolean matchesParentDir(Path path) {
return ...;
}
}
Then the walking would be:
public static void walk(Path start, String searchGlob) throws IOException {
final Glob glob = new Glob(searchGlob);
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
if (glob.matchesFile(file)) {
...; // Process file
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
return glob.matchesParentDir(dir)
? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
}
});
}
}
So I sacrified few MB of app's size for the sake of speed and used Ant's DirectoryScanner
in the end.
Also, there's Spring's PathMatchingResourcePatternResolver.
//files = new PatternDirWalker( filesPattern ).list( baseDir );
files = new DirScanner( filesPattern ).list( baseDir );
public class DirScanner {
private String pattern;
public DirScanner( String pattern ) {
this.pattern = pattern;
}
public List<File> list( File dirToScan ) throws IOException {
DirectoryScanner ds = new DirectoryScanner();
String[] includes = { this.pattern };
//String[] excludes = {"modules\\*\\**"};
ds.setIncludes(includes);
//ds.setExcludes(excludes);
ds.setBasedir( dirToScan );
//ds.setCaseSensitive(true);
ds.scan();
String[] matches = ds.getIncludedFiles();
List<File> files = new ArrayList(matches.length);
for (int i = 0; i < matches.length; i++) {
files.add( new File(matches[i]) );
}
return files;
}
}// class
And here's my impl I started to code, not finished, just if someone would like to finish it. The idea was it would keep a stack of patterns, traverse the dir tree and compare the contents to the actual stack depth and the rest of it in case of **
.
But I resorted to PathMatcher
and then to Ant's impl.
public class PatternDirWalker {
//private static final Logger log = LoggerFactory.getLogger( PatternDirWalker.class );
private String pattern;
private List segments;
private PathMatcher mat;
public PatternDirWalker( String pattern ) {
this.pattern = pattern;
this.segments = parseSegments(pattern);
this.mat = FileSystems.getDefault().getPathMatcher("glob:" + pattern);
}
public List<File> list( File dirToScan ) throws IOException{
return new DirectoryWalker() {
List<File> files = new LinkedList();
@Override protected void handleFile( File file, int depth, Collection results ) throws IOException {
if( PatternDirWalker.this.mat.matches( file.toPath()) )
results.add( file );
}
public List<File> findMatchingFiles( File dirToWalk ) throws IOException {
this.walk( dirToWalk, this.files );
return this.files;
}
}.findMatchingFiles( dirToScan );
}// list()
private List<Segment> parseSegments( String pattern ) {
String[] parts = StringUtils.split("/", pattern);
List<Segment> segs = new ArrayList(parts.length);
for( String part : parts ) {
Segment seg = new Segment(part);
segs.add( seg );
}
return segs;
}
class Segment {
public final String pat; // TODO: Tokenize
private Segment( String pat ) {
this.pat = pat;
}
}
}// class
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With