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 dir
67   * Files.<b>walkFileTree</b>(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 dir tree
74   * Files.<b>walkFileTree</b>(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         @Override
102         public WildcardFileFilter get() {
103             return new WildcardFileFilter(ioCase, wildcards);
104         }
105 
106         /**
107          * Sets how to handle case sensitivity, null means case-sensitive.
108          *
109          * @param ioCase how to handle case sensitivity, null means case-sensitive.
110          * @return this
111          */
112         public Builder setIoCase(final IOCase ioCase) {
113             this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE);
114             return this;
115         }
116 
117         /**
118          * Sets the list of wildcards to match, not null.
119          *
120          * @param wildcards the list of wildcards to match, not null.
121          * @return this
122          */
123         public Builder setWildcards(final List<String> wildcards) {
124             setWildcards(requireWildcards(wildcards).toArray(EMPTY_STRING_ARRAY));
125             return this;
126         }
127 
128         /**
129          * Sets the wildcards to match, not null.
130          *
131          * @param wildcards the wildcards to match, not null.
132          * @return this
133          */
134         public Builder setWildcards(final String... wildcards) {
135             this.wildcards = requireWildcards(wildcards);
136             return this;
137         }
138 
139     }
140 
141     private static final long serialVersionUID = -7426486598995782105L;
142 
143     /**
144      * Constructs a new {@link Builder}.
145      *
146      * @return a new {@link Builder}.
147      * @since 2.12.0
148      */
149     public static Builder builder() {
150         return new Builder();
151     }
152 
153     private static <T> T requireWildcards(final T wildcards) {
154         return Objects.requireNonNull(wildcards, "wildcards");
155     }
156 
157     /** The wildcards that will be used to match file names. */
158     private final String[] wildcards;
159 
160     /** Whether the comparison is case-sensitive. */
161     private final IOCase ioCase;
162 
163     /**
164      * Constructs a new wildcard filter for an array of wildcards specifying case-sensitivity.
165      *
166      * @param wildcards the array of wildcards to match, not null
167      * @param ioCase    how to handle case sensitivity, null means case-sensitive
168      * @throws NullPointerException if the pattern array is null
169      */
170     private WildcardFileFilter(final IOCase ioCase, final String... wildcards) {
171         this.wildcards = requireWildcards(wildcards).clone();
172         this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE);
173     }
174 
175     /**
176      * Constructs a new case-sensitive wildcard filter for a list of wildcards.
177      *
178      * @param wildcards the list of wildcards to match, not null
179      * @throws IllegalArgumentException if the pattern list is null
180      * @throws ClassCastException       if the list does not contain Strings
181      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
182      */
183     @Deprecated
184     public WildcardFileFilter(final List<String> wildcards) {
185         this(wildcards, IOCase.SENSITIVE);
186     }
187 
188     /**
189      * Constructs a new wildcard filter for a list of wildcards specifying case-sensitivity.
190      *
191      * @param wildcards the list of wildcards to match, not null
192      * @param ioCase    how to handle case sensitivity, null means case-sensitive
193      * @throws IllegalArgumentException if the pattern list is null
194      * @throws ClassCastException       if the list does not contain Strings
195      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
196      */
197     @Deprecated
198     public WildcardFileFilter(final List<String> wildcards, final IOCase ioCase) {
199         this(ioCase, requireWildcards(wildcards).toArray(EMPTY_STRING_ARRAY));
200     }
201 
202     /**
203      * Constructs a new case-sensitive wildcard filter for a single wildcard.
204      *
205      * @param wildcard the wildcard to match
206      * @throws IllegalArgumentException if the pattern is null
207      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
208      */
209     @Deprecated
210     public WildcardFileFilter(final String wildcard) {
211         this(IOCase.SENSITIVE, requireWildcards(wildcard));
212     }
213 
214     /**
215      * Constructs a new case-sensitive wildcard filter for an array of wildcards.
216      *
217      * @param wildcards the array of wildcards to match
218      * @throws NullPointerException if the pattern array is null
219      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
220      */
221     @Deprecated
222     public WildcardFileFilter(final String... wildcards) {
223         this(IOCase.SENSITIVE, wildcards);
224     }
225 
226     /**
227      * Constructs a new wildcard filter for a single wildcard specifying case-sensitivity.
228      *
229      * @param wildcard the wildcard to match, not null
230      * @param ioCase   how to handle case sensitivity, null means case-sensitive
231      * @throws NullPointerException if the pattern is null
232      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
233      */
234     @Deprecated
235     public WildcardFileFilter(final String wildcard, final IOCase ioCase) {
236         this(ioCase, wildcard);
237     }
238 
239     /**
240      * Constructs a new wildcard filter for an array of wildcards specifying case-sensitivity.
241      *
242      * @param wildcards the array of wildcards to match, not null
243      * @param ioCase    how to handle case sensitivity, null means case-sensitive
244      * @throws NullPointerException if the pattern array is null
245      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
246      */
247     @Deprecated
248     public WildcardFileFilter(final String[] wildcards, final IOCase ioCase) {
249         this(ioCase, wildcards);
250     }
251 
252     /**
253      * Checks to see if the file name matches one of the wildcards.
254      *
255      * @param file the file to check
256      * @return true if the file name matches one of the wildcards
257      */
258     @Override
259     public boolean accept(final File file) {
260         return accept(file.getName());
261     }
262 
263     /**
264      * Checks to see if the file name matches one of the wildcards.
265      *
266      * @param dir  the file directory (ignored)
267      * @param name the file name
268      * @return true if the file name matches one of the wildcards
269      */
270     @Override
271     public boolean accept(final File dir, final String name) {
272         return accept(name);
273     }
274 
275     /**
276      * Checks to see if the file name matches one of the wildcards.
277      *
278      * @param path the file to check
279      *
280      * @return true if the file name matches one of the wildcards.
281      * @since 2.9.0
282      */
283     @Override
284     public FileVisitResult accept(final Path path, final BasicFileAttributes attributes) {
285         return toFileVisitResult(accept(PathUtils.getFileNameString(path)));
286     }
287 
288     private boolean accept(final String name) {
289         return Stream.of(wildcards).anyMatch(wildcard -> FilenameUtils.wildcardMatch(name, wildcard, ioCase));
290     }
291 
292     /**
293      * Provide a String representation of this file filter.
294      *
295      * @return a String representation
296      */
297     @Override
298     public String toString() {
299         final StringBuilder buffer = new StringBuilder();
300         buffer.append(super.toString());
301         buffer.append("(");
302         append(wildcards, buffer);
303         buffer.append(")");
304         return buffer.toString();
305     }
306 }