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     */
017    package org.apache.commons.io.filefilter;
018    
019    import java.io.File;
020    import java.io.FileFilter;
021    import java.io.FilenameFilter;
022    import java.util.ArrayList;
023    import java.util.Arrays;
024    import java.util.Collection;
025    import java.util.Date;
026    import java.util.HashSet;
027    import java.util.List;
028    import java.util.Set;
029    
030    import 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 1307459 2012-03-30 15:11:44Z ggregory $
039     */
040    public 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(IOFileFilter filter, 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            List<File> acceptedFiles = new ArrayList<File>();
084            for (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(IOFileFilter filter, Iterable<File> files) {
122            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(IOFileFilter filter, 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(IOFileFilter filter, File... files) {
181            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(IOFileFilter filter, File... files) {
212            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(IOFileFilter filter, 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(IOFileFilter filter,
269                Iterable<File> files, T acceptedFiles) {
270            if (filter == null) {
271                throw new IllegalArgumentException("file filter is null");
272            }
273            if (files != null) {
274                for (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(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(String prefix, 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(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(String suffix, 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(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(String name, 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(IOFileFilter filter1, 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(IOFileFilter filter1, 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(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(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(IOFileFilter... filters) {
449            if (filters == null) {
450                throw new IllegalArgumentException("The filters must not be null");
451            }
452            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(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(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(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(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(long cutoff, 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(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(Date cutoffDate, 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(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(File cutoffReference, 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(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(long threshold, 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(long minSizeInclusive, long maxSizeInclusive ) {
637            IOFileFilter minimumFilter = new SizeFileFilter(minSizeInclusive, true);
638            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(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(String magicNumber, 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(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(byte[] magicNumber, 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(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(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(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(IOFileFilter filter) {
790            if (filter == null) {
791                return FileFileFilter.FILE;
792            }
793            return new AndFileFilter(FileFileFilter.FILE, filter);
794        }
795    
796    }