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.Serializable;
021import java.nio.file.FileVisitResult;
022import java.nio.file.Path;
023import java.nio.file.attribute.BasicFileAttributes;
024import java.util.List;
025import java.util.Objects;
026
027import org.apache.commons.io.IOCase;
028
029/**
030 * Filters files based on the suffix (what the file name ends with).
031 * This is used in retrieving all the files of a particular type.
032 * <p>
033 * For example, to retrieve and print all {@code *.java} files
034 * in the current directory:
035 * </p>
036 * <h2>Using Classic IO</h2>
037 * <pre>
038 * File dir = new File(".");
039 * String[] files = dir.list(new SuffixFileFilter(".java"));
040 * for (String file : files) {
041 *     System.out.println(file);
042 * }
043 * </pre>
044 *
045 * <h2>Using NIO</h2>
046 * <pre>
047 * final Path dir = Paths.get("");
048 * final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(new SuffixFileFilter(".java"));
049 * //
050 * // Walk one dir
051 * Files.<b>walkFileTree</b>(dir, Collections.emptySet(), 1, visitor);
052 * System.out.println(visitor.getPathCounters());
053 * System.out.println(visitor.getFileList());
054 * //
055 * visitor.getPathCounters().reset();
056 * //
057 * // Walk dir tree
058 * Files.<b>walkFileTree</b>(dir, visitor);
059 * System.out.println(visitor.getPathCounters());
060 * System.out.println(visitor.getDirList());
061 * System.out.println(visitor.getFileList());
062 * </pre>
063 *
064 * @since 1.0
065 * @see FileFilterUtils#suffixFileFilter(String)
066 * @see FileFilterUtils#suffixFileFilter(String, IOCase)
067 */
068public class SuffixFileFilter extends AbstractFileFilter implements Serializable {
069
070    private static final long serialVersionUID = -3389157631240246157L;
071
072    /** The file name suffixes to search for */
073    private final String[] suffixes;
074
075    /** Whether the comparison is case sensitive. */
076    private final IOCase caseSensitivity;
077
078    /**
079     * Constructs a new Suffix file filter for a list of suffixes.
080     *
081     * @param suffixes  the suffixes to allow, must not be null
082     * @throws IllegalArgumentException if the suffix list is null
083     * @throws ClassCastException if the list does not contain Strings
084     */
085    public SuffixFileFilter(final List<String> suffixes) {
086        this(suffixes, IOCase.SENSITIVE);
087    }
088
089    /**
090     * Constructs a new Suffix file filter for a list of suffixes
091     * specifying case-sensitivity.
092     *
093     * @param suffixes  the suffixes to allow, must not be null
094     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
095     * @throws IllegalArgumentException if the suffix list is null
096     * @throws ClassCastException if the list does not contain Strings
097     * @since 1.4
098     */
099    public SuffixFileFilter(final List<String> suffixes, final IOCase caseSensitivity) {
100        if (suffixes == null) {
101            throw new IllegalArgumentException("The list of suffixes must not be null");
102        }
103        this.suffixes = suffixes.toArray(EMPTY_STRING_ARRAY);
104        this.caseSensitivity = caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity;
105    }
106
107    /**
108     * Constructs a new Suffix file filter for a single extension.
109     *
110     * @param suffix  the suffix to allow, must not be null
111     * @throws IllegalArgumentException if the suffix is null
112     */
113    public SuffixFileFilter(final String suffix) {
114        this(suffix, IOCase.SENSITIVE);
115    }
116
117    /**
118     * Constructs a new Suffix file filter for an array of suffixes.
119     * <p>
120     * The array is not cloned, so could be changed after constructing the
121     * instance. This would be inadvisable however.
122     *
123     * @param suffixes  the suffixes to allow, must not be null
124     * @throws IllegalArgumentException if the suffix array is null
125     */
126    public SuffixFileFilter(final String... suffixes) {
127        this(suffixes, IOCase.SENSITIVE);
128    }
129
130    /**
131     * Constructs a new Suffix file filter for a single extension
132     * specifying case-sensitivity.
133     *
134     * @param suffix  the suffix to allow, must not be null
135     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
136     * @throws IllegalArgumentException if the suffix is null
137     * @since 1.4
138     */
139    public SuffixFileFilter(final String suffix, final IOCase caseSensitivity) {
140        if (suffix == null) {
141            throw new IllegalArgumentException("The suffix must not be null");
142        }
143        this.suffixes = new String[] {suffix};
144        this.caseSensitivity = caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity;
145    }
146
147    /**
148     * Constructs a new Suffix file filter for an array of suffixes
149     * specifying case-sensitivity.
150     *
151     * @param suffixes  the suffixes to allow, must not be null
152     * @param caseSensitivity  how to handle case sensitivity, null means case-sensitive
153     * @throws IllegalArgumentException if the suffix array is null
154     * @since 1.4
155     */
156    public SuffixFileFilter(final String[] suffixes, final IOCase caseSensitivity) {
157        if (suffixes == null) {
158            throw new IllegalArgumentException("The array of suffixes must not be null");
159        }
160        this.suffixes = new String[suffixes.length];
161        System.arraycopy(suffixes, 0, this.suffixes, 0, suffixes.length);
162        this.caseSensitivity = caseSensitivity == null ? IOCase.SENSITIVE : caseSensitivity;
163    }
164
165    /**
166     * Checks to see if the file name ends with the suffix.
167     *
168     * @param file  the File to check
169     * @return true if the file name ends with one of our suffixes
170     */
171    @Override
172    public boolean accept(final File file) {
173        return accept(file.getName());
174    }
175
176    /**
177     * Checks to see if the file name ends with the suffix.
178     *
179     * @param file  the File directory
180     * @param name  the file name
181     * @return true if the file name ends with one of our suffixes
182     */
183    @Override
184    public boolean accept(final File file, final String name) {
185        return accept(name);
186    }
187
188    /**
189     * Checks to see if the file name ends with the suffix.
190     * @param file  the File to check
191     *
192     * @return true if the file name ends with one of our suffixes
193     * @since 2.9.0
194     */
195    @Override
196    public FileVisitResult accept(final Path file, final BasicFileAttributes attributes) {
197        return toFileVisitResult(accept(Objects.toString(file.getFileName(), null)), file);
198    }
199
200    private boolean accept(final String name) {
201        for (final String suffix : this.suffixes) {
202            if (caseSensitivity.checkEndsWith(name, suffix)) {
203                return true;
204            }
205        }
206        return false;
207    }
208
209    /**
210     * Provide a String representation of this file filter.
211     *
212     * @return a String representation
213     */
214    @Override
215    public String toString() {
216        final StringBuilder buffer = new StringBuilder();
217        buffer.append(super.toString());
218        buffer.append("(");
219        if (suffixes != null) {
220            for (int i = 0; i < suffixes.length; i++) {
221                if (i > 0) {
222                    buffer.append(",");
223                }
224                buffer.append(suffixes[i]);
225            }
226        }
227        buffer.append(")");
228        return buffer.toString();
229    }
230
231}