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