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.Objects;
025import java.util.function.Function;
026import java.util.regex.Pattern;
027
028import org.apache.commons.io.IOCase;
029
030/**
031 * Filters files using supplied regular expression(s).
032 * <p>
033 * See java.util.regex.Pattern for regex matching rules.
034 * </p>
035 * <h2>Using Classic IO</h2>
036 * <p>
037 * e.g.
038 *
039 * <pre>
040 * File dir = FileUtils.current();
041 * FileFilter fileFilter = new RegexFileFilter("^.*[tT]est(-\\d+)?\\.java$");
042 * File[] files = dir.listFiles(fileFilter);
043 * for (String file : files) {
044 *     System.out.println(file);
045 * }
046 * </pre>
047 *
048 * <h2>Using NIO</h2>
049 *
050 * <pre>
051 * final Path dir = PathUtils.current();
052 * final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(new RegexFileFilter("^.*[tT]est(-\\d+)?\\.java$"));
053 * //
054 * // Walk one dir
055 * Files.<b>walkFileTree</b>(dir, Collections.emptySet(), 1, visitor);
056 * System.out.println(visitor.getPathCounters());
057 * System.out.println(visitor.getFileList());
058 * //
059 * visitor.getPathCounters().reset();
060 * //
061 * // Walk dir tree
062 * Files.<b>walkFileTree</b>(dir, visitor);
063 * System.out.println(visitor.getPathCounters());
064 * System.out.println(visitor.getDirList());
065 * System.out.println(visitor.getFileList());
066 * </pre>
067 * <h2>Deprecating Serialization</h2>
068 * <p>
069 * <em>Serialization is deprecated and will be removed in 3.0.</em>
070 * </p>
071 *
072 * @since 1.4
073 */
074public class RegexFileFilter extends AbstractFileFilter implements Serializable {
075
076    private static final long serialVersionUID = 4269646126155225062L;
077
078    /**
079     * Compiles the given pattern source.
080     *
081     * @param pattern the source pattern.
082     * @param flags the compilation flags.
083     * @return a new Pattern.
084     */
085    private static Pattern compile(final String pattern, final int flags) {
086        Objects.requireNonNull(pattern, "pattern");
087        return Pattern.compile(pattern, flags);
088    }
089
090    /**
091     * Converts IOCase to Pattern compilation flags.
092     *
093     * @param ioCase case-sensitivity.
094     * @return Pattern compilation flags.
095     */
096    private static int toFlags(final IOCase ioCase) {
097        return IOCase.isCaseSensitive(ioCase) ? 0 : Pattern.CASE_INSENSITIVE;
098    }
099
100    /** The regular expression pattern that will be used to match file names. */
101    private final Pattern pattern;
102
103    /** How convert a path to a string. */
104    private final Function<Path, String> pathToString;
105
106    /**
107     * Constructs a new regular expression filter for a compiled regular expression
108     *
109     * @param pattern regular expression to match.
110     * @throws NullPointerException if the pattern is null.
111     */
112    @SuppressWarnings("unchecked")
113    public RegexFileFilter(final Pattern pattern) {
114        this(pattern, (Function<Path, String> & Serializable) p -> p.getFileName().toString());
115    }
116
117    /**
118     * Constructs a new regular expression filter for a compiled regular expression
119     *
120     * @param pattern regular expression to match.
121     * @param pathToString How convert a path to a string.
122     * @throws NullPointerException if the pattern is null.
123     * @since 2.10.0
124     */
125    public RegexFileFilter(final Pattern pattern, final Function<Path, String> pathToString) {
126        Objects.requireNonNull(pattern, "pattern");
127        this.pattern = pattern;
128        this.pathToString = pathToString;
129    }
130
131    /**
132     * Constructs a new regular expression filter.
133     *
134     * @param pattern regular string expression to match
135     * @throws NullPointerException if the pattern is null
136     */
137    public RegexFileFilter(final String pattern) {
138        this(pattern, 0);
139    }
140
141    /**
142     * Constructs a new regular expression filter with the specified flags.
143     *
144     * @param pattern regular string expression to match
145     * @param flags pattern flags - e.g. {@link Pattern#CASE_INSENSITIVE}
146     * @throws IllegalArgumentException if the pattern is null
147     */
148    public RegexFileFilter(final String pattern, final int flags) {
149        this(compile(pattern, flags));
150    }
151
152    /**
153     * Constructs a new regular expression filter with the specified flags case sensitivity.
154     *
155     * @param pattern regular string expression to match
156     * @param ioCase how to handle case sensitivity, null means case-sensitive
157     * @throws IllegalArgumentException if the pattern is null
158     */
159    public RegexFileFilter(final String pattern, final IOCase ioCase) {
160        this(compile(pattern, toFlags(ioCase)));
161    }
162
163    /**
164     * Checks to see if the file name matches one of the regular expressions.
165     *
166     * @param dir the file directory (ignored)
167     * @param name the file name
168     * @return true if the file name matches one of the regular expressions
169     */
170    @Override
171    public boolean accept(final File dir, final String name) {
172        return pattern.matcher(name).matches();
173    }
174
175    /**
176     * Checks to see if the file name matches one of the regular expressions.
177     *
178     * @param path the path
179     * @param attributes the path attributes
180     * @return true if the file name matches one of the regular expressions
181     */
182    @Override
183    public FileVisitResult accept(final Path path, final BasicFileAttributes attributes) {
184        return toFileVisitResult(pattern.matcher(pathToString.apply(path)).matches());
185    }
186
187    /**
188     * Returns a debug string.
189     *
190     * @since 2.10.0
191     */
192    @Override
193    public String toString() {
194        return "RegexFileFilter [pattern=" + pattern + "]";
195    }
196
197}