1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.io.filefilter;
18
19 import java.io.File;
20 import java.io.Serializable;
21 import java.nio.file.FileVisitResult;
22 import java.nio.file.Path;
23 import java.nio.file.attribute.BasicFileAttributes;
24 import java.util.List;
25 import java.util.Objects;
26 import java.util.stream.Stream;
27
28 import org.apache.commons.io.IOCase;
29 import org.apache.commons.io.file.PathUtils;
30
31 /**
32 * Filters files based on the suffix (what the file name ends with).
33 * This is used in retrieving all the files of a particular type.
34 * <p>
35 * For example, to retrieve and print all {@code *.java} files
36 * in the current directory:
37 * </p>
38 * <h2>Using Classic IO</h2>
39 * <pre>
40 * File dir = FileUtils.current();
41 * String[] files = dir.list(new SuffixFileFilter(".java"));
42 * for (String file : files) {
43 * System.out.println(file);
44 * }
45 * </pre>
46 *
47 * <h2>Using NIO</h2>
48 * <pre>
49 * final Path dir = PathUtils.current();
50 * final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(new SuffixFileFilter(".java"));
51 * //
52 * // Walk one directory
53 * Files.<strong>walkFileTree</strong>(dir, Collections.emptySet(), 1, visitor);
54 * System.out.println(visitor.getPathCounters());
55 * System.out.println(visitor.getFileList());
56 * //
57 * visitor.getPathCounters().reset();
58 * //
59 * // Walk directory tree
60 * Files.<strong>walkFileTree</strong>(dir, visitor);
61 * System.out.println(visitor.getPathCounters());
62 * System.out.println(visitor.getDirList());
63 * System.out.println(visitor.getFileList());
64 * </pre>
65 * <h2>Deprecating Serialization</h2>
66 * <p>
67 * <em>Serialization is deprecated and will be removed in 3.0.</em>
68 * </p>
69 *
70 * @since 1.0
71 * @see FileFilterUtils#suffixFileFilter(String)
72 * @see FileFilterUtils#suffixFileFilter(String, IOCase)
73 */
74 public class SuffixFileFilter extends AbstractFileFilter implements Serializable {
75
76 private static final long serialVersionUID = -3389157631240246157L;
77
78 /** The file name suffixes to search for */
79 private final String[] suffixes;
80
81 /** Whether the comparison is case-sensitive. */
82 private final IOCase ioCase;
83
84 /**
85 * Constructs a new Suffix file filter for a list of suffixes.
86 *
87 * @param suffixes the suffixes to allow, must not be null.
88 * @throws IllegalArgumentException if the suffix list is null.
89 * @throws ClassCastException if the list does not contain Strings.
90 */
91 public SuffixFileFilter(final List<String> suffixes) {
92 this(suffixes, IOCase.SENSITIVE);
93 }
94
95 /**
96 * Constructs a new Suffix file filter for a list of suffixes
97 * specifying case-sensitivity.
98 *
99 * @param suffixes the suffixes to allow, must not be null.
100 * @param ioCase how to handle case sensitivity, null means case-sensitive.
101 * @throws IllegalArgumentException if the suffix list is null.
102 * @throws ClassCastException if the list does not contain Strings.
103 * @since 1.4
104 */
105 public SuffixFileFilter(final List<String> suffixes, final IOCase ioCase) {
106 Objects.requireNonNull(suffixes, "suffixes");
107 this.suffixes = suffixes.toArray(EMPTY_STRING_ARRAY);
108 this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE);
109 }
110
111 /**
112 * Constructs a new Suffix file filter for a single extension.
113 *
114 * @param suffix the suffix to allow, must not be null.
115 * @throws IllegalArgumentException if the suffix is null.
116 */
117 public SuffixFileFilter(final String suffix) {
118 this(suffix, IOCase.SENSITIVE);
119 }
120
121 /**
122 * Constructs a new Suffix file filter for an array of suffixes.
123 * <p>
124 * The array is not cloned, so could be changed after constructing the
125 * instance. This would be inadvisable however.
126 * </p>
127 *
128 * @param suffixes the suffixes to allow, must not be null.
129 * @throws NullPointerException if the suffix array is null.
130 */
131 public SuffixFileFilter(final String... suffixes) {
132 this(suffixes, IOCase.SENSITIVE);
133 }
134
135 /**
136 * Constructs a new Suffix file filter for a single extension
137 * specifying case-sensitivity.
138 *
139 * @param suffix the suffix to allow, must not be null.
140 * @param ioCase how to handle case sensitivity, null means case-sensitive.
141 * @throws NullPointerException if the suffix is null.
142 * @since 1.4
143 */
144 public SuffixFileFilter(final String suffix, final IOCase ioCase) {
145 Objects.requireNonNull(suffix, "suffix");
146 this.suffixes = new String[] {suffix};
147 this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE);
148 }
149
150 /**
151 * Constructs a new Suffix file filter for an array of suffixes
152 * specifying case-sensitivity.
153 *
154 * @param suffixes the suffixes to allow, must not be null.
155 * @param ioCase how to handle case sensitivity, null means case-sensitive.
156 * @throws NullPointerException if the suffix array is null.
157 * @since 1.4
158 */
159 public SuffixFileFilter(final String[] suffixes, final IOCase ioCase) {
160 Objects.requireNonNull(suffixes, "suffixes");
161 this.suffixes = suffixes.clone();
162 this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE);
163 }
164
165 /**
166 * Tests 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 * Tests 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 * Tests to see if the file name ends with the suffix.
190 *
191 * @param path the File to check.
192 * @param attributes the path's basic attributes (may be null).
193 * @return true if the file name ends with one of our suffixes.
194 * @since 2.9.0
195 */
196 @Override
197 public FileVisitResult accept(final Path path, final BasicFileAttributes attributes) {
198 return toFileVisitResult(accept(PathUtils.getFileNameString(path)));
199 }
200
201 private boolean accept(final String name) {
202 return Stream.of(suffixes).anyMatch(suffix -> ioCase.checkEndsWith(name, suffix));
203 }
204
205 /**
206 * Provides a String representation of this file filter.
207 *
208 * @return a String representation.
209 */
210 @Override
211 public String toString() {
212 final StringBuilder buffer = new StringBuilder();
213 buffer.append(super.toString());
214 buffer.append("(");
215 append(suffixes, buffer);
216 buffer.append(")");
217 return buffer.toString();
218 }
219
220 }