View Javadoc
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    *      http://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.FilenameUtils;
29  import org.apache.commons.io.IOCase;
30  import org.apache.commons.io.build.AbstractSupplier;
31  import org.apache.commons.io.file.PathUtils;
32  
33  /**
34   * Filters files using the supplied wildcards.
35   * <p>
36   * This filter selects files and directories based on one or more wildcards. Testing is case-sensitive by default, but this can be configured.
37   * </p>
38   * <p>
39   * The wildcard matcher uses the characters '?' and '*' to represent a single or multiple wildcard characters. This is the same as often found on DOS/Unix
40   * command lines. The check is case-sensitive by default. See {@link FilenameUtils#wildcardMatchOnSystem(String,String)} for more information.
41   * </p>
42   * <p>
43   * To build an instance, use {@link Builder}.
44   * </p>
45   * <p>
46   * For example:
47   * </p>
48   * <h2>Using Classic IO</h2>
49   *
50   * <pre>
51   * File dir = FileUtils.current();
52   * FileFilter fileFilter = WildcardFileFilter.builder().setWildcards("*test*.java~*~").get();
53   * File[] files = dir.listFiles(fileFilter);
54   * for (String file : files) {
55   *     System.out.println(file);
56   * }
57   * </pre>
58   *
59   * <h2>Using NIO</h2>
60   *
61   * <pre>
62   * final Path dir = PathUtils.current();
63   * final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(
64   *     WildcardFileFilter.builder().setWildcards("*test*.java~*~").get());
65   * //
66   * // Walk one directory
67   * Files.<strong>walkFileTree</strong>(dir, Collections.emptySet(), 1, visitor);
68   * System.out.println(visitor.getPathCounters());
69   * System.out.println(visitor.getFileList());
70   * //
71   * visitor.getPathCounters().reset();
72   * //
73   * // Walk directory tree
74   * Files.<strong>walkFileTree</strong>(dir, visitor);
75   * System.out.println(visitor.getPathCounters());
76   * System.out.println(visitor.getDirList());
77   * System.out.println(visitor.getFileList());
78   * </pre>
79   * <h2>Deprecating Serialization</h2>
80   * <p>
81   * <em>Serialization is deprecated and will be removed in 3.0.</em>
82   * </p>
83   *
84   * @since 1.3
85   */
86  public class WildcardFileFilter extends AbstractFileFilter implements Serializable {
87  
88      /**
89       * Builds a new {@link WildcardFileFilter} instance.
90       *
91       * @since 2.12.0
92       */
93      public static class Builder extends AbstractSupplier<WildcardFileFilter, Builder> {
94  
95          /** The wildcards that will be used to match file names. */
96          private String[] wildcards;
97  
98          /** Whether the comparison is case-sensitive. */
99          private IOCase ioCase = IOCase.SENSITIVE;
100 
101         /**
102          * Constructs a new builder of {@link WildcardFileFilter}.
103          */
104         public Builder() {
105             // empty
106         }
107 
108         @Override
109         public WildcardFileFilter get() {
110             return new WildcardFileFilter(ioCase, wildcards);
111         }
112 
113         /**
114          * Sets how to handle case sensitivity, null means case-sensitive.
115          *
116          * @param ioCase how to handle case sensitivity, null means case-sensitive.
117          * @return {@code this} instance.
118          */
119         public Builder setIoCase(final IOCase ioCase) {
120             this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE);
121             return this;
122         }
123 
124         /**
125          * Sets the list of wildcards to match, not null.
126          *
127          * @param wildcards the list of wildcards to match, not null.
128          * @return {@code this} instance.
129          */
130         public Builder setWildcards(final List<String> wildcards) {
131             setWildcards(requireWildcards(wildcards).toArray(EMPTY_STRING_ARRAY));
132             return this;
133         }
134 
135         /**
136          * Sets the wildcards to match, not null.
137          *
138          * @param wildcards the wildcards to match, not null.
139          * @return {@code this} instance.
140          */
141         public Builder setWildcards(final String... wildcards) {
142             this.wildcards = requireWildcards(wildcards);
143             return this;
144         }
145 
146     }
147 
148     private static final long serialVersionUID = -7426486598995782105L;
149 
150     /**
151      * Constructs a new {@link Builder}.
152      *
153      * @return a new {@link Builder}.
154      * @since 2.12.0
155      */
156     public static Builder builder() {
157         return new Builder();
158     }
159 
160     private static <T> T requireWildcards(final T wildcards) {
161         return Objects.requireNonNull(wildcards, "wildcards");
162     }
163 
164     /** The wildcards that will be used to match file names. */
165     private final String[] wildcards;
166 
167     /** Whether the comparison is case-sensitive. */
168     private final IOCase ioCase;
169 
170     /**
171      * Constructs a new wildcard filter for an array of wildcards specifying case-sensitivity.
172      *
173      * @param wildcards the array of wildcards to match, not null
174      * @param ioCase    how to handle case sensitivity, null means case-sensitive
175      * @throws NullPointerException if the pattern array is null
176      */
177     private WildcardFileFilter(final IOCase ioCase, final String... wildcards) {
178         this.wildcards = requireWildcards(wildcards).clone();
179         this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE);
180     }
181 
182     /**
183      * Constructs a new case-sensitive wildcard filter for a list of wildcards.
184      *
185      * @param wildcards the list of wildcards to match, not null
186      * @throws IllegalArgumentException if the pattern list is null
187      * @throws ClassCastException       if the list does not contain Strings
188      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
189      */
190     @Deprecated
191     public WildcardFileFilter(final List<String> wildcards) {
192         this(wildcards, IOCase.SENSITIVE);
193     }
194 
195     /**
196      * Constructs a new wildcard filter for a list of wildcards specifying case-sensitivity.
197      *
198      * @param wildcards the list of wildcards to match, not null
199      * @param ioCase    how to handle case sensitivity, null means case-sensitive
200      * @throws IllegalArgumentException if the pattern list is null
201      * @throws ClassCastException       if the list does not contain Strings
202      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
203      */
204     @Deprecated
205     public WildcardFileFilter(final List<String> wildcards, final IOCase ioCase) {
206         this(ioCase, requireWildcards(wildcards).toArray(EMPTY_STRING_ARRAY));
207     }
208 
209     /**
210      * Constructs a new case-sensitive wildcard filter for a single wildcard.
211      *
212      * @param wildcard the wildcard to match
213      * @throws IllegalArgumentException if the pattern is null
214      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
215      */
216     @Deprecated
217     public WildcardFileFilter(final String wildcard) {
218         this(IOCase.SENSITIVE, requireWildcards(wildcard));
219     }
220 
221     /**
222      * Constructs a new case-sensitive wildcard filter for an array of wildcards.
223      *
224      * @param wildcards the array of wildcards to match
225      * @throws NullPointerException if the pattern array is null
226      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
227      */
228     @Deprecated
229     public WildcardFileFilter(final String... wildcards) {
230         this(IOCase.SENSITIVE, wildcards);
231     }
232 
233     /**
234      * Constructs a new wildcard filter for a single wildcard specifying case-sensitivity.
235      *
236      * @param wildcard the wildcard to match, not null
237      * @param ioCase   how to handle case sensitivity, null means case-sensitive
238      * @throws NullPointerException if the pattern is null
239      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
240      */
241     @Deprecated
242     public WildcardFileFilter(final String wildcard, final IOCase ioCase) {
243         this(ioCase, wildcard);
244     }
245 
246     /**
247      * Constructs a new wildcard filter for an array of wildcards specifying case-sensitivity.
248      *
249      * @param wildcards the array of wildcards to match, not null
250      * @param ioCase    how to handle case sensitivity, null means case-sensitive
251      * @throws NullPointerException if the pattern array is null
252      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
253      */
254     @Deprecated
255     public WildcardFileFilter(final String[] wildcards, final IOCase ioCase) {
256         this(ioCase, wildcards);
257     }
258 
259     /**
260      * Checks to see if the file name matches one of the wildcards.
261      *
262      * @param file the file to check
263      * @return true if the file name matches one of the wildcards
264      */
265     @Override
266     public boolean accept(final File file) {
267         return accept(file.getName());
268     }
269 
270     /**
271      * Checks to see if the file name matches one of the wildcards.
272      *
273      * @param dir  the file directory (ignored)
274      * @param name the file name
275      * @return true if the file name matches one of the wildcards
276      */
277     @Override
278     public boolean accept(final File dir, final String name) {
279         return accept(name);
280     }
281 
282     /**
283      * Checks to see if the file name matches one of the wildcards.
284      *
285      * @param path the file to check
286      * @param attributes the path's basic attributes (may be null).
287      * @return true if the file name matches one of the wildcards.
288      * @since 2.9.0
289      */
290     @Override
291     public FileVisitResult accept(final Path path, final BasicFileAttributes attributes) {
292         return toFileVisitResult(accept(PathUtils.getFileNameString(path)));
293     }
294 
295     private boolean accept(final String name) {
296         return Stream.of(wildcards).anyMatch(wildcard -> FilenameUtils.wildcardMatch(name, wildcard, ioCase));
297     }
298 
299     /**
300      * Provide a String representation of this file filter.
301      *
302      * @return a String representation
303      */
304     @Override
305     public String toString() {
306         final StringBuilder buffer = new StringBuilder();
307         buffer.append(super.toString());
308         buffer.append("(");
309         append(wildcards, buffer);
310         buffer.append(")");
311         return buffer.toString();
312     }
313 }