001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.io.filefilter;
018
019import java.io.File;
020import java.io.FileFilter;
021import java.io.FilenameFilter;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collection;
025import java.util.Date;
026import java.util.HashSet;
027import java.util.List;
028import java.util.Set;
029
030import org.apache.commons.io.IOCase;
031
032/**
033 * Useful utilities for working with file filters. It provides access to all
034 * file filter implementations in this package so you don't have to import
035 * every class you use.
036 *
037 * @since 1.0
038 * @version $Id: FileFilterUtils.java 1471767 2013-04-24 23:24:19Z sebb $
039 */
040public class FileFilterUtils {
041
042    /**
043     * FileFilterUtils is not normally instantiated.
044     */
045    public FileFilterUtils() {
046    }
047
048    //-----------------------------------------------------------------------
049
050    /**
051     * <p>
052     * Applies an {@link IOFileFilter} to the provided {@link File}
053     * objects. The resulting array is a subset of the original file list that
054     * matches the provided filter.
055     * </p>
056     *
057     * <p>
058     * The {@link Set} returned by this method is not guaranteed to be thread safe.
059     * </p>
060     *
061     * <pre>
062     * Set&lt;File&gt; allFiles = ...
063     * Set&lt;File&gt; javaFiles = FileFilterUtils.filterSet(allFiles,
064     *     FileFilterUtils.suffixFileFilter(".java"));
065     * </pre>
066     * @param filter the filter to apply to the set of files.
067     * @param files the array of files to apply the filter to.
068     *
069     * @return a subset of <code>files</code> that is accepted by the
070     *         file filter.
071     * @throws IllegalArgumentException if the filter is {@code null}
072     *         or <code>files</code> contains a {@code null} value.
073     *
074     * @since 2.0
075     */
076    public static File[] filter(final IOFileFilter filter, final File... files) {
077        if (filter == null) {
078            throw new IllegalArgumentException("file filter is null");
079        }
080        if (files == null) {
081            return new File[0];
082        }
083        final List<File> acceptedFiles = new ArrayList<File>();
084        for (final File file : files) {
085            if (file == null) {
086                throw new IllegalArgumentException("file array contains null");
087            }
088            if (filter.accept(file)) {
089                acceptedFiles.add(file);
090            }
091        }
092        return acceptedFiles.toArray(new File[acceptedFiles.size()]);
093    }
094
095    /**
096     * <p>
097     * Applies an {@link IOFileFilter} to the provided {@link File}
098     * objects. The resulting array is a subset of the original file list that
099     * matches the provided filter.
100     * </p>
101     *
102     * <p>
103     * The {@link Set} returned by this method is not guaranteed to be thread safe.
104     * </p>
105     *
106     * <pre>
107     * Set&lt;File&gt; allFiles = ...
108     * Set&lt;File&gt; javaFiles = FileFilterUtils.filterSet(allFiles,
109     *     FileFilterUtils.suffixFileFilter(".java"));
110     * </pre>
111     * @param filter the filter to apply to the set of files.
112     * @param files the array of files to apply the filter to.
113     *
114     * @return a subset of <code>files</code> that is accepted by the
115     *         file filter.
116     * @throws IllegalArgumentException if the filter is {@code null}
117     *         or <code>files</code> contains a {@code null} value.
118     *
119     * @since 2.0
120     */
121    public static File[] filter(final IOFileFilter filter, final Iterable<File> files) {
122        final List<File> acceptedFiles = filterList(filter, files);
123        return acceptedFiles.toArray(new File[acceptedFiles.size()]);
124    }
125
126    /**
127     * <p>
128     * Applies an {@link IOFileFilter} to the provided {@link File}
129     * objects. The resulting list is a subset of the original files that
130     * matches the provided filter.
131     * </p>
132     *
133     * <p>
134     * The {@link List} returned by this method is not guaranteed to be thread safe.
135     * </p>
136     *
137     * <pre>
138     * List&lt;File&gt; filesAndDirectories = ...
139     * List&lt;File&gt; directories = FileFilterUtils.filterList(filesAndDirectories,
140     *     FileFilterUtils.directoryFileFilter());
141     * </pre>
142     * @param filter the filter to apply to each files in the list.
143     * @param files the collection of files to apply the filter to.
144     *
145     * @return a subset of <code>files</code> that is accepted by the
146     *         file filter.
147     * @throws IllegalArgumentException if the filter is {@code null}
148     *         or <code>files</code> contains a {@code null} value.
149     * @since 2.0
150     */
151    public static List<File> filterList(final IOFileFilter filter, final Iterable<File> files) {
152        return filter(filter, files, new ArrayList<File>());
153    }
154
155    /**
156     * <p>
157     * Applies an {@link IOFileFilter} to the provided {@link File}
158     * objects. The resulting list is a subset of the original files that
159     * matches the provided filter.
160     * </p>
161     *
162     * <p>
163     * The {@link List} returned by this method is not guaranteed to be thread safe.
164     * </p>
165     *
166     * <pre>
167     * List&lt;File&gt; filesAndDirectories = ...
168     * List&lt;File&gt; directories = FileFilterUtils.filterList(filesAndDirectories,
169     *     FileFilterUtils.directoryFileFilter());
170     * </pre>
171     * @param filter the filter to apply to each files in the list.
172     * @param files the collection of files to apply the filter to.
173     *
174     * @return a subset of <code>files</code> that is accepted by the
175     *         file filter.
176     * @throws IllegalArgumentException if the filter is {@code null}
177     *         or <code>files</code> contains a {@code null} value.
178     * @since 2.0
179     */
180    public static List<File> filterList(final IOFileFilter filter, final File... files) {
181        final File[] acceptedFiles = filter(filter, files);
182        return Arrays.asList(acceptedFiles);
183    }
184
185    /**
186     * <p>
187     * Applies an {@link IOFileFilter} to the provided {@link File}
188     * objects. The resulting set is a subset of the original file list that
189     * matches the provided filter.
190     * </p>
191     *
192     * <p>
193     * The {@link Set} returned by this method is not guaranteed to be thread safe.
194     * </p>
195     *
196     * <pre>
197     * Set&lt;File&gt; allFiles = ...
198     * Set&lt;File&gt; javaFiles = FileFilterUtils.filterSet(allFiles,
199     *     FileFilterUtils.suffixFileFilter(".java"));
200     * </pre>
201     * @param filter the filter to apply to the set of files.
202     * @param files the collection of files to apply the filter to.
203     *
204     * @return a subset of <code>files</code> that is accepted by the
205     *         file filter.
206     * @throws IllegalArgumentException if the filter is {@code null}
207     *         or <code>files</code> contains a {@code null} value.
208     *
209     * @since 2.0
210     */
211    public static Set<File> filterSet(final IOFileFilter filter, final File... files) {
212        final File[] acceptedFiles = filter(filter, files);
213        return new HashSet<File>(Arrays.asList(acceptedFiles));
214    }
215
216    /**
217     * <p>
218     * Applies an {@link IOFileFilter} to the provided {@link File}
219     * objects. The resulting set is a subset of the original file list that
220     * matches the provided filter.
221     * </p>
222     *
223     * <p>
224     * The {@link Set} returned by this method is not guaranteed to be thread safe.
225     * </p>
226     *
227     * <pre>
228     * Set&lt;File&gt; allFiles = ...
229     * Set&lt;File&gt; javaFiles = FileFilterUtils.filterSet(allFiles,
230     *     FileFilterUtils.suffixFileFilter(".java"));
231     * </pre>
232     * @param filter the filter to apply to the set of files.
233     * @param files the collection of files to apply the filter to.
234     *
235     * @return a subset of <code>files</code> that is accepted by the
236     *         file filter.
237     * @throws IllegalArgumentException if the filter is {@code null}
238     *         or <code>files</code> contains a {@code null} value.
239     *
240     * @since 2.0
241     */
242    public static Set<File> filterSet(final IOFileFilter filter, final Iterable<File> files) {
243        return filter(filter, files, new HashSet<File>());
244    }
245
246    /**
247     * <p>
248     * Applies an {@link IOFileFilter} to the provided {@link File}
249     * objects and appends the accepted files to the other supplied collection.
250     * </p>
251     *
252     * <pre>
253     * List&lt;File&gt; files = ...
254     * List&lt;File&gt; directories = FileFilterUtils.filterList(files,
255     *     FileFilterUtils.sizeFileFilter(FileUtils.FIFTY_MB),
256     *         new ArrayList&lt;File&gt;());
257     * </pre>
258     * @param filter the filter to apply to the collection of files.
259     * @param files the collection of files to apply the filter to.
260     * @param acceptedFiles the list of files to add accepted files to.
261     *
262     * @param <T> the type of the file collection.
263     * @return a subset of <code>files</code> that is accepted by the
264     *         file filter.
265     * @throws IllegalArgumentException if the filter is {@code null}
266     *         or <code>files</code> contains a {@code null} value.
267     */
268    private static <T extends Collection<File>> T filter(final IOFileFilter filter,
269            final Iterable<File> files, final T acceptedFiles) {
270        if (filter == null) {
271            throw new IllegalArgumentException("file filter is null");
272        }
273        if (files != null) {
274            for (final File file : files) {
275                if (file == null) {
276                    throw new IllegalArgumentException("file collection contains null");
277                }
278                if (filter.accept(file)) {
279                    acceptedFiles.add(file);
280                }
281            }
282        }
283        return acceptedFiles;
284    }
285
286    /**
287     * Returns a filter that returns true if the filename starts with the specified text.
288     *
289     * @param prefix  the filename prefix
290     * @return a prefix checking filter
291     * @see PrefixFileFilter
292     */
293    public static IOFileFilter prefixFileFilter(final String prefix) {
294        return new PrefixFileFilter(prefix);
295    }
296
297    /**
298     * Returns a filter that returns true if the filename starts with the specified text.
299     *
300     * @param prefix  the filename prefix
301     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
302     * @return a prefix checking filter
303     * @see PrefixFileFilter
304     * @since 2.0
305     */
306    public static IOFileFilter prefixFileFilter(final String prefix, final IOCase caseSensitivity) {
307        return new PrefixFileFilter(prefix, caseSensitivity);
308    }
309
310    /**
311     * Returns a filter that returns true if the filename ends with the specified text.
312     *
313     * @param suffix  the filename suffix
314     * @return a suffix checking filter
315     * @see SuffixFileFilter
316     */
317    public static IOFileFilter suffixFileFilter(final String suffix) {
318        return new SuffixFileFilter(suffix);
319    }
320
321    /**
322     * Returns a filter that returns true if the filename ends with the specified text.
323     *
324     * @param suffix  the filename suffix
325     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
326     * @return a suffix checking filter
327     * @see SuffixFileFilter
328     * @since 2.0
329     */
330    public static IOFileFilter suffixFileFilter(final String suffix, final IOCase caseSensitivity) {
331        return new SuffixFileFilter(suffix, caseSensitivity);
332    }
333
334    /**
335     * Returns a filter that returns true if the filename matches the specified text.
336     *
337     * @param name  the filename
338     * @return a name checking filter
339     * @see NameFileFilter
340     */
341    public static IOFileFilter nameFileFilter(final String name) {
342        return new NameFileFilter(name);
343    }
344
345    /**
346     * Returns a filter that returns true if the filename matches the specified text.
347     *
348     * @param name  the filename
349     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
350     * @return a name checking filter
351     * @see NameFileFilter
352     * @since 2.0
353     */
354    public static IOFileFilter nameFileFilter(final String name, final IOCase caseSensitivity) {
355        return new NameFileFilter(name, caseSensitivity);
356    }
357
358    /**
359     * Returns a filter that checks if the file is a directory.
360     *
361     * @return file filter that accepts only directories and not files
362     * @see DirectoryFileFilter#DIRECTORY
363     */
364    public static IOFileFilter directoryFileFilter() {
365        return DirectoryFileFilter.DIRECTORY;
366    }
367
368    /**
369     * Returns a filter that checks if the file is a file (and not a directory).
370     *
371     * @return file filter that accepts only files and not directories
372     * @see FileFileFilter#FILE
373     */
374    public static IOFileFilter fileFileFilter() {
375        return FileFileFilter.FILE;
376    }
377
378    //-----------------------------------------------------------------------
379    /**
380     * Returns a filter that ANDs the two specified filters.
381     *
382     * @param filter1  the first filter
383     * @param filter2  the second filter
384     * @return a filter that ANDs the two specified filters
385     * @see #and(IOFileFilter...)
386     * @see AndFileFilter
387     * @deprecated use {@link #and(IOFileFilter...)}
388     */
389    @Deprecated
390    public static IOFileFilter andFileFilter(final IOFileFilter filter1, final IOFileFilter filter2) {
391        return new AndFileFilter(filter1, filter2);
392    }
393
394    /**
395     * Returns a filter that ORs the two specified filters.
396     *
397     * @param filter1  the first filter
398     * @param filter2  the second filter
399     * @return a filter that ORs the two specified filters
400     * @see #or(IOFileFilter...)
401     * @see OrFileFilter
402     * @deprecated use {@link #or(IOFileFilter...)}
403     */
404    @Deprecated
405    public static IOFileFilter orFileFilter(final IOFileFilter filter1, final IOFileFilter filter2) {
406        return new OrFileFilter(filter1, filter2);
407    }
408
409    /**
410     * Returns a filter that ANDs the specified filters.
411     *
412     * @param filters the IOFileFilters that will be ANDed together.
413     * @return a filter that ANDs the specified filters
414     *
415     * @throws IllegalArgumentException if the filters are null or contain a
416     *         null value.
417     * @see AndFileFilter
418     * @since 2.0
419     */
420    public static IOFileFilter and(final IOFileFilter... filters) {
421        return new AndFileFilter(toList(filters));
422    }
423
424    /**
425     * Returns a filter that ORs the specified filters.
426     *
427     * @param filters the IOFileFilters that will be ORed together.
428     * @return a filter that ORs the specified filters
429     *
430     * @throws IllegalArgumentException if the filters are null or contain a
431     *         null value.
432     * @see OrFileFilter
433     * @since 2.0
434     */
435    public static IOFileFilter or(final IOFileFilter... filters) {
436        return new OrFileFilter(toList(filters));
437    }
438
439    /**
440     * Create a List of file filters.
441     *
442     * @param filters The file filters
443     * @return The list of file filters
444     * @throws IllegalArgumentException if the filters are null or contain a
445     *         null value.
446     * @since 2.0
447     */
448    public static List<IOFileFilter> toList(final IOFileFilter... filters) {
449        if (filters == null) {
450            throw new IllegalArgumentException("The filters must not be null");
451        }
452        final List<IOFileFilter> list = new ArrayList<IOFileFilter>(filters.length);
453        for (int i = 0; i < filters.length; i++) {
454            if (filters[i] == null) {
455                throw new IllegalArgumentException("The filter[" + i + "] is null");
456            }
457            list.add(filters[i]);
458        }
459        return list;
460    }
461
462    /**
463     * Returns a filter that NOTs the specified filter.
464     *
465     * @param filter  the filter to invert
466     * @return a filter that NOTs the specified filter
467     * @see NotFileFilter
468     */
469    public static IOFileFilter notFileFilter(final IOFileFilter filter) {
470        return new NotFileFilter(filter);
471    }
472
473    //-----------------------------------------------------------------------
474    /**
475     * Returns a filter that always returns true.
476     *
477     * @return a true filter
478     * @see TrueFileFilter#TRUE
479     */
480    public static IOFileFilter trueFileFilter() {
481        return TrueFileFilter.TRUE;
482    }
483
484    /**
485     * Returns a filter that always returns false.
486     *
487     * @return a false filter
488     * @see FalseFileFilter#FALSE
489     */
490    public static IOFileFilter falseFileFilter() {
491        return FalseFileFilter.FALSE;
492    }
493
494    //-----------------------------------------------------------------------
495    /**
496     * Returns an <code>IOFileFilter</code> that wraps the
497     * <code>FileFilter</code> instance.
498     *
499     * @param filter  the filter to be wrapped
500     * @return a new filter that implements IOFileFilter
501     * @see DelegateFileFilter
502     */
503    public static IOFileFilter asFileFilter(final FileFilter filter) {
504        return new DelegateFileFilter(filter);
505    }
506
507    /**
508     * Returns an <code>IOFileFilter</code> that wraps the
509     * <code>FilenameFilter</code> instance.
510     *
511     * @param filter  the filter to be wrapped
512     * @return a new filter that implements IOFileFilter
513     * @see DelegateFileFilter
514     */
515    public static IOFileFilter asFileFilter(final FilenameFilter filter) {
516        return new DelegateFileFilter(filter);
517    }
518
519    //-----------------------------------------------------------------------
520    /**
521     * Returns a filter that returns true if the file was last modified after
522     * the specified cutoff time.
523     *
524     * @param cutoff  the time threshold
525     * @return an appropriately configured age file filter
526     * @see AgeFileFilter
527     * @since 1.2
528     */
529    public static IOFileFilter ageFileFilter(final long cutoff) {
530        return new AgeFileFilter(cutoff);
531    }
532
533    /**
534     * Returns a filter that filters files based on a cutoff time.
535     *
536     * @param cutoff  the time threshold
537     * @param acceptOlder  if true, older files get accepted, if false, newer
538     * @return an appropriately configured age file filter
539     * @see AgeFileFilter
540     * @since 1.2
541     */
542    public static IOFileFilter ageFileFilter(final long cutoff, final boolean acceptOlder) {
543        return new AgeFileFilter(cutoff, acceptOlder);
544    }
545
546    /**
547     * Returns a filter that returns true if the file was last modified after
548     * the specified cutoff date.
549     *
550     * @param cutoffDate  the time threshold
551     * @return an appropriately configured age file filter
552     * @see AgeFileFilter
553     * @since 1.2
554     */
555    public static IOFileFilter ageFileFilter(final Date cutoffDate) {
556        return new AgeFileFilter(cutoffDate);
557    }
558
559    /**
560     * Returns a filter that filters files based on a cutoff date.
561     *
562     * @param cutoffDate  the time threshold
563     * @param acceptOlder  if true, older files get accepted, if false, newer
564     * @return an appropriately configured age file filter
565     * @see AgeFileFilter
566     * @since 1.2
567     */
568    public static IOFileFilter ageFileFilter(final Date cutoffDate, final boolean acceptOlder) {
569        return new AgeFileFilter(cutoffDate, acceptOlder);
570    }
571
572    /**
573     * Returns a filter that returns true if the file was last modified after
574     * the specified reference file.
575     *
576     * @param cutoffReference  the file whose last modification
577     *        time is usesd as the threshold age of the files
578     * @return an appropriately configured age file filter
579     * @see AgeFileFilter
580     * @since 1.2
581     */
582    public static IOFileFilter ageFileFilter(final File cutoffReference) {
583        return new AgeFileFilter(cutoffReference);
584    }
585
586    /**
587     * Returns a filter that filters files based on a cutoff reference file.
588     *
589     * @param cutoffReference  the file whose last modification
590     *        time is usesd as the threshold age of the files
591     * @param acceptOlder  if true, older files get accepted, if false, newer
592     * @return an appropriately configured age file filter
593     * @see AgeFileFilter
594     * @since 1.2
595     */
596    public static IOFileFilter ageFileFilter(final File cutoffReference, final boolean acceptOlder) {
597        return new AgeFileFilter(cutoffReference, acceptOlder);
598    }
599
600    //-----------------------------------------------------------------------
601    /**
602     * Returns a filter that returns true if the file is bigger than a certain size.
603     *
604     * @param threshold  the file size threshold
605     * @return an appropriately configured SizeFileFilter
606     * @see SizeFileFilter
607     * @since 1.2
608     */
609    public static IOFileFilter sizeFileFilter(final long threshold) {
610        return new SizeFileFilter(threshold);
611    }
612
613    /**
614     * Returns a filter that filters based on file size.
615     *
616     * @param threshold  the file size threshold
617     * @param acceptLarger  if true, larger files get accepted, if false, smaller
618     * @return an appropriately configured SizeFileFilter
619     * @see SizeFileFilter
620     * @since 1.2
621     */
622    public static IOFileFilter sizeFileFilter(final long threshold, final boolean acceptLarger) {
623        return new SizeFileFilter(threshold, acceptLarger);
624    }
625
626    /**
627     * Returns a filter that accepts files whose size is &gt;= minimum size
628     * and &lt;= maximum size.
629     *
630     * @param minSizeInclusive the minimum file size (inclusive)
631     * @param maxSizeInclusive the maximum file size (inclusive)
632     * @return an appropriately configured IOFileFilter
633     * @see SizeFileFilter
634     * @since 1.3
635     */
636    public static IOFileFilter sizeRangeFileFilter(final long minSizeInclusive, final long maxSizeInclusive ) {
637        final IOFileFilter minimumFilter = new SizeFileFilter(minSizeInclusive, true);
638        final IOFileFilter maximumFilter = new SizeFileFilter(maxSizeInclusive + 1L, false);
639        return new AndFileFilter(minimumFilter, maximumFilter);
640    }
641
642    /**
643     * Returns a filter that accepts files that begin with the provided magic
644     * number.
645     *
646     * @param magicNumber the magic number (byte sequence) to match at the
647     *        beginning of each file.
648     *
649     * @return an IOFileFilter that accepts files beginning with the provided
650     *         magic number.
651     *
652     * @throws IllegalArgumentException if <code>magicNumber</code> is
653     *         {@code null} or the empty String.
654     * @see MagicNumberFileFilter
655     * @since 2.0
656     */
657    public static IOFileFilter magicNumberFileFilter(final String magicNumber) {
658        return new MagicNumberFileFilter(magicNumber);
659    }
660
661    /**
662     * Returns a filter that accepts files that contains the provided magic
663     * number at a specified offset within the file.
664     *
665     * @param magicNumber the magic number (byte sequence) to match at the
666     *        provided offset in each file.
667     * @param offset the offset within the files to look for the magic number.
668     *
669     * @return an IOFileFilter that accepts files containing the magic number
670     *         at the specified offset.
671     *
672     * @throws IllegalArgumentException if <code>magicNumber</code> is
673     *         {@code null} or the empty String, or if offset is a
674     *         negative number.
675     * @see MagicNumberFileFilter
676     * @since 2.0
677     */
678    public static IOFileFilter magicNumberFileFilter(final String magicNumber, final long offset) {
679        return new MagicNumberFileFilter(magicNumber, offset);
680    }
681
682    /**
683     * Returns a filter that accepts files that begin with the provided magic
684     * number.
685     *
686     * @param magicNumber the magic number (byte sequence) to match at the
687     *        beginning of each file.
688     *
689     * @return an IOFileFilter that accepts files beginning with the provided
690     *         magic number.
691     *
692     * @throws IllegalArgumentException if <code>magicNumber</code> is
693     *         {@code null} or is of length zero.
694     * @see MagicNumberFileFilter
695     * @since 2.0
696     */
697    public static IOFileFilter magicNumberFileFilter(final byte[] magicNumber) {
698        return new MagicNumberFileFilter(magicNumber);
699    }
700
701    /**
702     * Returns a filter that accepts files that contains the provided magic
703     * number at a specified offset within the file.
704     *
705     * @param magicNumber the magic number (byte sequence) to match at the
706     *        provided offset in each file.
707     * @param offset the offset within the files to look for the magic number.
708     *
709     * @return an IOFileFilter that accepts files containing the magic number
710     *         at the specified offset.
711     *
712     * @throws IllegalArgumentException if <code>magicNumber</code> is
713     *         {@code null}, or contains no bytes, or <code>offset</code>
714     *         is a negative number.
715     * @see MagicNumberFileFilter
716     * @since 2.0
717     */
718    public static IOFileFilter magicNumberFileFilter(final byte[] magicNumber, final long offset) {
719        return new MagicNumberFileFilter(magicNumber, offset);
720    }
721
722    //-----------------------------------------------------------------------
723    /* Constructed on demand and then cached */
724    private static final IOFileFilter cvsFilter = notFileFilter(
725            and(directoryFileFilter(), nameFileFilter("CVS")));
726
727    /* Constructed on demand and then cached */
728    private static final IOFileFilter svnFilter = notFileFilter(
729            and(directoryFileFilter(), nameFileFilter(".svn")));
730
731    /**
732     * Decorates a filter to make it ignore CVS directories.
733     * Passing in {@code null} will return a filter that accepts everything
734     * except CVS directories.
735     *
736     * @param filter  the filter to decorate, null means an unrestricted filter
737     * @return the decorated filter, never null
738     * @since 1.1 (method existed but had bug in 1.0)
739     */
740    public static IOFileFilter makeCVSAware(final IOFileFilter filter) {
741        if (filter == null) {
742            return cvsFilter;
743        } else {
744            return and(filter, cvsFilter);
745        }
746    }
747
748    /**
749     * Decorates a filter to make it ignore SVN directories.
750     * Passing in {@code null} will return a filter that accepts everything
751     * except SVN directories.
752     *
753     * @param filter  the filter to decorate, null means an unrestricted filter
754     * @return the decorated filter, never null
755     * @since 1.1
756     */
757    public static IOFileFilter makeSVNAware(final IOFileFilter filter) {
758        if (filter == null) {
759            return svnFilter;
760        } else {
761            return and(filter, svnFilter);
762        }
763    }
764
765    //-----------------------------------------------------------------------
766    /**
767     * Decorates a filter so that it only applies to directories and not to files.
768     *
769     * @param filter  the filter to decorate, null means an unrestricted filter
770     * @return the decorated filter, never null
771     * @see DirectoryFileFilter#DIRECTORY
772     * @since 1.3
773     */
774    public static IOFileFilter makeDirectoryOnly(final IOFileFilter filter) {
775        if (filter == null) {
776            return DirectoryFileFilter.DIRECTORY;
777        }
778        return new AndFileFilter(DirectoryFileFilter.DIRECTORY, filter);
779    }
780
781    /**
782     * Decorates a filter so that it only applies to files and not to directories.
783     *
784     * @param filter  the filter to decorate, null means an unrestricted filter
785     * @return the decorated filter, never null
786     * @see FileFileFilter#FILE
787     * @since 1.3
788     */
789    public static IOFileFilter makeFileOnly(final IOFileFilter filter) {
790        if (filter == null) {
791            return FileFileFilter.FILE;
792        }
793        return new AndFileFilter(FileFileFilter.FILE, filter);
794    }
795
796}