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;
026import java.util.stream.Stream;
027
028import org.apache.commons.io.IOCase;
029import org.apache.commons.io.file.PathUtils;
030
031/**
032 * Filters file names for a certain name.
033 * <p>
034 * For example, to print all files and directories in the
035 * current directory whose name is {@code Test}:
036 * </p>
037 * <h2>Using Classic IO</h2>
038 * <pre>
039 * File dir = FileUtils.current();
040 * String[] files = dir.list(new NameFileFilter("Test"));
041 * for (String file : files) {
042 *     System.out.println(file);
043 * }
044 * </pre>
045 *
046 * <h2>Using NIO</h2>
047 * <pre>
048 * final Path dir = PathUtils.current();
049 * final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(new NameFileFilter("Test"));
050 * //
051 * // Walk one directory
052 * Files.<strong>walkFileTree</strong>(dir, Collections.emptySet(), 1, visitor);
053 * System.out.println(visitor.getPathCounters());
054 * System.out.println(visitor.getFileList());
055 * //
056 * visitor.getPathCounters().reset();
057 * //
058 * // Walk directory tree
059 * Files.<strong>walkFileTree</strong>(dir, visitor);
060 * System.out.println(visitor.getPathCounters());
061 * System.out.println(visitor.getDirList());
062 * System.out.println(visitor.getFileList());
063 * </pre>
064 * <h2>Deprecating Serialization</h2>
065 * <p>
066 * <em>Serialization is deprecated and will be removed in 3.0.</em>
067 * </p>
068 *
069 * @since 1.0
070 * @see FileFilterUtils#nameFileFilter(String)
071 * @see FileFilterUtils#nameFileFilter(String, IOCase)
072 */
073public class NameFileFilter extends AbstractFileFilter implements Serializable {
074
075    private static final long serialVersionUID = 176844364689077340L;
076
077    /** The file names to search for */
078    private final String[] names;
079
080    /** Whether the comparison is case-sensitive. */
081    private final IOCase ioCase;
082
083    /**
084     * Constructs a new case-sensitive name file filter for a list of names.
085     *
086     * @param names  the names to allow, must not be null
087     * @throws IllegalArgumentException if the name list is null
088     * @throws ClassCastException if the list does not contain Strings
089     */
090    public NameFileFilter(final List<String> names) {
091        this(names, null);
092    }
093
094    /**
095     * Constructs a new name file filter for a list of names specifying case-sensitivity.
096     *
097     * @param names  the names to allow, must not be null
098     * @param ioCase  how to handle case sensitivity, null means case-sensitive
099     * @throws NullPointerException if the name list is null
100     * @throws ClassCastException if the list does not contain Strings
101     */
102    public NameFileFilter(final List<String> names, final IOCase ioCase) {
103        Objects.requireNonNull(names, "names");
104        this.names = names.toArray(EMPTY_STRING_ARRAY);
105        this.ioCase = toIOCase(ioCase);
106    }
107
108    /**
109     * Constructs a new case-sensitive name file filter for a single name.
110     *
111     * @param name  the name to allow, must not be null
112     * @throws IllegalArgumentException if the name is null
113     */
114    public NameFileFilter(final String name) {
115        this(name, IOCase.SENSITIVE);
116    }
117
118    /**
119     * Constructs a new case-sensitive name file filter for an array of names.
120     * <p>
121     * The array is not cloned, so could be changed after constructing the
122     * instance. This would be inadvisable however.
123     * </p>
124     *
125     * @param names  the names to allow, must not be null
126     * @throws IllegalArgumentException if the names array is null
127     */
128    public NameFileFilter(final String... names) {
129        this(names, IOCase.SENSITIVE);
130    }
131
132    /**
133     * Constructs a new name file filter specifying case-sensitivity.
134     *
135     * @param name  the name to allow, must not be null
136     * @param ioCase  how to handle case sensitivity, null means case-sensitive
137     * @throws NullPointerException if the name is null
138     */
139    public NameFileFilter(final String name, final IOCase ioCase) {
140        Objects.requireNonNull(name, "name");
141        this.names = new String[] {name};
142        this.ioCase = toIOCase(ioCase);
143    }
144
145    /**
146     * Constructs a new name file filter for an array of names specifying case-sensitivity.
147     *
148     * @param names  the names to allow, must not be null
149     * @param ioCase  how to handle case sensitivity, null means case-sensitive
150     * @throws NullPointerException if the names array is null
151     */
152    public NameFileFilter(final String[] names, final IOCase ioCase) {
153        Objects.requireNonNull(names, "names");
154        this.names = names.clone();
155        this.ioCase = toIOCase(ioCase);
156    }
157
158    /**
159     * Checks to see if the file name matches.
160     *
161     * @param file  the File to check
162     * @return true if the file name matches
163     */
164    @Override
165    public boolean accept(final File file) {
166        return file != null && acceptBaseName(file.getName());
167    }
168
169    /**
170     * Checks to see if the file name matches.
171     *
172     * @param dir  the File directory (ignored)
173     * @param name  the file name
174     * @return true if the file name matches
175     */
176    @Override
177    public boolean accept(final File dir, final String name) {
178        return acceptBaseName(name);
179    }
180
181    /**
182     * Checks to see if the file name matches.
183     *
184     * @param path  the File to check
185     * @param attributes the path's basic attributes (may be null).
186     * @return true if the file name matches
187     * @since 2.9.0
188     */
189    @Override
190    public FileVisitResult accept(final Path path, final BasicFileAttributes attributes) {
191        return toFileVisitResult(acceptBaseName(PathUtils.getFileNameString(path)));
192    }
193
194    private boolean acceptBaseName(final String baseName) {
195        return Stream.of(names).anyMatch(testName -> ioCase.checkEquals(baseName, testName));
196    }
197
198    private IOCase toIOCase(final IOCase ioCase) {
199        return IOCase.value(ioCase, IOCase.SENSITIVE);
200    }
201
202    /**
203     * Provide a String representation of this file filter.
204     *
205     * @return a String representation
206     */
207    @Override
208    public String toString() {
209        final StringBuilder buffer = new StringBuilder();
210        buffer.append(super.toString());
211        buffer.append("(");
212        append(names, buffer);
213        buffer.append(")");
214        return buffer.toString();
215    }
216
217}