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