salocinx
salocinx

Reputation: 3823

How to exclude a specific file with Apache FileFilterUtils?

Please consider the following folder structure:

src
  |_text1.txt
  |_text2.txt
  |_content
      |_text1.txt
      |_text2.txt

How do I have to design an org.apache.commons.io.filefilter.IOFileFilter to exclude the src/text1.txt and src/text2.txt but keeping src/content/text1.txt and src/content/text2.txt ?

Currently my filter looks like this:

IOFileFilter filter = FileFilterUtils.and(
                    FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("text1.txt", IOCase.SENSITIVE)),
                    FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("text2.txt", IOCase.SENSITIVE))
            );
FileUtils.copyDirectory(new File("src"), new File("dst"), filter);

But the code snippet above obviously doesn't copy the two text files within the src/content/ folder either (which I want to have copied)... Btw. the names of the text files are not changeable.

Any ideas?

Upvotes: 0

Views: 2201

Answers (1)

Thomas
Thomas

Reputation: 88747

AFAIK commons io doesn't provide something like a PathFileFilter thus you'd have to add your own filte here.

NameFileFilter, as the name implies, only checks for the file name, i.e. the path is not relevant.

Providing your own filter should not be that hard. I'd suggest subclassing AbstractFileFilter or NameFileFilter here. Subclassing NameFileFilter might be considered a somewhat dirty approach, since you're not only checking the names, but would just require you to override the accept() methods:

public boolean accept(File file) {
   return accept( file.getPath() );
}

public boolean accept(File dir, String name) {
  //use normalize to account for possible double separators or windows paths which use \
  return accept( FilenameUtils.normalize( dir.getPath() + "/" + name ) );
}

protected boolean accept( String path ) {     
   for (String nameSuffix: names) {
     if (caseSensitivity.checkEndsWith( path, nameSuffix )) {
         return true;
     }
   }
   return false;
}

Then you'd use it like FileFilterUtils.notFileFilter(new PathFileFilter("/text1.txt")) etc.

Alternatively you could provide a set of patterns and check those:

private Set<Pattern> pathPatterns = new HashSet<>();

PathFileFilter(String... patterns) {
   for( String p : patterns ) {
     pathPatterns.add( Pattern.compile(p) );
   }
}

protected boolean accept( String path ) {     
  for (Pattern pattern : pathPatterns) {
    //separatorsToUnix is used to convert \ to /
    if ( pattern.matches( FilenameUtils.separatorsToUnix( path ) )) {
      return true;
    }
  }
  return false;
}

Usage: new PathFileFilter("(?i)(.*/)?test[12]\\.txt"); or new PathFileFilter("(?i)(.*/)?test1\\.txt", "(?i)(.*/)?anothertest2\\.txt");

Short breakdown of the regex:

  • (?i) makes the expression case-insensitive, leave it out for case-sensitive matches
  • (.*/)? means that if the filename is preceeded by anything it must end with a slash, i.e. this would match some/path/test1.txt but not someothertest1.txt.
  • test[12]\\.txt would be the file name, here meaning text followed by 1 or 2 and finally .txt

Upvotes: 1

Related Questions